Merge "core: add hdr capabilities to display" into nyc-dev
diff --git a/Android.mk b/Android.mk
index 3ac5889..0bd6b7f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -149,6 +149,7 @@
core/java/android/content/pm/IPackageMoveObserver.aidl \
core/java/android/content/pm/IPackageStatsObserver.aidl \
core/java/android/content/pm/IOnPermissionsChangeListener.aidl \
+ core/java/android/content/pm/IShortcutService.aidl \
core/java/android/database/IContentObserver.aidl \
../av/camera/aidl/android/hardware/ICameraService.aidl \
../av/camera/aidl/android/hardware/ICameraServiceListener.aidl \
@@ -241,11 +242,13 @@
core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
core/java/android/service/notification/IConditionListener.aidl \
core/java/android/service/notification/IConditionProvider.aidl \
+ core/java/android/service/vr/IVrListener.aidl \
core/java/android/print/ILayoutResultCallback.aidl \
core/java/android/print/IPrinterDiscoveryObserver.aidl \
core/java/android/print/IPrintDocumentAdapter.aidl \
core/java/android/print/IPrintDocumentAdapterObserver.aidl \
core/java/android/print/IPrintJobStateChangeListener.aidl \
+ core/java/android/print/IPrintServicesChangeListener.aidl \
core/java/android/print/IPrintManager.aidl \
core/java/android/print/IPrintSpooler.aidl \
core/java/android/print/IPrintSpoolerCallbacks.aidl \
@@ -410,6 +413,8 @@
telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl \
telephony/java/com/android/ims/internal/IImsEcbm.aidl \
telephony/java/com/android/ims/internal/IImsEcbmListener.aidl \
+ telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl \
+ telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl \
telephony/java/com/android/ims/internal/IImsService.aidl \
telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl \
telephony/java/com/android/ims/internal/IImsUt.aidl \
@@ -650,6 +655,7 @@
frameworks/base/core/java/android/content/pm/ProviderInfo.aidl \
frameworks/base/core/java/android/content/pm/PackageStats.aidl \
frameworks/base/core/java/android/content/pm/PermissionGroupInfo.aidl \
+ frameworks/base/core/java/android/content/pm/ShortcutInfo.aidl \
frameworks/base/core/java/android/content/pm/LabeledIntent.aidl \
frameworks/base/core/java/android/content/ComponentName.aidl \
frameworks/base/core/java/android/content/SyncStats.aidl \
@@ -823,6 +829,7 @@
-since $(SRC_API_DIR)/21.txt 21 \
-since $(SRC_API_DIR)/22.txt 22 \
-since $(SRC_API_DIR)/23.txt 23 \
+ -since ./frameworks/base/api/current.txt N \
-werror -hide 111 -hide 113 \
-overview $(LOCAL_PATH)/core/java/overview.html
@@ -866,9 +873,11 @@
framework_docs_SDK_REL_ID:=1
framework_docs_LOCAL_DROIDDOC_OPTIONS += \
+ -hdf sdk.codename N \
+ -hdf sdk.preview.version 2 \
-hdf sdk.version $(framework_docs_SDK_VERSION) \
-hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \
- -hdf sdk.preview 0
+ -hdf sdk.preview 1
# ==== the api stubs and current.xml ===========================
include $(CLEAR_VARS)
@@ -1023,23 +1032,24 @@
-offlinemode \
-title "Android SDK" \
-proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
- -todo $(OUT_DOCS)/$(LOCAL_MODULE)-docs-todo.html \
-sdkvalues $(OUT_DOCS) \
- -hdf android.whichdoc offline
+ -hdf android.whichdoc offline \
+ -referenceonly
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-refonly
include $(BUILD_DROIDDOC)
static_doc_index_redirect := $(out_dir)/index.html
$(static_doc_index_redirect): \
- $(LOCAL_PATH)/docs/docs-documentation-redirect.html | $(ACP)
+ $(LOCAL_PATH)/docs/docs-preview-index.html | $(ACP)
$(hide) mkdir -p $(dir $@)
$(hide) $(ACP) $< $@
$(full_target): $(static_doc_index_redirect)
$(full_target): $(framework_built)
+
# ==== docs for the web (on the androiddevdocs app engine server) =======================
include $(CLEAR_VARS)
diff --git a/api/current.txt b/api/current.txt
index 2152d6f..50e0de41 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38,6 +38,7 @@
field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
field public static final java.lang.String BIND_VOICE_INTERACTION = "android.permission.BIND_VOICE_INTERACTION";
field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
+ field public static final java.lang.String BIND_VR_LISTENER_SERVICE = "android.permission.BIND_VR_LISTENER_SERVICE";
field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN";
@@ -114,6 +115,7 @@
field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
+ field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
@@ -877,6 +879,7 @@
field public static final int nextFocusLeft = 16842977; // 0x10100e1
field public static final int nextFocusRight = 16842978; // 0x10100e2
field public static final int nextFocusUp = 16842979; // 0x10100e3
+ field public static final int nfcAntennaPositionDrawable = 16844063; // 0x101051f
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -3439,7 +3442,7 @@
method public android.view.View getCurrentFocus();
method public android.app.FragmentManager getFragmentManager();
method public android.content.Intent getIntent();
- method public deprecated java.lang.Object getLastNonConfigurationInstance();
+ method public java.lang.Object getLastNonConfigurationInstance();
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
@@ -3540,7 +3543,7 @@
method protected void onRestoreInstanceState(android.os.Bundle);
method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
method protected void onResume();
- method public deprecated java.lang.Object onRetainNonConfigurationInstance();
+ method public java.lang.Object onRetainNonConfigurationInstance();
method protected void onSaveInstanceState(android.os.Bundle);
method public void onSaveInstanceState(android.os.Bundle, android.os.PersistableBundle);
method public boolean onSearchRequested(android.view.SearchEvent);
@@ -3604,7 +3607,7 @@
method public deprecated void setTitleColor(int);
method public void setVisible(boolean);
method public final void setVolumeControlStream(int);
- method public void setVrMode(boolean);
+ method public void setVrModeEnabled(boolean, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean shouldShowRequestPermissionRationale(java.lang.String);
method public boolean shouldUpRecreateTask(android.content.Intent);
method public boolean showAssist(android.os.Bundle);
@@ -5258,7 +5261,7 @@
}
public static class NotificationManager.Policy implements android.os.Parcelable {
- ctor public deprecated NotificationManager.Policy(int, int, int);
+ ctor public NotificationManager.Policy(int, int, int);
ctor public NotificationManager.Policy(int, int, int, int);
method public int describeContents();
method public static java.lang.String priorityCategoriesToString(int);
@@ -5883,7 +5886,7 @@
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
- method public java.lang.String getWifiMacAddress();
+ method public java.lang.String getWifiMacAddress(android.content.ComponentName);
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -5976,6 +5979,7 @@
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
+ field public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5; // 0x5
field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
@@ -6369,8 +6373,8 @@
method public long getTxPackets();
method public int getUid();
field public static final int ROAMING_ALL = -1; // 0xffffffff
- field public static final int ROAMING_DEFAULT = 1; // 0x1
- field public static final int ROAMING_ROAMING = 2; // 0x2
+ field public static final int ROAMING_NO = 1; // 0x1
+ field public static final int ROAMING_YES = 2; // 0x2
field public static final int STATE_ALL = -1; // 0xffffffff
field public static final int STATE_DEFAULT = 1; // 0x1
field public static final int STATE_FOREGROUND = 2; // 0x2
@@ -8110,7 +8114,7 @@
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
field public static final java.lang.String DROPBOX_SERVICE = "dropbox";
field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint";
- field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardwareproperties";
+ field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
@@ -8138,7 +8142,9 @@
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";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
@@ -9322,6 +9328,7 @@
field public int flags;
field public int largestWidthLimitDp;
field public java.lang.String manageSpaceActivityName;
+ field public java.lang.String minSdkVersion;
field public java.lang.String nativeLibraryDir;
field public java.lang.String permission;
field public java.lang.String processName;
@@ -9443,13 +9450,20 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
+ method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
+ method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
+ method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
+ 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 boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9462,6 +9476,18 @@
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 void setActivity(android.content.ComponentName);
+ method public void setChangedSince(long);
+ method public void setPackage(java.lang.String);
+ method public void setQueryFlags(int);
+ field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
+ field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+ field public static final int FLAG_GET_PINNED = 2; // 0x2
}
public class PackageInfo implements android.os.Parcelable {
@@ -9539,6 +9565,7 @@
method public java.lang.String[] getNames() throws java.io.IOException;
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
+ method public void removeSplit(java.lang.String) throws java.io.IOException;
method public void setStagingProgress(float);
}
@@ -9961,6 +9988,59 @@
field public java.lang.String permission;
}
+ public class ShortcutInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.content.ComponentName getActivityComponent();
+ method public android.os.PersistableBundle getExtras();
+ method public java.lang.String getId();
+ method public android.content.Intent getIntent();
+ method public long getLastChangedTimestamp();
+ method public java.lang.String getPackageName();
+ method public java.lang.String getTitle();
+ method public int getWeight();
+ method public boolean hasIconFile();
+ method public boolean hasIconResource();
+ method public boolean hasKeyFieldsOnly();
+ method public boolean isDynamic();
+ method public boolean isPinned();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
+ field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
+ field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
+ field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+ field public static final int FLAG_DYNAMIC = 1; // 0x1
+ field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
+ field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
+ field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
+ field public static final int FLAG_PINNED = 2; // 0x2
+ }
+
+ public static class ShortcutInfo.Builder {
+ ctor public ShortcutInfo.Builder(android.content.Context);
+ method public android.content.pm.ShortcutInfo build();
+ method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+ 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 setId(java.lang.String);
+ method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+ method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
+ method public android.content.pm.ShortcutInfo.Builder setWeight(int);
+ }
+
+ public class ShortcutManager {
+ method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
+ method public void deleteAllDynamicShortcuts();
+ method public void deleteDynamicShortcut(java.lang.String);
+ method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+ method public int getIconMaxDimensions();
+ method public int getMaxDynamicShortcutCount();
+ method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+ method public long getRateLimitResetTime();
+ method public int getRemainingCallCount();
+ 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);
@@ -10037,7 +10117,6 @@
method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
- method public int getChangingConfigurations();
method public int getColorForState(int[], int);
method public int getDefaultColor();
method public boolean isOpaque();
@@ -10049,6 +10128,7 @@
public abstract class ComplexColor {
ctor public ComplexColor();
+ method public int getChangingConfigurations();
method public abstract int getDefaultColor();
method public boolean isStateful();
}
@@ -10466,7 +10546,6 @@
method public boolean hasNext();
method public java.util.Iterator<android.database.CursorJoiner.Result> iterator();
method public android.database.CursorJoiner.Result next();
- method public void remove();
}
public static final class CursorJoiner.Result extends java.lang.Enum {
@@ -12837,7 +12916,8 @@
method public int getGradientType();
method public int getOpacity();
method public android.graphics.drawable.GradientDrawable.Orientation getOrientation();
- method public boolean isUseLevel();
+ method public int getShape();
+ method public boolean getUseLevel();
method public void setAlpha(int);
method public void setColor(int);
method public void setColor(android.content.res.ColorStateList);
@@ -12956,9 +13036,9 @@
method public void setPaddingMode(int);
method public void setPaddingRelative(int, int, int, int);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+ field public static final int INSET_UNDEFINED = -2147483648; // 0x80000000
field public static final int PADDING_MODE_NEST = 0; // 0x0
field public static final int PADDING_MODE_STACK = 1; // 0x1
- field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000
}
public class LevelListDrawable extends android.graphics.drawable.DrawableContainer {
@@ -13699,6 +13779,7 @@
public static abstract class CameraCaptureSession.CaptureCallback {
ctor public CameraCaptureSession.CaptureCallback();
+ method public void onCaptureBufferLost(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.view.Surface, long);
method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
@@ -19172,24 +19253,6 @@
method public boolean hasFullBiasNanos();
method public boolean hasLeapSecond();
method public boolean hasTimeUncertaintyNanos();
- method public void reset();
- method public void resetBiasNanos();
- method public void resetBiasUncertaintyNanos();
- method public void resetDriftNanosPerSecond();
- method public void resetDriftUncertaintyNanosPerSecond();
- method public void resetFullBiasNanos();
- method public void resetLeapSecond();
- method public void resetTimeUncertaintyNanos();
- method public void set(android.location.GnssClock);
- method public void setBiasNanos(double);
- method public void setBiasUncertaintyNanos(double);
- method public void setDriftNanosPerSecond(double);
- method public void setDriftUncertaintyNanosPerSecond(double);
- method public void setFullBiasNanos(long);
- method public void setHardwareClockDiscontinuityCount(int);
- method public void setLeapSecond(int);
- method public void setTimeNanos(long);
- method public void setTimeUncertaintyNanos(double);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
}
@@ -19220,31 +19283,6 @@
method public boolean hasCarrierPhaseUncertainty();
method public boolean hasSnrInDb();
method public boolean isPseudorangeRateCorrected();
- method public void reset();
- method public void resetCarrierCycles();
- method public void resetCarrierFrequencyHz();
- method public void resetCarrierPhase();
- method public void resetCarrierPhaseUncertainty();
- method public void resetSnrInDb();
- method public void set(android.location.GnssMeasurement);
- method public void setAccumulatedDeltaRangeMeters(double);
- method public void setAccumulatedDeltaRangeState(int);
- method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
- method public void setCarrierCycles(long);
- method public void setCarrierFrequencyHz(float);
- method public void setCarrierPhase(double);
- method public void setCarrierPhaseUncertainty(double);
- method public void setCn0DbHz(double);
- method public void setConstellationType(int);
- method public void setMultipathIndicator(int);
- method public void setPseudorangeRateMetersPerSecond(double);
- method public void setPseudorangeRateUncertaintyMetersPerSecond(double);
- method public void setReceivedSvTimeNanos(long);
- method public void setReceivedSvTimeUncertaintyNanos(long);
- method public void setSnrInDb(double);
- method public void setState(int);
- method public void setSvid(int);
- method public void setTimeOffsetNanos(double);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
field public static final int ADR_STATE_RESET = 2; // 0x2
@@ -19294,14 +19332,6 @@
method public int getSubmessageId();
method public int getSvid();
method public int getType();
- method public void reset();
- method public void set(android.location.GnssNavigationMessage);
- method public void setData(byte[]);
- method public void setMessageId(int);
- method public void setStatus(int);
- method public void setSubmessageId(int);
- method public void setSvid(int);
- method public void setType(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
field public static final int STATUS_PARITY_PASSED = 1; // 0x1
@@ -19721,7 +19751,7 @@
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
- method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
+ method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -19867,7 +19897,7 @@
public static abstract class AudioManager.AudioRecordingCallback {
ctor public AudioManager.AudioRecordingCallback();
- method public void onRecordConfigChanged();
+ method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
}
public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19941,7 +19971,7 @@
method public abstract void onRoutingChanged(android.media.AudioRecord);
}
- public final class AudioRecordConfiguration implements android.os.Parcelable {
+ public final class AudioRecordingConfiguration implements android.os.Parcelable {
method public int describeContents();
method public android.media.AudioDeviceInfo getAudioDevice();
method public int getClientAudioSessionId();
@@ -19949,7 +19979,7 @@
method public android.media.AudioFormat getClientFormat();
method public android.media.AudioFormat getFormat();
method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+ field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
}
public abstract interface AudioRouting {
@@ -20157,37 +20187,129 @@
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final java.lang.String TAG_APERTURE = "FNumber";
+ field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+ field public static final java.lang.String TAG_ARTIST = "Artist";
+ field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+ field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+ field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+ field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+ field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+ field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+ field public static final java.lang.String TAG_COMPRESSION = "Compression";
+ field public static final java.lang.String TAG_CONTRAST = "Contrast";
+ field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+ field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
field public static final java.lang.String TAG_DATETIME = "DateTime";
field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+ field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+ field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+ field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
field public static final java.lang.String TAG_FLASH = "Flash";
+ field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+ field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+ field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+ field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+ field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+ field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+ field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+ field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+ field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+ field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+ field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+ field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+ field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+ field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+ field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+ field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+ field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+ field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+ field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+ field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+ field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
field public static final java.lang.String TAG_MAKE = "Make";
+ field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+ field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
field public static final java.lang.String TAG_MODEL = "Model";
+ field public static final java.lang.String TAG_OECF = "OECF";
field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+ field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+ field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+ field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+ field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+ field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+ field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+ field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+ field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+ field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+ field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+ field public static final java.lang.String TAG_SATURATION = "Saturation";
+ field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+ field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+ field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+ field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+ field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+ field public static final java.lang.String TAG_SOFTWARE = "Software";
+ field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+ field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+ field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+ field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+ field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+ field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+ field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+ field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+ field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+ field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+ field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+ field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+ field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+ field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+ field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+ field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
field public static final int WHITEBALANCE_AUTO = 0; // 0x0
field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
}
@@ -20385,6 +20507,7 @@
field public static final int ERROR_NO_KEY = 1; // 0x1
field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
+ field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6
}
public static final class MediaCodec.CryptoInfo {
@@ -20851,6 +20974,7 @@
method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String) throws java.io.IOException;
+ method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
method public void unselectTrack(int);
@@ -20911,6 +21035,7 @@
field public static final java.lang.String KEY_DURATION = "durationUs";
field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
+ field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
field public static final java.lang.String KEY_HEIGHT = "height";
field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
field public static final java.lang.String KEY_IS_ADTS = "is-adts";
@@ -23020,6 +23145,7 @@
method public boolean onTouchEvent(android.view.MotionEvent);
method public boolean onTrackballEvent(android.view.MotionEvent);
method public abstract boolean onTune(android.net.Uri);
+ method public boolean onTune(android.net.Uri, android.os.Bundle);
method public void onUnblockContent(android.media.tv.TvContentRating);
method public void setOverlayViewEnabled(boolean);
}
@@ -23093,12 +23219,15 @@
method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
method public void setStreamVolume(float);
method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
+ method public void setZOrderMediaOverlay(boolean);
+ method public void setZOrderOnTop(boolean);
method public void timeShiftPause();
method public void timeShiftPlay(java.lang.String, android.net.Uri);
method public void timeShiftResume();
method public void timeShiftSeekTo(long);
method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
method public void tune(java.lang.String, android.net.Uri);
+ method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
}
public static abstract interface TvView.OnUnhandledInputEventListener {
@@ -29047,6 +29176,7 @@
public class Process {
ctor public Process();
method public static final long getElapsedCpuTime();
+ method public static final int[] getExclusiveCores();
method public static final int getGidForName(java.lang.String);
method public static final long getStartElapsedRealtime();
method public static final long getStartUptimeMillis();
@@ -29331,6 +29461,147 @@
}
+package android.os.health {
+
+ public class HealthStats {
+ method public java.lang.String getDataType();
+ method public long getMeasurement(int);
+ method public int getMeasurementKeyAt(int);
+ method public int getMeasurementKeyCount();
+ method public java.util.Map<java.lang.String, java.lang.Long> getMeasurements(int);
+ method public int getMeasurementsKeyAt(int);
+ method public int getMeasurementsKeyCount();
+ method public java.util.Map<java.lang.String, android.os.health.HealthStats> getStats(int);
+ method public int getStatsKeyAt(int);
+ method public int getStatsKeyCount();
+ method public android.os.health.TimerStat getTimer(int);
+ method public int getTimerCount(int);
+ method public int getTimerKeyAt(int);
+ method public int getTimerKeyCount();
+ method public long getTimerTime(int);
+ method public java.util.Map<java.lang.String, android.os.health.TimerStat> getTimers(int);
+ method public int getTimersKeyAt(int);
+ method public int getTimersKeyCount();
+ method public boolean hasMeasurement(int);
+ method public boolean hasMeasurements(int);
+ method public boolean hasStats(int);
+ method public boolean hasTimer(int);
+ method public boolean hasTimers(int);
+ }
+
+ public final class PackageHealthStats {
+ field public static final int MEASUREMENTS_WAKEUP_ALARMS_COUNT = 40002; // 0x9c42
+ field public static final int STATS_SERVICES = 40001; // 0x9c41
+ }
+
+ public final class PidHealthStats {
+ field public static final int MEASUREMENT_WAKE_NESTING_COUNT = 20001; // 0x4e21
+ field public static final int MEASUREMENT_WAKE_START_MS = 20003; // 0x4e23
+ field public static final int MEASUREMENT_WAKE_SUM_MS = 20002; // 0x4e22
+ }
+
+ public final class ProcessHealthStats {
+ field public static final int MEASUREMENT_ANR_COUNT = 30005; // 0x7535
+ field public static final int MEASUREMENT_CRASHES_COUNT = 30004; // 0x7534
+ field public static final int MEASUREMENT_FOREGROUND_MS = 30006; // 0x7536
+ field public static final int MEASUREMENT_STARTS_COUNT = 30003; // 0x7533
+ field public static final int MEASUREMENT_SYSTEM_TIME_MS = 30002; // 0x7532
+ field public static final int MEASUREMENT_USER_TIME_MS = 30001; // 0x7531
+ }
+
+ public final class ServiceHealthStats {
+ field public static final int MEASUREMENT_LAUNCH_COUNT = 50002; // 0xc352
+ field public static final int MEASUREMENT_START_SERVICE_COUNT = 50001; // 0xc351
+ }
+
+ public class SystemHealthManager {
+ method public static android.os.health.SystemHealthManager from(android.content.Context);
+ method public android.os.health.HealthStats takeMyUidSnapshot();
+ method public android.os.health.HealthStats takeUidSnapshot(int);
+ method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
+ }
+
+ public class TimerStat implements android.os.Parcelable {
+ ctor public TimerStat();
+ ctor public TimerStat(int, long);
+ ctor public TimerStat(android.os.Parcel);
+ method public int describeContents();
+ method public int getCount();
+ method public long getTime();
+ method public void setCount(int);
+ method public void setTime(long);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.health.TimerStat> CREATOR;
+ }
+
+ public final class UidHealthStats {
+ field public static final int MEASUREMENT_BLUETOOTH_IDLE_MS = 10020; // 0x2724
+ field public static final int MEASUREMENT_BLUETOOTH_POWER_MAMS = 10023; // 0x2727
+ field public static final int MEASUREMENT_BLUETOOTH_RX_BYTES = 10052; // 0x2744
+ field public static final int MEASUREMENT_BLUETOOTH_RX_MS = 10021; // 0x2725
+ field public static final int MEASUREMENT_BLUETOOTH_RX_PACKETS = 10058; // 0x274a
+ field public static final int MEASUREMENT_BLUETOOTH_TX_BYTES = 10053; // 0x2745
+ field public static final int MEASUREMENT_BLUETOOTH_TX_MS = 10022; // 0x2726
+ field public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = 10059; // 0x274b
+ field public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = 10046; // 0x273e
+ field public static final int MEASUREMENT_CPU_POWER_MAUS = 10064; // 0x2750
+ field public static final int MEASUREMENT_MOBILE_IDLE_MS = 10024; // 0x2728
+ field public static final int MEASUREMENT_MOBILE_POWER_MAMS = 10027; // 0x272b
+ field public static final int MEASUREMENT_MOBILE_RX_BYTES = 10048; // 0x2740
+ field public static final int MEASUREMENT_MOBILE_RX_MS = 10025; // 0x2729
+ field public static final int MEASUREMENT_MOBILE_RX_PACKETS = 10054; // 0x2746
+ field public static final int MEASUREMENT_MOBILE_TX_BYTES = 10049; // 0x2741
+ field public static final int MEASUREMENT_MOBILE_TX_MS = 10026; // 0x272a
+ field public static final int MEASUREMENT_MOBILE_TX_PACKETS = 10055; // 0x2747
+ field public static final int MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = 10045; // 0x273d
+ field public static final int MEASUREMENT_REALTIME_BATTERY_MS = 10001; // 0x2711
+ field public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = 10003; // 0x2713
+ field public static final int MEASUREMENT_SYSTEM_CPU_TIME_US = 10063; // 0x274f
+ field public static final int MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT = 10047; // 0x273f
+ field public static final int MEASUREMENT_UPTIME_BATTERY_MS = 10002; // 0x2712
+ field public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = 10004; // 0x2714
+ field public static final int MEASUREMENT_USER_CPU_TIME_US = 10062; // 0x274e
+ field public static final int MEASUREMENT_WIFI_FULL_LOCK_MS = 10029; // 0x272d
+ field public static final int MEASUREMENT_WIFI_IDLE_MS = 10016; // 0x2720
+ field public static final int MEASUREMENT_WIFI_MULTICAST_MS = 10031; // 0x272f
+ field public static final int MEASUREMENT_WIFI_POWER_MAMS = 10019; // 0x2723
+ field public static final int MEASUREMENT_WIFI_RUNNING_MS = 10028; // 0x272c
+ field public static final int MEASUREMENT_WIFI_RX_BYTES = 10050; // 0x2742
+ field public static final int MEASUREMENT_WIFI_RX_MS = 10017; // 0x2721
+ field public static final int MEASUREMENT_WIFI_RX_PACKETS = 10056; // 0x2748
+ field public static final int MEASUREMENT_WIFI_TX_BYTES = 10051; // 0x2743
+ field public static final int MEASUREMENT_WIFI_TX_MS = 10018; // 0x2722
+ field public static final int MEASUREMENT_WIFI_TX_PACKETS = 10057; // 0x2749
+ field public static final int STATS_PACKAGES = 10015; // 0x271f
+ field public static final int STATS_PIDS = 10013; // 0x271d
+ field public static final int STATS_PROCESSES = 10014; // 0x271e
+ field public static final int TIMERS_JOBS = 10010; // 0x271a
+ field public static final int TIMERS_SENSORS = 10012; // 0x271c
+ field public static final int TIMERS_SYNCS = 10009; // 0x2719
+ field public static final int TIMERS_WAKELOCKS_DRAW = 10008; // 0x2718
+ field public static final int TIMERS_WAKELOCKS_FULL = 10005; // 0x2715
+ field public static final int TIMERS_WAKELOCKS_PARTIAL = 10006; // 0x2716
+ field public static final int TIMERS_WAKELOCKS_WINDOW = 10007; // 0x2717
+ field public static final int TIMER_AUDIO = 10032; // 0x2730
+ field public static final int TIMER_BLUETOOTH_SCAN = 10037; // 0x2735
+ field public static final int TIMER_CAMERA = 10035; // 0x2733
+ field public static final int TIMER_FLASHLIGHT = 10034; // 0x2732
+ field public static final int TIMER_FOREGROUND_ACTIVITY = 10036; // 0x2734
+ field public static final int TIMER_GPS_SENSOR = 10011; // 0x271b
+ field public static final int TIMER_MOBILE_RADIO_ACTIVE = 10061; // 0x274d
+ field public static final int TIMER_PROCESS_STATE_BACKGROUND_MS = 10042; // 0x273a
+ field public static final int TIMER_PROCESS_STATE_CACHED_MS = 10043; // 0x273b
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_MS = 10041; // 0x2739
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS = 10039; // 0x2737
+ field public static final int TIMER_PROCESS_STATE_TOP_MS = 10038; // 0x2736
+ field public static final int TIMER_PROCESS_STATE_TOP_SLEEPING_MS = 10040; // 0x2738
+ field public static final int TIMER_VIBRATOR = 10044; // 0x273c
+ field public static final int TIMER_VIDEO = 10033; // 0x2731
+ field public static final int TIMER_WIFI_SCAN = 10030; // 0x272e
+ }
+
+}
+
package android.os.storage {
public abstract class OnObbStateChangeListener {
@@ -30492,6 +30763,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";
@@ -30514,6 +30786,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
@@ -31715,6 +31988,7 @@
public static final class DocumentsContract.Root {
field public static final java.lang.String COLUMN_AVAILABLE_BYTES = "available_bytes";
+ field public static final java.lang.String COLUMN_CAPACITY_BYTES = "capacity_bytes";
field public static final java.lang.String COLUMN_DOCUMENT_ID = "document_id";
field public static final java.lang.String COLUMN_FLAGS = "flags";
field public static final java.lang.String COLUMN_ICON = "icon";
@@ -32169,6 +32443,7 @@
field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+ field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
@@ -32206,6 +32481,7 @@
field public static final java.lang.String AUTO_TIME = "auto_time";
field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+ field public static final java.lang.String BOOT_COUNT = "boot_count";
field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DATA_ROAMING = "data_roaming";
@@ -34420,8 +34696,9 @@
package android.service.notification {
public class Condition implements android.os.Parcelable {
- ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int);
+ ctor public Condition(android.net.Uri, java.lang.String, int);
ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
+ ctor public Condition(android.os.Parcel);
method public android.service.notification.Condition copy();
method public int describeContents();
method public static boolean isValidId(android.net.Uri, java.lang.String);
@@ -34452,6 +34729,7 @@
method public final void notifyConditions(android.service.notification.Condition...);
method public android.os.IBinder onBind(android.content.Intent);
method public abstract void onConnected();
+ method public void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
@@ -34486,7 +34764,6 @@
method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
method public final void requestUnbind() throws android.os.RemoteException;
method public final void setNotificationsShown(java.lang.String[]);
- field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
@@ -34781,6 +35058,17 @@
}
+package android.service.vr {
+
+ public abstract class VrListenerService extends android.app.Service {
+ ctor public VrListenerService();
+ method public static final boolean isVrModePackageEnabled(android.content.Context, android.content.ComponentName);
+ method public android.os.IBinder onBind(android.content.Intent);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
+ }
+
+}
+
package android.service.wallpaper {
public abstract class WallpaperService extends android.app.Service {
@@ -35773,9 +36061,11 @@
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
+ method public void pullExternalCall();
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 void sendCallEvent(java.lang.String, android.os.Bundle);
method public void splitFromConference();
method public void stopDtmfTone();
method public void swapConference();
@@ -35789,6 +36079,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
}
@@ -35799,6 +36090,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);
@@ -35829,6 +36121,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
@@ -35848,6 +36141,7 @@
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
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
field public static final int PROPERTY_WORK_CALL = 32; // 0x20
}
@@ -35962,15 +36256,19 @@
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 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 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);
@@ -35994,9 +36292,12 @@
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 = 33554432; // 0x2000000
+ 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
field public static final int CAPABILITY_HOLD = 1; // 0x1
+ field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -36010,6 +36311,7 @@
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_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
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";
@@ -36019,6 +36321,7 @@
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
}
@@ -36096,7 +36399,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;
@@ -36132,6 +36437,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);
@@ -36282,6 +36588,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();
@@ -36298,6 +36605,7 @@
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 onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -36395,6 +36703,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
@@ -38478,7 +38787,6 @@
method public boolean hasNext();
method public java.util.Iterator<java.lang.String> iterator();
method public java.lang.String next();
- method public void remove();
method public void setString(java.lang.String);
}
@@ -40090,6 +40398,7 @@
method public int describeContents();
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public static android.util.LocaleList getAdjustedDefault();
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getFirstMatch(java.lang.String[]);
@@ -50627,6 +50936,7 @@
public abstract interface Iterable {
method public default void forEach(java.util.function.Consumer<? super T>);
method public abstract java.util.Iterator<T> iterator();
+ method public default java.util.Spliterator<T> spliterator();
}
public class LinkageError extends java.lang.Error {
@@ -57047,6 +57357,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
@@ -57057,7 +57368,9 @@
method public void ensureCapacity(int);
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
+ method public boolean removeIf(java.util.function.Predicate<? super E>);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public void trimToSize();
}
@@ -57140,6 +57453,24 @@
method public static int hashCode(float[]);
method public static int hashCode(double[]);
method public static int hashCode(java.lang.Object[]);
+ method public static void parallelSort(byte[]);
+ method public static void parallelSort(byte[], int, int);
+ method public static void parallelSort(char[]);
+ method public static void parallelSort(char[], int, int);
+ method public static void parallelSort(short[]);
+ method public static void parallelSort(short[], int, int);
+ method public static void parallelSort(int[]);
+ method public static void parallelSort(int[], int, int);
+ method public static void parallelSort(long[]);
+ method public static void parallelSort(long[], int, int);
+ method public static void parallelSort(float[]);
+ method public static void parallelSort(float[], int, int);
+ method public static void parallelSort(double[]);
+ method public static void parallelSort(double[], int, int);
+ method public static void parallelSort(T[]);
+ method public static void parallelSort(T[], int, int);
+ method public static void parallelSort(T[], java.util.Comparator<? super T>);
+ method public static void parallelSort(T[], int, int, java.util.Comparator<? super T>);
method public static void sort(int[]);
method public static void sort(int[], int, int);
method public static void sort(long[]);
@@ -57158,6 +57489,14 @@
method public static void sort(java.lang.Object[], int, int);
method public static void sort(T[], java.util.Comparator<? super T>);
method public static void sort(T[], int, int, java.util.Comparator<? super T>);
+ method public static java.util.Spliterator<T> spliterator(T[]);
+ method public static java.util.Spliterator<T> spliterator(T[], int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[]);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[]);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[]);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int);
method public static java.lang.String toString(long[]);
method public static java.lang.String toString(int[]);
method public static java.lang.String toString(short[]);
@@ -57319,6 +57658,7 @@
method public abstract java.util.Iterator<E> iterator();
method public abstract boolean remove(java.lang.Object);
method public abstract boolean removeAll(java.util.Collection<?>);
+ method public default boolean removeIf(java.util.function.Predicate<? super E>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract int size();
method public abstract java.lang.Object[] toArray();
@@ -57388,7 +57728,23 @@
public abstract interface Comparator {
method public abstract int compare(T, T);
+ method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+ method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>);
+ method public static java.util.Comparator<T> comparingDouble(java.util.function.ToDoubleFunction<? super T>);
+ method public static java.util.Comparator<T> comparingInt(java.util.function.ToIntFunction<? super T>);
+ method public static java.util.Comparator<T> comparingLong(java.util.function.ToLongFunction<? super T>);
method public abstract boolean equals(java.lang.Object);
+ method public static java.util.Comparator<T> naturalOrder();
+ method public static java.util.Comparator<T> nullsFirst(java.util.Comparator<? super T>);
+ method public static java.util.Comparator<T> nullsLast(java.util.Comparator<? super T>);
+ method public static java.util.Comparator<T> reverseOrder();
+ method public default java.util.Comparator<T> reversed();
+ method public default java.util.Comparator<T> thenComparing(java.util.Comparator<? super T>);
+ method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+ method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>);
+ method public default java.util.Comparator<T> thenComparingDouble(java.util.function.ToDoubleFunction<? super T>);
+ method public default java.util.Comparator<T> thenComparingInt(java.util.function.ToIntFunction<? super T>);
+ method public default java.util.Comparator<T> thenComparingLong(java.util.function.ToLongFunction<? super T>);
}
public class ConcurrentModificationException extends java.lang.RuntimeException {
@@ -57485,6 +57841,17 @@
method public abstract int size();
}
+ public class DoubleSummaryStatistics implements java.util.function.DoubleConsumer {
+ ctor public DoubleSummaryStatistics();
+ method public void accept(double);
+ method public void combine(java.util.DoubleSummaryStatistics);
+ method public final double getAverage();
+ method public final long getCount();
+ method public final double getMax();
+ method public final double getMin();
+ method public final double getSum();
+ }
+
public class DuplicateFormatFlagsException extends java.util.IllegalFormatException {
ctor public DuplicateFormatFlagsException(java.lang.String);
method public java.lang.String getFlags();
@@ -57630,6 +57997,7 @@
method public java.lang.Object clone();
method public java.util.Iterator<E> iterator();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class Hashtable extends java.util.Dictionary implements java.lang.Cloneable java.util.Map java.io.Serializable {
@@ -57707,15 +58075,27 @@
ctor public InputMismatchException(java.lang.String);
}
+ public class IntSummaryStatistics implements java.util.function.IntConsumer {
+ ctor public IntSummaryStatistics();
+ method public void accept(int);
+ method public void combine(java.util.IntSummaryStatistics);
+ method public final double getAverage();
+ method public final long getCount();
+ method public final int getMax();
+ method public final int getMin();
+ method public final long getSum();
+ }
+
public class InvalidPropertiesFormatException extends java.io.IOException {
ctor public InvalidPropertiesFormatException(java.lang.Throwable);
ctor public InvalidPropertiesFormatException(java.lang.String);
}
public abstract interface Iterator {
+ method public default void forEachRemaining(java.util.function.Consumer<? super E>);
method public abstract boolean hasNext();
method public abstract E next();
- method public abstract void remove();
+ method public default void remove();
}
public class LinkedHashMap extends java.util.HashMap implements java.util.Map {
@@ -57762,6 +58142,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public abstract interface List implements java.util.Collection {
@@ -57897,25 +58278,51 @@
enum_constant public static final java.util.Locale.Category FORMAT;
}
+ public class LongSummaryStatistics implements java.util.function.IntConsumer java.util.function.LongConsumer {
+ ctor public LongSummaryStatistics();
+ method public void accept(int);
+ method public void accept(long);
+ method public void combine(java.util.LongSummaryStatistics);
+ method public final double getAverage();
+ method public final long getCount();
+ method public final long getMax();
+ method public final long getMin();
+ method public final long getSum();
+ }
+
public abstract interface Map {
method public abstract void clear();
+ method public default V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public default V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public default V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public abstract boolean containsKey(java.lang.Object);
method public abstract boolean containsValue(java.lang.Object);
method public abstract java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public abstract boolean equals(java.lang.Object);
method public default void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public abstract V get(java.lang.Object);
+ method public default V getOrDefault(java.lang.Object, V);
method public abstract int hashCode();
method public abstract boolean isEmpty();
method public abstract java.util.Set<K> keySet();
+ method public default V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public abstract V put(K, V);
method public abstract void putAll(java.util.Map<? extends K, ? extends V>);
+ method public default V putIfAbsent(K, V);
method public abstract V remove(java.lang.Object);
+ method public default boolean remove(java.lang.Object, java.lang.Object);
+ method public default boolean replace(K, V, V);
+ method public default V replace(K, V);
+ method public default void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public abstract int size();
method public abstract java.util.Collection<V> values();
}
public static abstract interface Map.Entry {
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey();
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey(java.util.Comparator<? super K>);
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByValue();
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByValue(java.util.Comparator<? super V>);
method public abstract boolean equals(java.lang.Object);
method public abstract K getKey();
method public abstract V getValue();
@@ -58066,9 +58473,35 @@
method public long orElseThrow(java.util.function.Supplier<X>) throws java.lang.Throwable;
}
+ public abstract interface PrimitiveIterator implements java.util.Iterator {
+ method public abstract void forEachRemaining(T_CONS);
+ }
+
+ public static abstract interface PrimitiveIterator.OfDouble implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public default java.lang.Double next();
+ method public abstract double nextDouble();
+ }
+
+ public static abstract interface PrimitiveIterator.OfInt implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public default java.lang.Integer next();
+ method public abstract int nextInt();
+ }
+
+ public static abstract interface PrimitiveIterator.OfLong implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public default java.lang.Long next();
+ method public abstract long nextLong();
+ }
+
public class PriorityQueue extends java.util.AbstractQueue implements java.io.Serializable {
ctor public PriorityQueue();
ctor public PriorityQueue(int);
+ ctor public PriorityQueue(java.util.Comparator<? super E>);
ctor public PriorityQueue(int, java.util.Comparator<? super E>);
ctor public PriorityQueue(java.util.Collection<? extends E>);
ctor public PriorityQueue(java.util.PriorityQueue<? extends E>);
@@ -58079,6 +58512,7 @@
method public E peek();
method public E poll();
method public int size();
+ method public final java.util.Spliterator<E> spliterator();
}
public class Properties extends java.util.Hashtable {
@@ -58237,7 +58671,6 @@
method public short nextShort();
method public short nextShort(int);
method public int radix();
- method public void remove();
method public java.util.Scanner reset();
method public java.util.Scanner skip(java.util.regex.Pattern);
method public java.util.Scanner skip(java.lang.String);
@@ -58322,6 +58755,111 @@
method public abstract java.util.SortedSet<E> tailSet(E);
}
+ public abstract interface Spliterator {
+ method public abstract int characteristics();
+ method public abstract long estimateSize();
+ method public default void forEachRemaining(java.util.function.Consumer<? super T>);
+ method public default java.util.Comparator<? super T> getComparator();
+ method public default long getExactSizeIfKnown();
+ method public default boolean hasCharacteristics(int);
+ method public abstract boolean tryAdvance(java.util.function.Consumer<? super T>);
+ method public abstract java.util.Spliterator<T> trySplit();
+ field public static final int CONCURRENT = 4096; // 0x1000
+ field public static final int DISTINCT = 1; // 0x1
+ field public static final int IMMUTABLE = 1024; // 0x400
+ field public static final int NONNULL = 256; // 0x100
+ field public static final int ORDERED = 16; // 0x10
+ field public static final int SIZED = 64; // 0x40
+ field public static final int SORTED = 4; // 0x4
+ field public static final int SUBSIZED = 16384; // 0x4000
+ }
+
+ public static abstract interface Spliterator.OfDouble implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract boolean tryAdvance(java.util.function.DoubleConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract interface Spliterator.OfInt implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract boolean tryAdvance(java.util.function.IntConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract interface Spliterator.OfLong implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract boolean tryAdvance(java.util.function.LongConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract interface Spliterator.OfPrimitive implements java.util.Spliterator {
+ method public default void forEachRemaining(T_CONS);
+ method public abstract boolean tryAdvance(T_CONS);
+ method public abstract T_SPLITR trySplit();
+ }
+
+ public final class Spliterators {
+ method public static java.util.Spliterator.OfDouble emptyDoubleSpliterator();
+ method public static java.util.Spliterator.OfInt emptyIntSpliterator();
+ method public static java.util.Spliterator.OfLong emptyLongSpliterator();
+ method public static java.util.Spliterator<T> emptySpliterator();
+ method public static java.util.Iterator<T> iterator(java.util.Spliterator<? extends T>);
+ method public static java.util.PrimitiveIterator.OfInt iterator(java.util.Spliterator.OfInt);
+ method public static java.util.PrimitiveIterator.OfLong iterator(java.util.Spliterator.OfLong);
+ method public static java.util.PrimitiveIterator.OfDouble iterator(java.util.Spliterator.OfDouble);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int, int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Collection<? extends T>, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Iterator<? extends T>, long, int);
+ method public static java.util.Spliterator.OfInt spliterator(java.util.PrimitiveIterator.OfInt, long, int);
+ method public static java.util.Spliterator.OfLong spliterator(java.util.PrimitiveIterator.OfLong, long, int);
+ method public static java.util.Spliterator.OfDouble spliterator(java.util.PrimitiveIterator.OfDouble, long, int);
+ method public static java.util.Spliterator<T> spliteratorUnknownSize(java.util.Iterator<? extends T>, int);
+ method public static java.util.Spliterator.OfInt spliteratorUnknownSize(java.util.PrimitiveIterator.OfInt, int);
+ method public static java.util.Spliterator.OfLong spliteratorUnknownSize(java.util.PrimitiveIterator.OfLong, int);
+ method public static java.util.Spliterator.OfDouble spliteratorUnknownSize(java.util.PrimitiveIterator.OfDouble, int);
+ }
+
+ public static abstract class Spliterators.AbstractDoubleSpliterator implements java.util.Spliterator.OfDouble {
+ ctor protected Spliterators.AbstractDoubleSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractIntSpliterator implements java.util.Spliterator.OfInt {
+ ctor protected Spliterators.AbstractIntSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractLongSpliterator implements java.util.Spliterator.OfLong {
+ ctor protected Spliterators.AbstractLongSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractSpliterator implements java.util.Spliterator {
+ ctor protected Spliterators.AbstractSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator<T> trySplit();
+ }
+
public class Stack extends java.util.Vector {
ctor public Stack();
method public boolean empty();
@@ -58331,6 +58869,15 @@
method public synchronized int search(java.lang.Object);
}
+ public final class StringJoiner {
+ ctor public StringJoiner(java.lang.CharSequence);
+ ctor public StringJoiner(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+ method public java.util.StringJoiner add(java.lang.CharSequence);
+ method public int length();
+ method public java.util.StringJoiner merge(java.util.StringJoiner);
+ method public java.util.StringJoiner setEmptyValue(java.lang.CharSequence);
+ }
+
public class StringTokenizer implements java.util.Enumeration {
ctor public StringTokenizer(java.lang.String, java.lang.String, boolean);
ctor public StringTokenizer(java.lang.String, java.lang.String);
@@ -58452,6 +58999,7 @@
method public E pollFirst();
method public E pollLast();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
method public java.util.SortedSet<E> subSet(E, E);
method public java.util.NavigableSet<E> tailSet(E, boolean);
@@ -58505,9 +59053,11 @@
method public synchronized void removeAllElements();
method public synchronized boolean removeElement(java.lang.Object);
method public synchronized void removeElementAt(int);
+ method public synchronized boolean removeIf(java.util.function.Predicate<? super E>);
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
field protected int elementCount;
@@ -58555,6 +59105,7 @@
method public void put(E) throws java.lang.InterruptedException;
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -58618,6 +59169,78 @@
ctor public CancellationException(java.lang.String);
}
+ public class CompletableFuture implements java.util.concurrent.CompletionStage java.util.concurrent.Future {
+ ctor public CompletableFuture();
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Void> allOf(java.util.concurrent.CompletableFuture<?>...);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Object> anyOf(java.util.concurrent.CompletableFuture<?>...);
+ method public java.util.concurrent.CompletableFuture<U> applyToEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public java.util.concurrent.CompletableFuture<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public java.util.concurrent.CompletableFuture<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>, java.util.concurrent.Executor);
+ method public boolean cancel(boolean);
+ method public boolean complete(T);
+ method public boolean completeExceptionally(java.lang.Throwable);
+ method public static java.util.concurrent.CompletableFuture<U> completedFuture(U);
+ method public java.util.concurrent.CompletableFuture<T> exceptionally(java.util.function.Function<java.lang.Throwable, ? extends T>);
+ method public T get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+ method public T get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+ method public T getNow(T);
+ method public int getNumberOfDependents();
+ method public java.util.concurrent.CompletableFuture<U> handle(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>, java.util.concurrent.Executor);
+ method public boolean isCancelled();
+ method public boolean isCompletedExceptionally();
+ method public boolean isDone();
+ method public T join();
+ method public void obtrudeException(java.lang.Throwable);
+ method public void obtrudeValue(T);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBoth(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEither(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Void> runAsync(java.lang.Runnable);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Void> runAsync(java.lang.Runnable, java.util.concurrent.Executor);
+ method public static java.util.concurrent.CompletableFuture<U> supplyAsync(java.util.function.Supplier<U>);
+ method public static java.util.concurrent.CompletableFuture<U> supplyAsync(java.util.function.Supplier<U>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAccept(java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBoth(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<U> thenApply(java.util.function.Function<? super T, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<V> thenCombine(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public java.util.concurrent.CompletableFuture<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public java.util.concurrent.CompletableFuture<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<U> thenCompose(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public java.util.concurrent.CompletableFuture<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public java.util.concurrent.CompletableFuture<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRun(java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRunAsync(java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRunAsync(java.lang.Runnable, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<T> toCompletableFuture();
+ method public java.util.concurrent.CompletableFuture<T> whenComplete(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public java.util.concurrent.CompletableFuture<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public java.util.concurrent.CompletableFuture<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>, java.util.concurrent.Executor);
+ }
+
+ public static abstract interface CompletableFuture.AsynchronousCompletionTask {
+ }
+
+ public class CompletionException extends java.lang.RuntimeException {
+ ctor protected CompletionException();
+ ctor protected CompletionException(java.lang.String);
+ ctor public CompletionException(java.lang.String, java.lang.Throwable);
+ ctor public CompletionException(java.lang.Throwable);
+ }
+
public abstract interface CompletionService {
method public abstract java.util.concurrent.Future<V> poll();
method public abstract java.util.concurrent.Future<V> poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
@@ -58626,20 +59249,130 @@
method public abstract java.util.concurrent.Future<V> take() throws java.lang.InterruptedException;
}
+ public abstract interface CompletionStage {
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> acceptEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<U> applyToEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public abstract java.util.concurrent.CompletionStage<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public abstract java.util.concurrent.CompletionStage<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<T> exceptionally(java.util.function.Function<java.lang.Throwable, ? extends T>);
+ method public abstract java.util.concurrent.CompletionStage<U> handle(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterBoth(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterEither(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAccept(java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBoth(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<U> thenApply(java.util.function.Function<? super T, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<V> thenCombine(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public abstract java.util.concurrent.CompletionStage<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public abstract java.util.concurrent.CompletionStage<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<U> thenCompose(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenRun(java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenRunAsync(java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenRunAsync(java.lang.Runnable, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletableFuture<T> toCompletableFuture();
+ method public abstract java.util.concurrent.CompletionStage<T> whenComplete(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public abstract java.util.concurrent.CompletionStage<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public abstract java.util.concurrent.CompletionStage<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>, java.util.concurrent.Executor);
+ }
+
public class ConcurrentHashMap extends java.util.AbstractMap implements java.util.concurrent.ConcurrentMap java.io.Serializable {
ctor public ConcurrentHashMap();
ctor public ConcurrentHashMap(int);
ctor public ConcurrentHashMap(java.util.Map<? extends K, ? extends V>);
ctor public ConcurrentHashMap(int, float);
ctor public ConcurrentHashMap(int, float, int);
+ method public V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public boolean contains(java.lang.Object);
method public java.util.Enumeration<V> elements();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+ method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public void forEach(long, java.util.function.BiConsumer<? super K, ? super V>);
+ method public void forEach(long, java.util.function.BiFunction<? super K, ? super V, ? extends U>, java.util.function.Consumer<? super U>);
+ method public void forEachEntry(long, java.util.function.Consumer<? super java.util.Map.Entry<K, V>>);
+ method public void forEachEntry(long, java.util.function.Function<java.util.Map.Entry<K, V>, ? extends U>, java.util.function.Consumer<? super U>);
+ method public void forEachKey(long, java.util.function.Consumer<? super K>);
+ method public void forEachKey(long, java.util.function.Function<? super K, ? extends U>, java.util.function.Consumer<? super U>);
+ method public void forEachValue(long, java.util.function.Consumer<? super V>);
+ method public void forEachValue(long, java.util.function.Function<? super V, ? extends U>, java.util.function.Consumer<? super U>);
+ method public V getOrDefault(java.lang.Object, V);
+ method public java.util.concurrent.ConcurrentHashMap.KeySetView<K, V> keySet(V);
method public java.util.Enumeration<K> keys();
+ method public long mappingCount();
+ method public V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
+ method public static java.util.concurrent.ConcurrentHashMap.KeySetView<K, java.lang.Boolean> newKeySet();
+ method public static java.util.concurrent.ConcurrentHashMap.KeySetView<K, java.lang.Boolean> newKeySet(int);
method public V putIfAbsent(K, V);
+ method public U reduce(long, java.util.function.BiFunction<? super K, ? super V, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public java.util.Map.Entry<K, V> reduceEntries(long, java.util.function.BiFunction<java.util.Map.Entry<K, V>, java.util.Map.Entry<K, V>, ? extends java.util.Map.Entry<K, V>>);
+ method public U reduceEntries(long, java.util.function.Function<java.util.Map.Entry<K, V>, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public double reduceEntriesToDouble(long, java.util.function.ToDoubleFunction<java.util.Map.Entry<K, V>>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceEntriesToInt(long, java.util.function.ToIntFunction<java.util.Map.Entry<K, V>>, int, java.util.function.IntBinaryOperator);
+ method public long reduceEntriesToLong(long, java.util.function.ToLongFunction<java.util.Map.Entry<K, V>>, long, java.util.function.LongBinaryOperator);
+ method public K reduceKeys(long, java.util.function.BiFunction<? super K, ? super K, ? extends K>);
+ method public U reduceKeys(long, java.util.function.Function<? super K, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public double reduceKeysToDouble(long, java.util.function.ToDoubleFunction<? super K>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceKeysToInt(long, java.util.function.ToIntFunction<? super K>, int, java.util.function.IntBinaryOperator);
+ method public long reduceKeysToLong(long, java.util.function.ToLongFunction<? super K>, long, java.util.function.LongBinaryOperator);
+ method public double reduceToDouble(long, java.util.function.ToDoubleBiFunction<? super K, ? super V>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceToInt(long, java.util.function.ToIntBiFunction<? super K, ? super V>, int, java.util.function.IntBinaryOperator);
+ method public long reduceToLong(long, java.util.function.ToLongBiFunction<? super K, ? super V>, long, java.util.function.LongBinaryOperator);
+ method public V reduceValues(long, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
+ method public U reduceValues(long, java.util.function.Function<? super V, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public double reduceValuesToDouble(long, java.util.function.ToDoubleFunction<? super V>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceValuesToInt(long, java.util.function.ToIntFunction<? super V>, int, java.util.function.IntBinaryOperator);
+ method public long reduceValuesToLong(long, java.util.function.ToLongFunction<? super V>, long, java.util.function.LongBinaryOperator);
method public boolean remove(java.lang.Object, java.lang.Object);
method public boolean replace(K, V, V);
method public V replace(K, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public U search(long, java.util.function.BiFunction<? super K, ? super V, ? extends U>);
+ method public U searchEntries(long, java.util.function.Function<java.util.Map.Entry<K, V>, ? extends U>);
+ method public U searchKeys(long, java.util.function.Function<? super K, ? extends U>);
+ method public U searchValues(long, java.util.function.Function<? super V, ? extends U>);
+ }
+
+ static abstract class ConcurrentHashMap.CollectionView implements java.util.Collection java.io.Serializable {
+ method public final void clear();
+ method public abstract boolean contains(java.lang.Object);
+ method public final boolean containsAll(java.util.Collection<?>);
+ method public java.util.concurrent.ConcurrentHashMap<K, V> getMap();
+ method public final boolean isEmpty();
+ method public abstract java.util.Iterator<E> iterator();
+ method public abstract boolean remove(java.lang.Object);
+ method public final boolean removeAll(java.util.Collection<?>);
+ method public final boolean retainAll(java.util.Collection<?>);
+ method public final int size();
+ method public final java.lang.Object[] toArray();
+ method public final T[] toArray(T[]);
+ method public final java.lang.String toString();
+ }
+
+ public static class ConcurrentHashMap.KeySetView extends java.util.concurrent.ConcurrentHashMap.CollectionView implements java.io.Serializable java.util.Set {
+ method public boolean add(K);
+ method public boolean addAll(java.util.Collection<? extends K>);
+ method public boolean contains(java.lang.Object);
+ method public void forEach(java.util.function.Consumer<? super K>);
+ method public V getMappedValue();
+ method public java.util.Iterator<K> iterator();
+ method public boolean remove(java.lang.Object);
+ method public java.util.Spliterator<K> spliterator();
}
public class ConcurrentLinkedDeque extends java.util.AbstractCollection implements java.util.Deque java.io.Serializable {
@@ -58669,6 +59402,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class ConcurrentLinkedQueue extends java.util.AbstractQueue implements java.util.Queue java.io.Serializable {
@@ -58679,6 +59413,7 @@
method public E peek();
method public E poll();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public abstract interface ConcurrentMap implements java.util.Map {
@@ -58710,6 +59445,9 @@
method public K ceilingKey(K);
method public java.util.concurrent.ConcurrentSkipListMap<K, V> clone();
method public java.util.Comparator<? super K> comparator();
+ method public V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.NavigableSet<K> descendingKeySet();
method public java.util.concurrent.ConcurrentNavigableMap<K, V> descendingMap();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
@@ -58717,6 +59455,8 @@
method public K firstKey();
method public java.util.Map.Entry<K, V> floorEntry(K);
method public K floorKey(K);
+ method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public V getOrDefault(java.lang.Object, V);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> headMap(K, boolean);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> headMap(K);
method public java.util.Map.Entry<K, V> higherEntry(K);
@@ -58725,6 +59465,7 @@
method public K lastKey();
method public java.util.Map.Entry<K, V> lowerEntry(K);
method public K lowerKey(K);
+ method public V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public java.util.NavigableSet<K> navigableKeySet();
method public java.util.Map.Entry<K, V> pollFirstEntry();
method public java.util.Map.Entry<K, V> pollLastEntry();
@@ -58732,6 +59473,7 @@
method public boolean remove(java.lang.Object, java.lang.Object);
method public boolean replace(K, V, V);
method public V replace(K, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> subMap(K, boolean, K, boolean);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> subMap(K, K);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> tailMap(K, boolean);
@@ -58759,6 +59501,7 @@
method public E pollFirst();
method public E pollLast();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
method public java.util.NavigableSet<E> subSet(E, E);
method public java.util.NavigableSet<E> tailSet(E, boolean);
@@ -58769,31 +59512,34 @@
ctor public CopyOnWriteArrayList();
ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
ctor public CopyOnWriteArrayList(E[]);
- method public synchronized boolean add(E);
- method public synchronized void add(int, E);
- method public synchronized boolean addAll(java.util.Collection<? extends E>);
- method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
- method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
- method public synchronized boolean addIfAbsent(E);
- method public synchronized void clear();
+ method public boolean add(E);
+ method public void add(int, E);
+ method public boolean addAll(java.util.Collection<? extends E>);
+ method public boolean addAll(int, java.util.Collection<? extends E>);
+ method public int addAllAbsent(java.util.Collection<? extends E>);
+ method public boolean addIfAbsent(E);
+ method public void clear();
method public java.lang.Object clone();
method public boolean contains(java.lang.Object);
method public boolean containsAll(java.util.Collection<?>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
- method public int indexOf(E, int);
method public int indexOf(java.lang.Object);
+ method public int indexOf(E, int);
method public boolean isEmpty();
method public java.util.Iterator<E> iterator();
- method public int lastIndexOf(E, int);
method public int lastIndexOf(java.lang.Object);
- method public java.util.ListIterator<E> listIterator(int);
+ method public int lastIndexOf(E, int);
method public java.util.ListIterator<E> listIterator();
- method public synchronized E remove(int);
- method public synchronized boolean remove(java.lang.Object);
- method public synchronized boolean removeAll(java.util.Collection<?>);
- method public synchronized boolean retainAll(java.util.Collection<?>);
- method public synchronized E set(int, E);
+ method public java.util.ListIterator<E> listIterator(int);
+ method public E remove(int);
+ method public boolean remove(java.lang.Object);
+ method public boolean removeAll(java.util.Collection<?>);
+ method public void replaceAll(java.util.function.UnaryOperator<E>);
+ method public boolean retainAll(java.util.Collection<?>);
+ method public E set(int, E);
method public int size();
+ method public void sort(java.util.Comparator<? super E>);
method public java.util.List<E> subList(int, int);
method public java.lang.Object[] toArray();
method public T[] toArray(T[]);
@@ -58802,8 +59548,11 @@
public class CopyOnWriteArraySet extends java.util.AbstractSet implements java.io.Serializable {
ctor public CopyOnWriteArraySet();
ctor public CopyOnWriteArraySet(java.util.Collection<? extends E>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public java.util.Iterator<E> iterator();
+ method public boolean removeIf(java.util.function.Predicate<? super E>);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class CountDownLatch {
@@ -58814,6 +59563,32 @@
method public long getCount();
}
+ public abstract class CountedCompleter extends java.util.concurrent.ForkJoinTask {
+ ctor protected CountedCompleter(java.util.concurrent.CountedCompleter<?>, int);
+ ctor protected CountedCompleter(java.util.concurrent.CountedCompleter<?>);
+ ctor protected CountedCompleter();
+ method public final void addToPendingCount(int);
+ method public final boolean compareAndSetPendingCount(int, int);
+ method public void complete(T);
+ method public abstract void compute();
+ method public final int decrementPendingCountUnlessZero();
+ method protected final boolean exec();
+ method public final java.util.concurrent.CountedCompleter<?> firstComplete();
+ method public final java.util.concurrent.CountedCompleter<?> getCompleter();
+ method public final int getPendingCount();
+ method public T getRawResult();
+ method public final java.util.concurrent.CountedCompleter<?> getRoot();
+ method public final void helpComplete(int);
+ method public final java.util.concurrent.CountedCompleter<?> nextComplete();
+ method public void onCompletion(java.util.concurrent.CountedCompleter<?>);
+ method public boolean onExceptionalCompletion(java.lang.Throwable, java.util.concurrent.CountedCompleter<?>);
+ method public final void propagateCompletion();
+ method public final void quietlyCompleteRoot();
+ method public final void setPendingCount(int);
+ method protected void setRawResult(T);
+ method public final void tryComplete();
+ }
+
public class CyclicBarrier {
ctor public CyclicBarrier(int, java.lang.Runnable);
ctor public CyclicBarrier(int);
@@ -58904,6 +59679,8 @@
method public static java.util.concurrent.ExecutorService newSingleThreadExecutor(java.util.concurrent.ThreadFactory);
method public static java.util.concurrent.ScheduledExecutorService newSingleThreadScheduledExecutor();
method public static java.util.concurrent.ScheduledExecutorService newSingleThreadScheduledExecutor(java.util.concurrent.ThreadFactory);
+ method public static java.util.concurrent.ExecutorService newWorkStealingPool(int);
+ method public static java.util.concurrent.ExecutorService newWorkStealingPool();
method public static java.util.concurrent.Callable<T> privilegedCallable(java.util.concurrent.Callable<T>);
method public static java.util.concurrent.Callable<T> privilegedCallableUsingCurrentClassLoader(java.util.concurrent.Callable<T>);
method public static java.util.concurrent.ThreadFactory privilegedThreadFactory();
@@ -58917,11 +59694,13 @@
ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean);
method public boolean awaitQuiescence(long, java.util.concurrent.TimeUnit);
method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public static java.util.concurrent.ForkJoinPool commonPool();
method protected int drainTasksTo(java.util.Collection<? super java.util.concurrent.ForkJoinTask<?>>);
method public void execute(java.util.concurrent.ForkJoinTask<?>);
method public void execute(java.lang.Runnable);
method public int getActiveThreadCount();
method public boolean getAsyncMode();
+ method public static int getCommonPoolParallelism();
method public java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory getFactory();
method public int getParallelism();
method public int getPoolSize();
@@ -58959,6 +59738,7 @@
method public static java.util.concurrent.ForkJoinTask<T> adapt(java.lang.Runnable, T);
method public static java.util.concurrent.ForkJoinTask<T> adapt(java.util.concurrent.Callable<? extends T>);
method public boolean cancel(boolean);
+ method public final boolean compareAndSetForkJoinTaskTag(short, short);
method public void complete(V);
method public void completeExceptionally(java.lang.Throwable);
method protected abstract boolean exec();
@@ -58966,6 +59746,7 @@
method public final V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
method public final V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
method public final java.lang.Throwable getException();
+ method public final short getForkJoinTaskTag();
method public static java.util.concurrent.ForkJoinPool getPool();
method public static int getQueuedTaskCount();
method public abstract V getRawResult();
@@ -58984,9 +59765,11 @@
method protected static java.util.concurrent.ForkJoinTask<?> peekNextLocalTask();
method protected static java.util.concurrent.ForkJoinTask<?> pollNextLocalTask();
method protected static java.util.concurrent.ForkJoinTask<?> pollTask();
+ method public final void quietlyComplete();
method public final void quietlyInvoke();
method public final void quietlyJoin();
method public void reinitialize();
+ method public final short setForkJoinTaskTag(short);
method protected abstract void setRawResult(V);
method public boolean tryUnfork();
}
@@ -59060,6 +59843,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
method public E takeFirst() throws java.lang.InterruptedException;
method public E takeLast() throws java.lang.InterruptedException;
@@ -59080,6 +59864,7 @@
method public void put(E) throws java.lang.InterruptedException;
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -59099,6 +59884,7 @@
method public void put(E);
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
method public void transfer(E) throws java.lang.InterruptedException;
method public boolean tryTransfer(E);
@@ -59146,6 +59932,7 @@
method public void put(E);
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -59249,6 +60036,7 @@
method public void put(E) throws java.lang.InterruptedException;
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -59378,112 +60166,136 @@
public class AtomicInteger extends java.lang.Number implements java.io.Serializable {
ctor public AtomicInteger(int);
ctor public AtomicInteger();
+ method public final int accumulateAndGet(int, java.util.function.IntBinaryOperator);
method public final int addAndGet(int);
method public final boolean compareAndSet(int, int);
method public final int decrementAndGet();
method public double doubleValue();
method public float floatValue();
method public final int get();
+ method public final int getAndAccumulate(int, java.util.function.IntBinaryOperator);
method public final int getAndAdd(int);
method public final int getAndDecrement();
method public final int getAndIncrement();
method public final int getAndSet(int);
+ method public final int getAndUpdate(java.util.function.IntUnaryOperator);
method public final int incrementAndGet();
method public int intValue();
method public final void lazySet(int);
method public long longValue();
method public final void set(int);
+ method public final int updateAndGet(java.util.function.IntUnaryOperator);
method public final boolean weakCompareAndSet(int, int);
}
public class AtomicIntegerArray implements java.io.Serializable {
ctor public AtomicIntegerArray(int);
ctor public AtomicIntegerArray(int[]);
+ method public final int accumulateAndGet(int, int, java.util.function.IntBinaryOperator);
method public final int addAndGet(int, int);
method public final boolean compareAndSet(int, int, int);
method public final int decrementAndGet(int);
method public final int get(int);
+ method public final int getAndAccumulate(int, int, java.util.function.IntBinaryOperator);
method public final int getAndAdd(int, int);
method public final int getAndDecrement(int);
method public final int getAndIncrement(int);
method public final int getAndSet(int, int);
+ method public final int getAndUpdate(int, java.util.function.IntUnaryOperator);
method public final int incrementAndGet(int);
method public final void lazySet(int, int);
method public final int length();
method public final void set(int, int);
+ method public final int updateAndGet(int, java.util.function.IntUnaryOperator);
method public final boolean weakCompareAndSet(int, int, int);
}
public abstract class AtomicIntegerFieldUpdater {
ctor protected AtomicIntegerFieldUpdater();
+ method public final int accumulateAndGet(T, int, java.util.function.IntBinaryOperator);
method public int addAndGet(T, int);
method public abstract boolean compareAndSet(T, int, int);
method public int decrementAndGet(T);
method public abstract int get(T);
+ method public final int getAndAccumulate(T, int, java.util.function.IntBinaryOperator);
method public int getAndAdd(T, int);
method public int getAndDecrement(T);
method public int getAndIncrement(T);
method public int getAndSet(T, int);
+ method public final int getAndUpdate(T, java.util.function.IntUnaryOperator);
method public int incrementAndGet(T);
method public abstract void lazySet(T, int);
method public static java.util.concurrent.atomic.AtomicIntegerFieldUpdater<U> newUpdater(java.lang.Class<U>, java.lang.String);
method public abstract void set(T, int);
+ method public final int updateAndGet(T, java.util.function.IntUnaryOperator);
method public abstract boolean weakCompareAndSet(T, int, int);
}
public class AtomicLong extends java.lang.Number implements java.io.Serializable {
ctor public AtomicLong(long);
ctor public AtomicLong();
+ method public final long accumulateAndGet(long, java.util.function.LongBinaryOperator);
method public final long addAndGet(long);
method public final boolean compareAndSet(long, long);
method public final long decrementAndGet();
method public double doubleValue();
method public float floatValue();
method public final long get();
+ method public final long getAndAccumulate(long, java.util.function.LongBinaryOperator);
method public final long getAndAdd(long);
method public final long getAndDecrement();
method public final long getAndIncrement();
method public final long getAndSet(long);
+ method public final long getAndUpdate(java.util.function.LongUnaryOperator);
method public final long incrementAndGet();
method public int intValue();
method public final void lazySet(long);
method public long longValue();
method public final void set(long);
+ method public final long updateAndGet(java.util.function.LongUnaryOperator);
method public final boolean weakCompareAndSet(long, long);
}
public class AtomicLongArray implements java.io.Serializable {
ctor public AtomicLongArray(int);
ctor public AtomicLongArray(long[]);
+ method public final long accumulateAndGet(int, long, java.util.function.LongBinaryOperator);
method public long addAndGet(int, long);
method public final boolean compareAndSet(int, long, long);
method public final long decrementAndGet(int);
method public final long get(int);
+ method public final long getAndAccumulate(int, long, java.util.function.LongBinaryOperator);
method public final long getAndAdd(int, long);
method public final long getAndDecrement(int);
method public final long getAndIncrement(int);
method public final long getAndSet(int, long);
+ method public final long getAndUpdate(int, java.util.function.LongUnaryOperator);
method public final long incrementAndGet(int);
method public final void lazySet(int, long);
method public final int length();
method public final void set(int, long);
+ method public final long updateAndGet(int, java.util.function.LongUnaryOperator);
method public final boolean weakCompareAndSet(int, long, long);
}
public abstract class AtomicLongFieldUpdater {
ctor protected AtomicLongFieldUpdater();
+ method public final long accumulateAndGet(T, long, java.util.function.LongBinaryOperator);
method public long addAndGet(T, long);
method public abstract boolean compareAndSet(T, long, long);
method public long decrementAndGet(T);
method public abstract long get(T);
+ method public final long getAndAccumulate(T, long, java.util.function.LongBinaryOperator);
method public long getAndAdd(T, long);
method public long getAndDecrement(T);
method public long getAndIncrement(T);
method public long getAndSet(T, long);
+ method public final long getAndUpdate(T, java.util.function.LongUnaryOperator);
method public long incrementAndGet(T);
method public abstract void lazySet(T, long);
method public static java.util.concurrent.atomic.AtomicLongFieldUpdater<U> newUpdater(java.lang.Class<U>, java.lang.String);
method public abstract void set(T, long);
+ method public final long updateAndGet(T, java.util.function.LongUnaryOperator);
method public abstract boolean weakCompareAndSet(T, long, long);
}
@@ -59501,34 +60313,46 @@
public class AtomicReference implements java.io.Serializable {
ctor public AtomicReference(V);
ctor public AtomicReference();
+ method public final V accumulateAndGet(V, java.util.function.BinaryOperator<V>);
method public final boolean compareAndSet(V, V);
method public final V get();
+ method public final V getAndAccumulate(V, java.util.function.BinaryOperator<V>);
method public final V getAndSet(V);
+ method public final V getAndUpdate(java.util.function.UnaryOperator<V>);
method public final void lazySet(V);
method public final void set(V);
+ method public final V updateAndGet(java.util.function.UnaryOperator<V>);
method public final boolean weakCompareAndSet(V, V);
}
public class AtomicReferenceArray implements java.io.Serializable {
ctor public AtomicReferenceArray(int);
ctor public AtomicReferenceArray(E[]);
+ method public final E accumulateAndGet(int, E, java.util.function.BinaryOperator<E>);
method public final boolean compareAndSet(int, E, E);
method public final E get(int);
+ method public final E getAndAccumulate(int, E, java.util.function.BinaryOperator<E>);
method public final E getAndSet(int, E);
+ method public final E getAndUpdate(int, java.util.function.UnaryOperator<E>);
method public final void lazySet(int, E);
method public final int length();
method public final void set(int, E);
+ method public final E updateAndGet(int, java.util.function.UnaryOperator<E>);
method public final boolean weakCompareAndSet(int, E, E);
}
public abstract class AtomicReferenceFieldUpdater {
ctor protected AtomicReferenceFieldUpdater();
+ method public final V accumulateAndGet(T, V, java.util.function.BinaryOperator<V>);
method public abstract boolean compareAndSet(T, V, V);
method public abstract V get(T);
+ method public final V getAndAccumulate(T, V, java.util.function.BinaryOperator<V>);
method public V getAndSet(T, V);
+ method public final V getAndUpdate(T, java.util.function.UnaryOperator<V>);
method public abstract void lazySet(T, V);
method public static java.util.concurrent.atomic.AtomicReferenceFieldUpdater<U, W> newUpdater(java.lang.Class<U>, java.lang.Class<W>, java.lang.String);
method public abstract void set(T, V);
+ method public final V updateAndGet(T, java.util.function.UnaryOperator<V>);
method public abstract boolean weakCompareAndSet(T, V, V);
}
@@ -59543,6 +60367,59 @@
method public boolean weakCompareAndSet(V, V, int, int);
}
+ public class DoubleAccumulator extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public DoubleAccumulator(java.util.function.DoubleBinaryOperator, double);
+ method public void accumulate(double);
+ method public double doubleValue();
+ method public float floatValue();
+ method public double get();
+ method public double getThenReset();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ }
+
+ public class DoubleAdder extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public DoubleAdder();
+ method public void add(double);
+ method public double doubleValue();
+ method public float floatValue();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ method public double sum();
+ method public double sumThenReset();
+ }
+
+ public class LongAccumulator extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public LongAccumulator(java.util.function.LongBinaryOperator, long);
+ method public void accumulate(long);
+ method public double doubleValue();
+ method public float floatValue();
+ method public long get();
+ method public long getThenReset();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ }
+
+ public class LongAdder extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public LongAdder();
+ method public void add(long);
+ method public void decrement();
+ method public double doubleValue();
+ method public float floatValue();
+ method public void increment();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ method public long sum();
+ method public long sumThenReset();
+ }
+
+ abstract class Striped64 extends java.lang.Number {
+ }
+
}
package java.util.concurrent.locks {
@@ -59750,6 +60627,34 @@
method public void unlock();
}
+ public class StampedLock implements java.io.Serializable {
+ ctor public StampedLock();
+ method public java.util.concurrent.locks.Lock asReadLock();
+ method public java.util.concurrent.locks.ReadWriteLock asReadWriteLock();
+ method public java.util.concurrent.locks.Lock asWriteLock();
+ method public int getReadLockCount();
+ method public boolean isReadLocked();
+ method public boolean isWriteLocked();
+ method public long readLock();
+ method public long readLockInterruptibly() throws java.lang.InterruptedException;
+ method public long tryConvertToOptimisticRead(long);
+ method public long tryConvertToReadLock(long);
+ method public long tryConvertToWriteLock(long);
+ method public long tryOptimisticRead();
+ method public long tryReadLock();
+ method public long tryReadLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public boolean tryUnlockRead();
+ method public boolean tryUnlockWrite();
+ method public long tryWriteLock();
+ method public long tryWriteLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public void unlock(long);
+ method public void unlockRead(long);
+ method public void unlockWrite(long);
+ method public boolean validate(long);
+ method public long writeLock();
+ method public long writeLockInterruptibly() throws java.lang.InterruptedException;
+ }
+
}
package java.util.function {
diff --git a/api/removed.txt b/api/removed.txt
index 115224c..2f55373 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -43,6 +43,14 @@
}
+package android.media.tv {
+
+ public class TvView extends android.view.ViewGroup {
+ method public void requestUnblockContent(android.media.tv.TvContentRating);
+ }
+
+}
+
package android.net {
public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
@@ -201,14 +209,6 @@
}
-package android.service.notification {
-
- public abstract class ConditionProviderService extends android.app.Service {
- method public void onRequestConditions(int);
- }
-
-}
-
package android.test.mock {
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
diff --git a/api/system-current.txt b/api/system-current.txt
index 8347cd7..6433194 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -52,6 +52,7 @@
field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
field public static final java.lang.String BIND_VOICE_INTERACTION = "android.permission.BIND_VOICE_INTERACTION";
field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
+ field public static final java.lang.String BIND_VR_LISTENER_SERVICE = "android.permission.BIND_VR_LISTENER_SERVICE";
field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN";
@@ -193,6 +194,7 @@
field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
+ field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
field public static final java.lang.String SERIAL_PORT = "android.permission.SERIAL_PORT";
field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -972,6 +974,7 @@
field public static final int nextFocusLeft = 16842977; // 0x10100e1
field public static final int nextFocusRight = 16842978; // 0x10100e2
field public static final int nextFocusUp = 16842979; // 0x10100e3
+ field public static final int nfcAntennaPositionDrawable = 16844063; // 0x101051f
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -3554,7 +3557,7 @@
method public android.view.View getCurrentFocus();
method public android.app.FragmentManager getFragmentManager();
method public android.content.Intent getIntent();
- method public deprecated java.lang.Object getLastNonConfigurationInstance();
+ method public java.lang.Object getLastNonConfigurationInstance();
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
@@ -3657,7 +3660,7 @@
method protected void onRestoreInstanceState(android.os.Bundle);
method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
method protected void onResume();
- method public deprecated java.lang.Object onRetainNonConfigurationInstance();
+ method public java.lang.Object onRetainNonConfigurationInstance();
method protected void onSaveInstanceState(android.os.Bundle);
method public void onSaveInstanceState(android.os.Bundle, android.os.PersistableBundle);
method public boolean onSearchRequested(android.view.SearchEvent);
@@ -3721,7 +3724,7 @@
method public deprecated void setTitleColor(int);
method public void setVisible(boolean);
method public final void setVolumeControlStream(int);
- method public void setVrMode(boolean);
+ method public void setVrModeEnabled(boolean, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean shouldShowRequestPermissionRationale(java.lang.String);
method public boolean shouldUpRecreateTask(android.content.Intent);
method public boolean showAssist(android.os.Bundle);
@@ -5390,7 +5393,7 @@
}
public static class NotificationManager.Policy implements android.os.Parcelable {
- ctor public deprecated NotificationManager.Policy(int, int, int);
+ ctor public NotificationManager.Policy(int, int, int);
ctor public NotificationManager.Policy(int, int, int, int);
method public int describeContents();
method public static java.lang.String priorityCategoriesToString(int);
@@ -6029,7 +6032,7 @@
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
method public int getUserProvisioningState();
method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
- method public java.lang.String getWifiMacAddress();
+ method public java.lang.String getWifiMacAddress(android.content.ComponentName);
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -6127,6 +6130,7 @@
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
+ field public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5; // 0x5
field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
@@ -6635,8 +6639,8 @@
method public long getTxPackets();
method public int getUid();
field public static final int ROAMING_ALL = -1; // 0xffffffff
- field public static final int ROAMING_DEFAULT = 1; // 0x1
- field public static final int ROAMING_ROAMING = 2; // 0x2
+ field public static final int ROAMING_NO = 1; // 0x1
+ field public static final int ROAMING_YES = 2; // 0x2
field public static final int STATE_ALL = -1; // 0xffffffff
field public static final int STATE_DEFAULT = 1; // 0x1
field public static final int STATE_FOREGROUND = 2; // 0x2
@@ -8413,7 +8417,7 @@
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
field public static final java.lang.String DROPBOX_SERVICE = "dropbox";
field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint";
- field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardwareproperties";
+ field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
@@ -8444,7 +8448,9 @@
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";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
@@ -9644,6 +9650,7 @@
field public int flags;
field public int largestWidthLimitDp;
field public java.lang.String manageSpaceActivityName;
+ field public java.lang.String minSdkVersion;
field public java.lang.String nativeLibraryDir;
field public java.lang.String permission;
field public java.lang.String processName;
@@ -9777,13 +9784,20 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
+ method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
+ method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
+ method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
+ 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 boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9796,6 +9810,18 @@
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 void setActivity(android.content.ComponentName);
+ method public void setChangedSince(long);
+ method public void setPackage(java.lang.String);
+ method public void setQueryFlags(int);
+ field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
+ field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+ field public static final int FLAG_GET_PINNED = 2; // 0x2
}
public class PackageInfo implements android.os.Parcelable {
@@ -9873,6 +9899,7 @@
method public java.lang.String[] getNames() throws java.io.IOException;
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
+ method public void removeSplit(java.lang.String) throws java.io.IOException;
method public void setStagingProgress(float);
}
@@ -9902,6 +9929,7 @@
public static class PackageInstaller.SessionParams implements android.os.Parcelable {
ctor public PackageInstaller.SessionParams(int);
method public int describeContents();
+ method public void setAllowDowngrade(boolean);
method public void setAppIcon(android.graphics.Bitmap);
method public void setAppLabel(java.lang.CharSequence);
method public void setAppPackageName(java.lang.String);
@@ -10186,6 +10214,8 @@
field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99
field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a
field public static final int INSTALL_SUCCEEDED = 1; // 0x1
+ field public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1; // 0xffffffff
+ field public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1; // 0x1
field public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
@@ -10353,6 +10383,59 @@
field public java.lang.String permission;
}
+ public class ShortcutInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.content.ComponentName getActivityComponent();
+ method public android.os.PersistableBundle getExtras();
+ method public java.lang.String getId();
+ method public android.content.Intent getIntent();
+ method public long getLastChangedTimestamp();
+ method public java.lang.String getPackageName();
+ method public java.lang.String getTitle();
+ method public int getWeight();
+ method public boolean hasIconFile();
+ method public boolean hasIconResource();
+ method public boolean hasKeyFieldsOnly();
+ method public boolean isDynamic();
+ method public boolean isPinned();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
+ field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
+ field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
+ field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+ field public static final int FLAG_DYNAMIC = 1; // 0x1
+ field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
+ field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
+ field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
+ field public static final int FLAG_PINNED = 2; // 0x2
+ }
+
+ public static class ShortcutInfo.Builder {
+ ctor public ShortcutInfo.Builder(android.content.Context);
+ method public android.content.pm.ShortcutInfo build();
+ method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+ 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 setId(java.lang.String);
+ method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+ method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
+ method public android.content.pm.ShortcutInfo.Builder setWeight(int);
+ }
+
+ public class ShortcutManager {
+ method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
+ method public void deleteAllDynamicShortcuts();
+ method public void deleteDynamicShortcut(java.lang.String);
+ method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+ method public int getIconMaxDimensions();
+ method public int getMaxDynamicShortcutCount();
+ method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+ method public long getRateLimitResetTime();
+ method public int getRemainingCallCount();
+ 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);
@@ -10429,7 +10512,6 @@
method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
- method public int getChangingConfigurations();
method public int getColorForState(int[], int);
method public int getDefaultColor();
method public boolean isOpaque();
@@ -10441,6 +10523,7 @@
public abstract class ComplexColor {
ctor public ComplexColor();
+ method public int getChangingConfigurations();
method public abstract int getDefaultColor();
method public boolean isStateful();
}
@@ -10858,7 +10941,6 @@
method public boolean hasNext();
method public java.util.Iterator<android.database.CursorJoiner.Result> iterator();
method public android.database.CursorJoiner.Result next();
- method public void remove();
}
public static final class CursorJoiner.Result extends java.lang.Enum {
@@ -13229,7 +13311,8 @@
method public int getGradientType();
method public int getOpacity();
method public android.graphics.drawable.GradientDrawable.Orientation getOrientation();
- method public boolean isUseLevel();
+ method public int getShape();
+ method public boolean getUseLevel();
method public void setAlpha(int);
method public void setColor(int);
method public void setColor(android.content.res.ColorStateList);
@@ -13348,9 +13431,9 @@
method public void setPaddingMode(int);
method public void setPaddingRelative(int, int, int, int);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+ field public static final int INSET_UNDEFINED = -2147483648; // 0x80000000
field public static final int PADDING_MODE_NEST = 0; // 0x0
field public static final int PADDING_MODE_STACK = 1; // 0x1
- field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000
}
public class LevelListDrawable extends android.graphics.drawable.DrawableContainer {
@@ -14098,6 +14181,7 @@
public static abstract class CameraCaptureSession.CaptureCallback {
ctor public CameraCaptureSession.CaptureCallback();
+ method public void onCaptureBufferLost(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.view.Surface, long);
method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
@@ -20343,24 +20427,6 @@
method public boolean hasFullBiasNanos();
method public boolean hasLeapSecond();
method public boolean hasTimeUncertaintyNanos();
- method public void reset();
- method public void resetBiasNanos();
- method public void resetBiasUncertaintyNanos();
- method public void resetDriftNanosPerSecond();
- method public void resetDriftUncertaintyNanosPerSecond();
- method public void resetFullBiasNanos();
- method public void resetLeapSecond();
- method public void resetTimeUncertaintyNanos();
- method public void set(android.location.GnssClock);
- method public void setBiasNanos(double);
- method public void setBiasUncertaintyNanos(double);
- method public void setDriftNanosPerSecond(double);
- method public void setDriftUncertaintyNanosPerSecond(double);
- method public void setFullBiasNanos(long);
- method public void setHardwareClockDiscontinuityCount(int);
- method public void setLeapSecond(int);
- method public void setTimeNanos(long);
- method public void setTimeUncertaintyNanos(double);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssClock> CREATOR;
}
@@ -20391,31 +20457,6 @@
method public boolean hasCarrierPhaseUncertainty();
method public boolean hasSnrInDb();
method public boolean isPseudorangeRateCorrected();
- method public void reset();
- method public void resetCarrierCycles();
- method public void resetCarrierFrequencyHz();
- method public void resetCarrierPhase();
- method public void resetCarrierPhaseUncertainty();
- method public void resetSnrInDb();
- method public void set(android.location.GnssMeasurement);
- method public void setAccumulatedDeltaRangeMeters(double);
- method public void setAccumulatedDeltaRangeState(int);
- method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
- method public void setCarrierCycles(long);
- method public void setCarrierFrequencyHz(float);
- method public void setCarrierPhase(double);
- method public void setCarrierPhaseUncertainty(double);
- method public void setCn0DbHz(double);
- method public void setConstellationType(int);
- method public void setMultipathIndicator(int);
- method public void setPseudorangeRateMetersPerSecond(double);
- method public void setPseudorangeRateUncertaintyMetersPerSecond(double);
- method public void setReceivedSvTimeNanos(long);
- method public void setReceivedSvTimeUncertaintyNanos(long);
- method public void setSnrInDb(double);
- method public void setState(int);
- method public void setSvid(int);
- method public void setTimeOffsetNanos(double);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ADR_STATE_CYCLE_SLIP = 4; // 0x4
field public static final int ADR_STATE_RESET = 2; // 0x2
@@ -20465,14 +20506,6 @@
method public int getSubmessageId();
method public int getSvid();
method public int getType();
- method public void reset();
- method public void set(android.location.GnssNavigationMessage);
- method public void setData(byte[]);
- method public void setMessageId(int);
- method public void setStatus(int);
- method public void setSubmessageId(int);
- method public void setSvid(int);
- method public void setType(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessage> CREATOR;
field public static final int STATUS_PARITY_PASSED = 1; // 0x1
@@ -21200,7 +21233,7 @@
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
- method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
+ method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -21354,7 +21387,7 @@
public static abstract class AudioManager.AudioRecordingCallback {
ctor public AudioManager.AudioRecordingCallback();
- method public void onRecordConfigChanged();
+ method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
}
public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -21431,7 +21464,7 @@
method public abstract void onRoutingChanged(android.media.AudioRecord);
}
- public final class AudioRecordConfiguration implements android.os.Parcelable {
+ public final class AudioRecordingConfiguration implements android.os.Parcelable {
method public int describeContents();
method public android.media.AudioDeviceInfo getAudioDevice();
method public int getClientAudioSessionId();
@@ -21439,7 +21472,7 @@
method public android.media.AudioFormat getClientFormat();
method public android.media.AudioFormat getFormat();
method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+ field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
}
public abstract interface AudioRouting {
@@ -21647,37 +21680,129 @@
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final java.lang.String TAG_APERTURE = "FNumber";
+ field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+ field public static final java.lang.String TAG_ARTIST = "Artist";
+ field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+ field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+ field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+ field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+ field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+ field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+ field public static final java.lang.String TAG_COMPRESSION = "Compression";
+ field public static final java.lang.String TAG_CONTRAST = "Contrast";
+ field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+ field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
field public static final java.lang.String TAG_DATETIME = "DateTime";
field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+ field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+ field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+ field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
field public static final java.lang.String TAG_FLASH = "Flash";
+ field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+ field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+ field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+ field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+ field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+ field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+ field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+ field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+ field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+ field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+ field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+ field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+ field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+ field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+ field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+ field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+ field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+ field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+ field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+ field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+ field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
field public static final java.lang.String TAG_MAKE = "Make";
+ field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+ field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
field public static final java.lang.String TAG_MODEL = "Model";
+ field public static final java.lang.String TAG_OECF = "OECF";
field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+ field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+ field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+ field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+ field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+ field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+ field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+ field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+ field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+ field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+ field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+ field public static final java.lang.String TAG_SATURATION = "Saturation";
+ field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+ field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+ field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+ field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+ field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+ field public static final java.lang.String TAG_SOFTWARE = "Software";
+ field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+ field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+ field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+ field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+ field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+ field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+ field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+ field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+ field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+ field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+ field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+ field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+ field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+ field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+ field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+ field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
field public static final int WHITEBALANCE_AUTO = 0; // 0x0
field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
}
@@ -21875,6 +22000,7 @@
field public static final int ERROR_NO_KEY = 1; // 0x1
field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
+ field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6
}
public static final class MediaCodec.CryptoInfo {
@@ -22341,6 +22467,7 @@
method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String) throws java.io.IOException;
+ method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
method public void unselectTrack(int);
@@ -22401,6 +22528,7 @@
field public static final java.lang.String KEY_DURATION = "durationUs";
field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
+ field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
field public static final java.lang.String KEY_HEIGHT = "height";
field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
field public static final java.lang.String KEY_IS_ADTS = "is-adts";
@@ -23703,6 +23831,7 @@
public static class AudioMix.Builder {
ctor public AudioMix.Builder(android.media.audiopolicy.AudioMixingRule) throws java.lang.IllegalArgumentException;
method public android.media.audiopolicy.AudioMix build() throws java.lang.IllegalArgumentException;
+ method public android.media.audiopolicy.AudioMix.Builder setDevice(android.media.AudioDeviceInfo) throws java.lang.IllegalArgumentException;
method public android.media.audiopolicy.AudioMix.Builder setFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
method public android.media.audiopolicy.AudioMix.Builder setRouteFlags(int) throws java.lang.IllegalArgumentException;
}
@@ -24867,7 +24996,6 @@
method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
method protected void onLayout(boolean, int, int, int, int);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
- method public deprecated void requestUnblockContent(android.media.tv.TvContentRating);
method public void reset();
method public void selectTrack(int, java.lang.String);
method public void sendAppPrivateCommand(java.lang.String, android.os.Bundle);
@@ -31338,6 +31466,7 @@
public class Process {
ctor public Process();
method public static final long getElapsedCpuTime();
+ method public static final int[] getExclusiveCores();
method public static final int getGidForName(java.lang.String);
method public static final long getStartElapsedRealtime();
method public static final long getStartUptimeMillis();
@@ -31683,6 +31812,147 @@
}
+package android.os.health {
+
+ public class HealthStats {
+ method public java.lang.String getDataType();
+ method public long getMeasurement(int);
+ method public int getMeasurementKeyAt(int);
+ method public int getMeasurementKeyCount();
+ method public java.util.Map<java.lang.String, java.lang.Long> getMeasurements(int);
+ method public int getMeasurementsKeyAt(int);
+ method public int getMeasurementsKeyCount();
+ method public java.util.Map<java.lang.String, android.os.health.HealthStats> getStats(int);
+ method public int getStatsKeyAt(int);
+ method public int getStatsKeyCount();
+ method public android.os.health.TimerStat getTimer(int);
+ method public int getTimerCount(int);
+ method public int getTimerKeyAt(int);
+ method public int getTimerKeyCount();
+ method public long getTimerTime(int);
+ method public java.util.Map<java.lang.String, android.os.health.TimerStat> getTimers(int);
+ method public int getTimersKeyAt(int);
+ method public int getTimersKeyCount();
+ method public boolean hasMeasurement(int);
+ method public boolean hasMeasurements(int);
+ method public boolean hasStats(int);
+ method public boolean hasTimer(int);
+ method public boolean hasTimers(int);
+ }
+
+ public final class PackageHealthStats {
+ field public static final int MEASUREMENTS_WAKEUP_ALARMS_COUNT = 40002; // 0x9c42
+ field public static final int STATS_SERVICES = 40001; // 0x9c41
+ }
+
+ public final class PidHealthStats {
+ field public static final int MEASUREMENT_WAKE_NESTING_COUNT = 20001; // 0x4e21
+ field public static final int MEASUREMENT_WAKE_START_MS = 20003; // 0x4e23
+ field public static final int MEASUREMENT_WAKE_SUM_MS = 20002; // 0x4e22
+ }
+
+ public final class ProcessHealthStats {
+ field public static final int MEASUREMENT_ANR_COUNT = 30005; // 0x7535
+ field public static final int MEASUREMENT_CRASHES_COUNT = 30004; // 0x7534
+ field public static final int MEASUREMENT_FOREGROUND_MS = 30006; // 0x7536
+ field public static final int MEASUREMENT_STARTS_COUNT = 30003; // 0x7533
+ field public static final int MEASUREMENT_SYSTEM_TIME_MS = 30002; // 0x7532
+ field public static final int MEASUREMENT_USER_TIME_MS = 30001; // 0x7531
+ }
+
+ public final class ServiceHealthStats {
+ field public static final int MEASUREMENT_LAUNCH_COUNT = 50002; // 0xc352
+ field public static final int MEASUREMENT_START_SERVICE_COUNT = 50001; // 0xc351
+ }
+
+ public class SystemHealthManager {
+ method public static android.os.health.SystemHealthManager from(android.content.Context);
+ method public android.os.health.HealthStats takeMyUidSnapshot();
+ method public android.os.health.HealthStats takeUidSnapshot(int);
+ method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
+ }
+
+ public class TimerStat implements android.os.Parcelable {
+ ctor public TimerStat();
+ ctor public TimerStat(int, long);
+ ctor public TimerStat(android.os.Parcel);
+ method public int describeContents();
+ method public int getCount();
+ method public long getTime();
+ method public void setCount(int);
+ method public void setTime(long);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.health.TimerStat> CREATOR;
+ }
+
+ public final class UidHealthStats {
+ field public static final int MEASUREMENT_BLUETOOTH_IDLE_MS = 10020; // 0x2724
+ field public static final int MEASUREMENT_BLUETOOTH_POWER_MAMS = 10023; // 0x2727
+ field public static final int MEASUREMENT_BLUETOOTH_RX_BYTES = 10052; // 0x2744
+ field public static final int MEASUREMENT_BLUETOOTH_RX_MS = 10021; // 0x2725
+ field public static final int MEASUREMENT_BLUETOOTH_RX_PACKETS = 10058; // 0x274a
+ field public static final int MEASUREMENT_BLUETOOTH_TX_BYTES = 10053; // 0x2745
+ field public static final int MEASUREMENT_BLUETOOTH_TX_MS = 10022; // 0x2726
+ field public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = 10059; // 0x274b
+ field public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = 10046; // 0x273e
+ field public static final int MEASUREMENT_CPU_POWER_MAUS = 10064; // 0x2750
+ field public static final int MEASUREMENT_MOBILE_IDLE_MS = 10024; // 0x2728
+ field public static final int MEASUREMENT_MOBILE_POWER_MAMS = 10027; // 0x272b
+ field public static final int MEASUREMENT_MOBILE_RX_BYTES = 10048; // 0x2740
+ field public static final int MEASUREMENT_MOBILE_RX_MS = 10025; // 0x2729
+ field public static final int MEASUREMENT_MOBILE_RX_PACKETS = 10054; // 0x2746
+ field public static final int MEASUREMENT_MOBILE_TX_BYTES = 10049; // 0x2741
+ field public static final int MEASUREMENT_MOBILE_TX_MS = 10026; // 0x272a
+ field public static final int MEASUREMENT_MOBILE_TX_PACKETS = 10055; // 0x2747
+ field public static final int MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = 10045; // 0x273d
+ field public static final int MEASUREMENT_REALTIME_BATTERY_MS = 10001; // 0x2711
+ field public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = 10003; // 0x2713
+ field public static final int MEASUREMENT_SYSTEM_CPU_TIME_US = 10063; // 0x274f
+ field public static final int MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT = 10047; // 0x273f
+ field public static final int MEASUREMENT_UPTIME_BATTERY_MS = 10002; // 0x2712
+ field public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = 10004; // 0x2714
+ field public static final int MEASUREMENT_USER_CPU_TIME_US = 10062; // 0x274e
+ field public static final int MEASUREMENT_WIFI_FULL_LOCK_MS = 10029; // 0x272d
+ field public static final int MEASUREMENT_WIFI_IDLE_MS = 10016; // 0x2720
+ field public static final int MEASUREMENT_WIFI_MULTICAST_MS = 10031; // 0x272f
+ field public static final int MEASUREMENT_WIFI_POWER_MAMS = 10019; // 0x2723
+ field public static final int MEASUREMENT_WIFI_RUNNING_MS = 10028; // 0x272c
+ field public static final int MEASUREMENT_WIFI_RX_BYTES = 10050; // 0x2742
+ field public static final int MEASUREMENT_WIFI_RX_MS = 10017; // 0x2721
+ field public static final int MEASUREMENT_WIFI_RX_PACKETS = 10056; // 0x2748
+ field public static final int MEASUREMENT_WIFI_TX_BYTES = 10051; // 0x2743
+ field public static final int MEASUREMENT_WIFI_TX_MS = 10018; // 0x2722
+ field public static final int MEASUREMENT_WIFI_TX_PACKETS = 10057; // 0x2749
+ field public static final int STATS_PACKAGES = 10015; // 0x271f
+ field public static final int STATS_PIDS = 10013; // 0x271d
+ field public static final int STATS_PROCESSES = 10014; // 0x271e
+ field public static final int TIMERS_JOBS = 10010; // 0x271a
+ field public static final int TIMERS_SENSORS = 10012; // 0x271c
+ field public static final int TIMERS_SYNCS = 10009; // 0x2719
+ field public static final int TIMERS_WAKELOCKS_DRAW = 10008; // 0x2718
+ field public static final int TIMERS_WAKELOCKS_FULL = 10005; // 0x2715
+ field public static final int TIMERS_WAKELOCKS_PARTIAL = 10006; // 0x2716
+ field public static final int TIMERS_WAKELOCKS_WINDOW = 10007; // 0x2717
+ field public static final int TIMER_AUDIO = 10032; // 0x2730
+ field public static final int TIMER_BLUETOOTH_SCAN = 10037; // 0x2735
+ field public static final int TIMER_CAMERA = 10035; // 0x2733
+ field public static final int TIMER_FLASHLIGHT = 10034; // 0x2732
+ field public static final int TIMER_FOREGROUND_ACTIVITY = 10036; // 0x2734
+ field public static final int TIMER_GPS_SENSOR = 10011; // 0x271b
+ field public static final int TIMER_MOBILE_RADIO_ACTIVE = 10061; // 0x274d
+ field public static final int TIMER_PROCESS_STATE_BACKGROUND_MS = 10042; // 0x273a
+ field public static final int TIMER_PROCESS_STATE_CACHED_MS = 10043; // 0x273b
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_MS = 10041; // 0x2739
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS = 10039; // 0x2737
+ field public static final int TIMER_PROCESS_STATE_TOP_MS = 10038; // 0x2736
+ field public static final int TIMER_PROCESS_STATE_TOP_SLEEPING_MS = 10040; // 0x2738
+ field public static final int TIMER_VIBRATOR = 10044; // 0x273c
+ field public static final int TIMER_VIDEO = 10033; // 0x2731
+ field public static final int TIMER_WIFI_SCAN = 10030; // 0x272e
+ }
+
+}
+
package android.os.storage {
public abstract class OnObbStateChangeListener {
@@ -32845,6 +33115,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";
@@ -32867,6 +33138,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
@@ -34098,6 +34370,7 @@
public static final class DocumentsContract.Root {
field public static final java.lang.String COLUMN_AVAILABLE_BYTES = "available_bytes";
+ field public static final java.lang.String COLUMN_CAPACITY_BYTES = "capacity_bytes";
field public static final java.lang.String COLUMN_DOCUMENT_ID = "document_id";
field public static final java.lang.String COLUMN_FLAGS = "flags";
field public static final java.lang.String COLUMN_ICON = "icon";
@@ -34654,6 +34927,7 @@
field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+ field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
@@ -34691,6 +34965,7 @@
field public static final java.lang.String AUTO_TIME = "auto_time";
field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+ field public static final java.lang.String BOOT_COUNT = "boot_count";
field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DATA_ROAMING = "data_roaming";
@@ -36908,8 +37183,9 @@
package android.service.notification {
public class Condition implements android.os.Parcelable {
- ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int);
+ ctor public Condition(android.net.Uri, java.lang.String, int);
ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
+ ctor public Condition(android.os.Parcel);
method public android.service.notification.Condition copy();
method public int describeContents();
method public static boolean isValidId(android.net.Uri, java.lang.String);
@@ -36950,37 +37226,6 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
- public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
- ctor public NotificationAssistantService();
- method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
- method public final android.os.IBinder onBind(android.content.Intent);
- method public void onNotificationActionClick(java.lang.String, long, int);
- method public void onNotificationClick(java.lang.String, long);
- method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
- method public void onNotificationRemoved(java.lang.String, long, int);
- method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
- field public static final int REASON_APP_CANCEL = 8; // 0x8
- field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
- field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
- field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
- field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
- field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
- field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
- field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
- field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
- field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
- field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
- field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
- field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
- field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
- field public static final int REASON_USER_STOPPED = 6; // 0x6
- field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
- }
-
- public class NotificationAssistantService.Adjustment {
- ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
- }
-
public abstract class NotificationListenerService extends android.app.Service {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();
@@ -37011,7 +37256,6 @@
method public final void setNotificationsShown(java.lang.String[]);
method public final void setOnNotificationPostedTrim(int);
method public void unregisterAsSystemService() throws android.os.RemoteException;
- field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
@@ -37051,6 +37295,37 @@
field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
}
+ public abstract class NotificationRankerService extends android.service.notification.NotificationListenerService {
+ ctor public NotificationRankerService();
+ method public final void adjustImportance(java.lang.String, android.service.notification.NotificationRankerService.Adjustment);
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onNotificationActionClick(java.lang.String, long, int);
+ method public void onNotificationClick(java.lang.String, long);
+ method public abstract android.service.notification.NotificationRankerService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public void onNotificationRemoved(java.lang.String, long, int);
+ method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
+ field public static final int REASON_APP_CANCEL = 8; // 0x8
+ field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
+ field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
+ field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
+ field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
+ field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
+ field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
+ field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
+ field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
+ field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
+ field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
+ field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+ field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
+ field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
+ field public static final int REASON_USER_STOPPED = 6; // 0x6
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationRankerService";
+ }
+
+ public class NotificationRankerService.Adjustment {
+ ctor public NotificationRankerService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+ }
+
public class StatusBarNotification implements android.os.Parcelable {
ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
ctor public StatusBarNotification(android.os.Parcel);
@@ -37364,6 +37639,17 @@
}
+package android.service.vr {
+
+ public abstract class VrListenerService extends android.app.Service {
+ ctor public VrListenerService();
+ method public static final boolean isVrModePackageEnabled(android.content.Context, android.content.ComponentName);
+ method public android.os.IBinder onBind(android.content.Intent);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
+ }
+
+}
+
package android.service.wallpaper {
public abstract class WallpaperService extends android.app.Service {
@@ -38375,10 +38661,12 @@
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
+ method public void pullExternalCall();
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 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();
@@ -38393,6 +38681,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
}
@@ -38403,6 +38692,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);
@@ -38433,6 +38723,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
@@ -38452,6 +38743,7 @@
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
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
field public static final int PROPERTY_WORK_CALL = 32; // 0x20
}
@@ -38577,15 +38869,19 @@
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 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 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);
@@ -38609,9 +38905,12 @@
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 = 33554432; // 0x2000000
+ 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
field public static final int CAPABILITY_HOLD = 1; // 0x1
+ field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -38625,6 +38924,7 @@
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_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
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";
@@ -38634,6 +38934,7 @@
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
}
@@ -38711,7 +39012,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;
@@ -38748,6 +39051,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();
@@ -38953,6 +39257,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();
@@ -38970,6 +39275,7 @@
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 onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -39094,6 +39400,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
@@ -41243,7 +41550,6 @@
method public boolean hasNext();
method public java.util.Iterator<java.lang.String> iterator();
method public java.lang.String next();
- method public void remove();
method public void setString(java.lang.String);
}
@@ -42855,6 +43161,7 @@
method public int describeContents();
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public static android.util.LocaleList getAdjustedDefault();
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getFirstMatch(java.lang.String[]);
@@ -53729,6 +54036,7 @@
public abstract interface Iterable {
method public default void forEach(java.util.function.Consumer<? super T>);
method public abstract java.util.Iterator<T> iterator();
+ method public default java.util.Spliterator<T> spliterator();
}
public class LinkageError extends java.lang.Error {
@@ -60149,6 +60457,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
@@ -60159,7 +60468,9 @@
method public void ensureCapacity(int);
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
+ method public boolean removeIf(java.util.function.Predicate<? super E>);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public void trimToSize();
}
@@ -60242,6 +60553,24 @@
method public static int hashCode(float[]);
method public static int hashCode(double[]);
method public static int hashCode(java.lang.Object[]);
+ method public static void parallelSort(byte[]);
+ method public static void parallelSort(byte[], int, int);
+ method public static void parallelSort(char[]);
+ method public static void parallelSort(char[], int, int);
+ method public static void parallelSort(short[]);
+ method public static void parallelSort(short[], int, int);
+ method public static void parallelSort(int[]);
+ method public static void parallelSort(int[], int, int);
+ method public static void parallelSort(long[]);
+ method public static void parallelSort(long[], int, int);
+ method public static void parallelSort(float[]);
+ method public static void parallelSort(float[], int, int);
+ method public static void parallelSort(double[]);
+ method public static void parallelSort(double[], int, int);
+ method public static void parallelSort(T[]);
+ method public static void parallelSort(T[], int, int);
+ method public static void parallelSort(T[], java.util.Comparator<? super T>);
+ method public static void parallelSort(T[], int, int, java.util.Comparator<? super T>);
method public static void sort(int[]);
method public static void sort(int[], int, int);
method public static void sort(long[]);
@@ -60260,6 +60589,14 @@
method public static void sort(java.lang.Object[], int, int);
method public static void sort(T[], java.util.Comparator<? super T>);
method public static void sort(T[], int, int, java.util.Comparator<? super T>);
+ method public static java.util.Spliterator<T> spliterator(T[]);
+ method public static java.util.Spliterator<T> spliterator(T[], int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[]);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[]);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[]);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int);
method public static java.lang.String toString(long[]);
method public static java.lang.String toString(int[]);
method public static java.lang.String toString(short[]);
@@ -60421,6 +60758,7 @@
method public abstract java.util.Iterator<E> iterator();
method public abstract boolean remove(java.lang.Object);
method public abstract boolean removeAll(java.util.Collection<?>);
+ method public default boolean removeIf(java.util.function.Predicate<? super E>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract int size();
method public abstract java.lang.Object[] toArray();
@@ -60490,7 +60828,23 @@
public abstract interface Comparator {
method public abstract int compare(T, T);
+ method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+ method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>);
+ method public static java.util.Comparator<T> comparingDouble(java.util.function.ToDoubleFunction<? super T>);
+ method public static java.util.Comparator<T> comparingInt(java.util.function.ToIntFunction<? super T>);
+ method public static java.util.Comparator<T> comparingLong(java.util.function.ToLongFunction<? super T>);
method public abstract boolean equals(java.lang.Object);
+ method public static java.util.Comparator<T> naturalOrder();
+ method public static java.util.Comparator<T> nullsFirst(java.util.Comparator<? super T>);
+ method public static java.util.Comparator<T> nullsLast(java.util.Comparator<? super T>);
+ method public static java.util.Comparator<T> reverseOrder();
+ method public default java.util.Comparator<T> reversed();
+ method public default java.util.Comparator<T> thenComparing(java.util.Comparator<? super T>);
+ method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+ method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>);
+ method public default java.util.Comparator<T> thenComparingDouble(java.util.function.ToDoubleFunction<? super T>);
+ method public default java.util.Comparator<T> thenComparingInt(java.util.function.ToIntFunction<? super T>);
+ method public default java.util.Comparator<T> thenComparingLong(java.util.function.ToLongFunction<? super T>);
}
public class ConcurrentModificationException extends java.lang.RuntimeException {
@@ -60587,6 +60941,17 @@
method public abstract int size();
}
+ public class DoubleSummaryStatistics implements java.util.function.DoubleConsumer {
+ ctor public DoubleSummaryStatistics();
+ method public void accept(double);
+ method public void combine(java.util.DoubleSummaryStatistics);
+ method public final double getAverage();
+ method public final long getCount();
+ method public final double getMax();
+ method public final double getMin();
+ method public final double getSum();
+ }
+
public class DuplicateFormatFlagsException extends java.util.IllegalFormatException {
ctor public DuplicateFormatFlagsException(java.lang.String);
method public java.lang.String getFlags();
@@ -60732,6 +61097,7 @@
method public java.lang.Object clone();
method public java.util.Iterator<E> iterator();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class Hashtable extends java.util.Dictionary implements java.lang.Cloneable java.util.Map java.io.Serializable {
@@ -60809,15 +61175,27 @@
ctor public InputMismatchException(java.lang.String);
}
+ public class IntSummaryStatistics implements java.util.function.IntConsumer {
+ ctor public IntSummaryStatistics();
+ method public void accept(int);
+ method public void combine(java.util.IntSummaryStatistics);
+ method public final double getAverage();
+ method public final long getCount();
+ method public final int getMax();
+ method public final int getMin();
+ method public final long getSum();
+ }
+
public class InvalidPropertiesFormatException extends java.io.IOException {
ctor public InvalidPropertiesFormatException(java.lang.Throwable);
ctor public InvalidPropertiesFormatException(java.lang.String);
}
public abstract interface Iterator {
+ method public default void forEachRemaining(java.util.function.Consumer<? super E>);
method public abstract boolean hasNext();
method public abstract E next();
- method public abstract void remove();
+ method public default void remove();
}
public class LinkedHashMap extends java.util.HashMap implements java.util.Map {
@@ -60864,6 +61242,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public abstract interface List implements java.util.Collection {
@@ -60999,25 +61378,51 @@
enum_constant public static final java.util.Locale.Category FORMAT;
}
+ public class LongSummaryStatistics implements java.util.function.IntConsumer java.util.function.LongConsumer {
+ ctor public LongSummaryStatistics();
+ method public void accept(int);
+ method public void accept(long);
+ method public void combine(java.util.LongSummaryStatistics);
+ method public final double getAverage();
+ method public final long getCount();
+ method public final long getMax();
+ method public final long getMin();
+ method public final long getSum();
+ }
+
public abstract interface Map {
method public abstract void clear();
+ method public default V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public default V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public default V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public abstract boolean containsKey(java.lang.Object);
method public abstract boolean containsValue(java.lang.Object);
method public abstract java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public abstract boolean equals(java.lang.Object);
method public default void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public abstract V get(java.lang.Object);
+ method public default V getOrDefault(java.lang.Object, V);
method public abstract int hashCode();
method public abstract boolean isEmpty();
method public abstract java.util.Set<K> keySet();
+ method public default V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public abstract V put(K, V);
method public abstract void putAll(java.util.Map<? extends K, ? extends V>);
+ method public default V putIfAbsent(K, V);
method public abstract V remove(java.lang.Object);
+ method public default boolean remove(java.lang.Object, java.lang.Object);
+ method public default boolean replace(K, V, V);
+ method public default V replace(K, V);
+ method public default void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public abstract int size();
method public abstract java.util.Collection<V> values();
}
public static abstract interface Map.Entry {
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey();
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey(java.util.Comparator<? super K>);
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByValue();
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByValue(java.util.Comparator<? super V>);
method public abstract boolean equals(java.lang.Object);
method public abstract K getKey();
method public abstract V getValue();
@@ -61168,9 +61573,35 @@
method public long orElseThrow(java.util.function.Supplier<X>) throws java.lang.Throwable;
}
+ public abstract interface PrimitiveIterator implements java.util.Iterator {
+ method public abstract void forEachRemaining(T_CONS);
+ }
+
+ public static abstract interface PrimitiveIterator.OfDouble implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public default java.lang.Double next();
+ method public abstract double nextDouble();
+ }
+
+ public static abstract interface PrimitiveIterator.OfInt implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public default java.lang.Integer next();
+ method public abstract int nextInt();
+ }
+
+ public static abstract interface PrimitiveIterator.OfLong implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public default java.lang.Long next();
+ method public abstract long nextLong();
+ }
+
public class PriorityQueue extends java.util.AbstractQueue implements java.io.Serializable {
ctor public PriorityQueue();
ctor public PriorityQueue(int);
+ ctor public PriorityQueue(java.util.Comparator<? super E>);
ctor public PriorityQueue(int, java.util.Comparator<? super E>);
ctor public PriorityQueue(java.util.Collection<? extends E>);
ctor public PriorityQueue(java.util.PriorityQueue<? extends E>);
@@ -61181,6 +61612,7 @@
method public E peek();
method public E poll();
method public int size();
+ method public final java.util.Spliterator<E> spliterator();
}
public class Properties extends java.util.Hashtable {
@@ -61339,7 +61771,6 @@
method public short nextShort();
method public short nextShort(int);
method public int radix();
- method public void remove();
method public java.util.Scanner reset();
method public java.util.Scanner skip(java.util.regex.Pattern);
method public java.util.Scanner skip(java.lang.String);
@@ -61424,6 +61855,111 @@
method public abstract java.util.SortedSet<E> tailSet(E);
}
+ public abstract interface Spliterator {
+ method public abstract int characteristics();
+ method public abstract long estimateSize();
+ method public default void forEachRemaining(java.util.function.Consumer<? super T>);
+ method public default java.util.Comparator<? super T> getComparator();
+ method public default long getExactSizeIfKnown();
+ method public default boolean hasCharacteristics(int);
+ method public abstract boolean tryAdvance(java.util.function.Consumer<? super T>);
+ method public abstract java.util.Spliterator<T> trySplit();
+ field public static final int CONCURRENT = 4096; // 0x1000
+ field public static final int DISTINCT = 1; // 0x1
+ field public static final int IMMUTABLE = 1024; // 0x400
+ field public static final int NONNULL = 256; // 0x100
+ field public static final int ORDERED = 16; // 0x10
+ field public static final int SIZED = 64; // 0x40
+ field public static final int SORTED = 4; // 0x4
+ field public static final int SUBSIZED = 16384; // 0x4000
+ }
+
+ public static abstract interface Spliterator.OfDouble implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract boolean tryAdvance(java.util.function.DoubleConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract interface Spliterator.OfInt implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract boolean tryAdvance(java.util.function.IntConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract interface Spliterator.OfLong implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract boolean tryAdvance(java.util.function.LongConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract interface Spliterator.OfPrimitive implements java.util.Spliterator {
+ method public default void forEachRemaining(T_CONS);
+ method public abstract boolean tryAdvance(T_CONS);
+ method public abstract T_SPLITR trySplit();
+ }
+
+ public final class Spliterators {
+ method public static java.util.Spliterator.OfDouble emptyDoubleSpliterator();
+ method public static java.util.Spliterator.OfInt emptyIntSpliterator();
+ method public static java.util.Spliterator.OfLong emptyLongSpliterator();
+ method public static java.util.Spliterator<T> emptySpliterator();
+ method public static java.util.Iterator<T> iterator(java.util.Spliterator<? extends T>);
+ method public static java.util.PrimitiveIterator.OfInt iterator(java.util.Spliterator.OfInt);
+ method public static java.util.PrimitiveIterator.OfLong iterator(java.util.Spliterator.OfLong);
+ method public static java.util.PrimitiveIterator.OfDouble iterator(java.util.Spliterator.OfDouble);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int, int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Collection<? extends T>, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Iterator<? extends T>, long, int);
+ method public static java.util.Spliterator.OfInt spliterator(java.util.PrimitiveIterator.OfInt, long, int);
+ method public static java.util.Spliterator.OfLong spliterator(java.util.PrimitiveIterator.OfLong, long, int);
+ method public static java.util.Spliterator.OfDouble spliterator(java.util.PrimitiveIterator.OfDouble, long, int);
+ method public static java.util.Spliterator<T> spliteratorUnknownSize(java.util.Iterator<? extends T>, int);
+ method public static java.util.Spliterator.OfInt spliteratorUnknownSize(java.util.PrimitiveIterator.OfInt, int);
+ method public static java.util.Spliterator.OfLong spliteratorUnknownSize(java.util.PrimitiveIterator.OfLong, int);
+ method public static java.util.Spliterator.OfDouble spliteratorUnknownSize(java.util.PrimitiveIterator.OfDouble, int);
+ }
+
+ public static abstract class Spliterators.AbstractDoubleSpliterator implements java.util.Spliterator.OfDouble {
+ ctor protected Spliterators.AbstractDoubleSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractIntSpliterator implements java.util.Spliterator.OfInt {
+ ctor protected Spliterators.AbstractIntSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractLongSpliterator implements java.util.Spliterator.OfLong {
+ ctor protected Spliterators.AbstractLongSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractSpliterator implements java.util.Spliterator {
+ ctor protected Spliterators.AbstractSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator<T> trySplit();
+ }
+
public class Stack extends java.util.Vector {
ctor public Stack();
method public boolean empty();
@@ -61433,6 +61969,15 @@
method public synchronized int search(java.lang.Object);
}
+ public final class StringJoiner {
+ ctor public StringJoiner(java.lang.CharSequence);
+ ctor public StringJoiner(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+ method public java.util.StringJoiner add(java.lang.CharSequence);
+ method public int length();
+ method public java.util.StringJoiner merge(java.util.StringJoiner);
+ method public java.util.StringJoiner setEmptyValue(java.lang.CharSequence);
+ }
+
public class StringTokenizer implements java.util.Enumeration {
ctor public StringTokenizer(java.lang.String, java.lang.String, boolean);
ctor public StringTokenizer(java.lang.String, java.lang.String);
@@ -61554,6 +62099,7 @@
method public E pollFirst();
method public E pollLast();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
method public java.util.SortedSet<E> subSet(E, E);
method public java.util.NavigableSet<E> tailSet(E, boolean);
@@ -61607,9 +62153,11 @@
method public synchronized void removeAllElements();
method public synchronized boolean removeElement(java.lang.Object);
method public synchronized void removeElementAt(int);
+ method public synchronized boolean removeIf(java.util.function.Predicate<? super E>);
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
field protected int elementCount;
@@ -61657,6 +62205,7 @@
method public void put(E) throws java.lang.InterruptedException;
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -61720,6 +62269,78 @@
ctor public CancellationException(java.lang.String);
}
+ public class CompletableFuture implements java.util.concurrent.CompletionStage java.util.concurrent.Future {
+ ctor public CompletableFuture();
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Void> allOf(java.util.concurrent.CompletableFuture<?>...);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Object> anyOf(java.util.concurrent.CompletableFuture<?>...);
+ method public java.util.concurrent.CompletableFuture<U> applyToEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public java.util.concurrent.CompletableFuture<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public java.util.concurrent.CompletableFuture<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>, java.util.concurrent.Executor);
+ method public boolean cancel(boolean);
+ method public boolean complete(T);
+ method public boolean completeExceptionally(java.lang.Throwable);
+ method public static java.util.concurrent.CompletableFuture<U> completedFuture(U);
+ method public java.util.concurrent.CompletableFuture<T> exceptionally(java.util.function.Function<java.lang.Throwable, ? extends T>);
+ method public T get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+ method public T get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+ method public T getNow(T);
+ method public int getNumberOfDependents();
+ method public java.util.concurrent.CompletableFuture<U> handle(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>, java.util.concurrent.Executor);
+ method public boolean isCancelled();
+ method public boolean isCompletedExceptionally();
+ method public boolean isDone();
+ method public T join();
+ method public void obtrudeException(java.lang.Throwable);
+ method public void obtrudeValue(T);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBoth(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEither(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Void> runAsync(java.lang.Runnable);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Void> runAsync(java.lang.Runnable, java.util.concurrent.Executor);
+ method public static java.util.concurrent.CompletableFuture<U> supplyAsync(java.util.function.Supplier<U>);
+ method public static java.util.concurrent.CompletableFuture<U> supplyAsync(java.util.function.Supplier<U>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAccept(java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBoth(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<U> thenApply(java.util.function.Function<? super T, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<V> thenCombine(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public java.util.concurrent.CompletableFuture<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public java.util.concurrent.CompletableFuture<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<U> thenCompose(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public java.util.concurrent.CompletableFuture<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public java.util.concurrent.CompletableFuture<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRun(java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRunAsync(java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRunAsync(java.lang.Runnable, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<T> toCompletableFuture();
+ method public java.util.concurrent.CompletableFuture<T> whenComplete(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public java.util.concurrent.CompletableFuture<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public java.util.concurrent.CompletableFuture<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>, java.util.concurrent.Executor);
+ }
+
+ public static abstract interface CompletableFuture.AsynchronousCompletionTask {
+ }
+
+ public class CompletionException extends java.lang.RuntimeException {
+ ctor protected CompletionException();
+ ctor protected CompletionException(java.lang.String);
+ ctor public CompletionException(java.lang.String, java.lang.Throwable);
+ ctor public CompletionException(java.lang.Throwable);
+ }
+
public abstract interface CompletionService {
method public abstract java.util.concurrent.Future<V> poll();
method public abstract java.util.concurrent.Future<V> poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
@@ -61728,20 +62349,130 @@
method public abstract java.util.concurrent.Future<V> take() throws java.lang.InterruptedException;
}
+ public abstract interface CompletionStage {
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> acceptEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<U> applyToEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public abstract java.util.concurrent.CompletionStage<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public abstract java.util.concurrent.CompletionStage<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<T> exceptionally(java.util.function.Function<java.lang.Throwable, ? extends T>);
+ method public abstract java.util.concurrent.CompletionStage<U> handle(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterBoth(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterEither(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAccept(java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBoth(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<U> thenApply(java.util.function.Function<? super T, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<V> thenCombine(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public abstract java.util.concurrent.CompletionStage<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public abstract java.util.concurrent.CompletionStage<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<U> thenCompose(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenRun(java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenRunAsync(java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenRunAsync(java.lang.Runnable, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletableFuture<T> toCompletableFuture();
+ method public abstract java.util.concurrent.CompletionStage<T> whenComplete(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public abstract java.util.concurrent.CompletionStage<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public abstract java.util.concurrent.CompletionStage<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>, java.util.concurrent.Executor);
+ }
+
public class ConcurrentHashMap extends java.util.AbstractMap implements java.util.concurrent.ConcurrentMap java.io.Serializable {
ctor public ConcurrentHashMap();
ctor public ConcurrentHashMap(int);
ctor public ConcurrentHashMap(java.util.Map<? extends K, ? extends V>);
ctor public ConcurrentHashMap(int, float);
ctor public ConcurrentHashMap(int, float, int);
+ method public V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public boolean contains(java.lang.Object);
method public java.util.Enumeration<V> elements();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+ method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public void forEach(long, java.util.function.BiConsumer<? super K, ? super V>);
+ method public void forEach(long, java.util.function.BiFunction<? super K, ? super V, ? extends U>, java.util.function.Consumer<? super U>);
+ method public void forEachEntry(long, java.util.function.Consumer<? super java.util.Map.Entry<K, V>>);
+ method public void forEachEntry(long, java.util.function.Function<java.util.Map.Entry<K, V>, ? extends U>, java.util.function.Consumer<? super U>);
+ method public void forEachKey(long, java.util.function.Consumer<? super K>);
+ method public void forEachKey(long, java.util.function.Function<? super K, ? extends U>, java.util.function.Consumer<? super U>);
+ method public void forEachValue(long, java.util.function.Consumer<? super V>);
+ method public void forEachValue(long, java.util.function.Function<? super V, ? extends U>, java.util.function.Consumer<? super U>);
+ method public V getOrDefault(java.lang.Object, V);
+ method public java.util.concurrent.ConcurrentHashMap.KeySetView<K, V> keySet(V);
method public java.util.Enumeration<K> keys();
+ method public long mappingCount();
+ method public V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
+ method public static java.util.concurrent.ConcurrentHashMap.KeySetView<K, java.lang.Boolean> newKeySet();
+ method public static java.util.concurrent.ConcurrentHashMap.KeySetView<K, java.lang.Boolean> newKeySet(int);
method public V putIfAbsent(K, V);
+ method public U reduce(long, java.util.function.BiFunction<? super K, ? super V, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public java.util.Map.Entry<K, V> reduceEntries(long, java.util.function.BiFunction<java.util.Map.Entry<K, V>, java.util.Map.Entry<K, V>, ? extends java.util.Map.Entry<K, V>>);
+ method public U reduceEntries(long, java.util.function.Function<java.util.Map.Entry<K, V>, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public double reduceEntriesToDouble(long, java.util.function.ToDoubleFunction<java.util.Map.Entry<K, V>>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceEntriesToInt(long, java.util.function.ToIntFunction<java.util.Map.Entry<K, V>>, int, java.util.function.IntBinaryOperator);
+ method public long reduceEntriesToLong(long, java.util.function.ToLongFunction<java.util.Map.Entry<K, V>>, long, java.util.function.LongBinaryOperator);
+ method public K reduceKeys(long, java.util.function.BiFunction<? super K, ? super K, ? extends K>);
+ method public U reduceKeys(long, java.util.function.Function<? super K, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public double reduceKeysToDouble(long, java.util.function.ToDoubleFunction<? super K>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceKeysToInt(long, java.util.function.ToIntFunction<? super K>, int, java.util.function.IntBinaryOperator);
+ method public long reduceKeysToLong(long, java.util.function.ToLongFunction<? super K>, long, java.util.function.LongBinaryOperator);
+ method public double reduceToDouble(long, java.util.function.ToDoubleBiFunction<? super K, ? super V>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceToInt(long, java.util.function.ToIntBiFunction<? super K, ? super V>, int, java.util.function.IntBinaryOperator);
+ method public long reduceToLong(long, java.util.function.ToLongBiFunction<? super K, ? super V>, long, java.util.function.LongBinaryOperator);
+ method public V reduceValues(long, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
+ method public U reduceValues(long, java.util.function.Function<? super V, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public double reduceValuesToDouble(long, java.util.function.ToDoubleFunction<? super V>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceValuesToInt(long, java.util.function.ToIntFunction<? super V>, int, java.util.function.IntBinaryOperator);
+ method public long reduceValuesToLong(long, java.util.function.ToLongFunction<? super V>, long, java.util.function.LongBinaryOperator);
method public boolean remove(java.lang.Object, java.lang.Object);
method public boolean replace(K, V, V);
method public V replace(K, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public U search(long, java.util.function.BiFunction<? super K, ? super V, ? extends U>);
+ method public U searchEntries(long, java.util.function.Function<java.util.Map.Entry<K, V>, ? extends U>);
+ method public U searchKeys(long, java.util.function.Function<? super K, ? extends U>);
+ method public U searchValues(long, java.util.function.Function<? super V, ? extends U>);
+ }
+
+ static abstract class ConcurrentHashMap.CollectionView implements java.util.Collection java.io.Serializable {
+ method public final void clear();
+ method public abstract boolean contains(java.lang.Object);
+ method public final boolean containsAll(java.util.Collection<?>);
+ method public java.util.concurrent.ConcurrentHashMap<K, V> getMap();
+ method public final boolean isEmpty();
+ method public abstract java.util.Iterator<E> iterator();
+ method public abstract boolean remove(java.lang.Object);
+ method public final boolean removeAll(java.util.Collection<?>);
+ method public final boolean retainAll(java.util.Collection<?>);
+ method public final int size();
+ method public final java.lang.Object[] toArray();
+ method public final T[] toArray(T[]);
+ method public final java.lang.String toString();
+ }
+
+ public static class ConcurrentHashMap.KeySetView extends java.util.concurrent.ConcurrentHashMap.CollectionView implements java.io.Serializable java.util.Set {
+ method public boolean add(K);
+ method public boolean addAll(java.util.Collection<? extends K>);
+ method public boolean contains(java.lang.Object);
+ method public void forEach(java.util.function.Consumer<? super K>);
+ method public V getMappedValue();
+ method public java.util.Iterator<K> iterator();
+ method public boolean remove(java.lang.Object);
+ method public java.util.Spliterator<K> spliterator();
}
public class ConcurrentLinkedDeque extends java.util.AbstractCollection implements java.util.Deque java.io.Serializable {
@@ -61771,6 +62502,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class ConcurrentLinkedQueue extends java.util.AbstractQueue implements java.util.Queue java.io.Serializable {
@@ -61781,6 +62513,7 @@
method public E peek();
method public E poll();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public abstract interface ConcurrentMap implements java.util.Map {
@@ -61812,6 +62545,9 @@
method public K ceilingKey(K);
method public java.util.concurrent.ConcurrentSkipListMap<K, V> clone();
method public java.util.Comparator<? super K> comparator();
+ method public V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.NavigableSet<K> descendingKeySet();
method public java.util.concurrent.ConcurrentNavigableMap<K, V> descendingMap();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
@@ -61819,6 +62555,8 @@
method public K firstKey();
method public java.util.Map.Entry<K, V> floorEntry(K);
method public K floorKey(K);
+ method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public V getOrDefault(java.lang.Object, V);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> headMap(K, boolean);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> headMap(K);
method public java.util.Map.Entry<K, V> higherEntry(K);
@@ -61827,6 +62565,7 @@
method public K lastKey();
method public java.util.Map.Entry<K, V> lowerEntry(K);
method public K lowerKey(K);
+ method public V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public java.util.NavigableSet<K> navigableKeySet();
method public java.util.Map.Entry<K, V> pollFirstEntry();
method public java.util.Map.Entry<K, V> pollLastEntry();
@@ -61834,6 +62573,7 @@
method public boolean remove(java.lang.Object, java.lang.Object);
method public boolean replace(K, V, V);
method public V replace(K, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> subMap(K, boolean, K, boolean);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> subMap(K, K);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> tailMap(K, boolean);
@@ -61861,6 +62601,7 @@
method public E pollFirst();
method public E pollLast();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
method public java.util.NavigableSet<E> subSet(E, E);
method public java.util.NavigableSet<E> tailSet(E, boolean);
@@ -61871,31 +62612,34 @@
ctor public CopyOnWriteArrayList();
ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
ctor public CopyOnWriteArrayList(E[]);
- method public synchronized boolean add(E);
- method public synchronized void add(int, E);
- method public synchronized boolean addAll(java.util.Collection<? extends E>);
- method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
- method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
- method public synchronized boolean addIfAbsent(E);
- method public synchronized void clear();
+ method public boolean add(E);
+ method public void add(int, E);
+ method public boolean addAll(java.util.Collection<? extends E>);
+ method public boolean addAll(int, java.util.Collection<? extends E>);
+ method public int addAllAbsent(java.util.Collection<? extends E>);
+ method public boolean addIfAbsent(E);
+ method public void clear();
method public java.lang.Object clone();
method public boolean contains(java.lang.Object);
method public boolean containsAll(java.util.Collection<?>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
- method public int indexOf(E, int);
method public int indexOf(java.lang.Object);
+ method public int indexOf(E, int);
method public boolean isEmpty();
method public java.util.Iterator<E> iterator();
- method public int lastIndexOf(E, int);
method public int lastIndexOf(java.lang.Object);
- method public java.util.ListIterator<E> listIterator(int);
+ method public int lastIndexOf(E, int);
method public java.util.ListIterator<E> listIterator();
- method public synchronized E remove(int);
- method public synchronized boolean remove(java.lang.Object);
- method public synchronized boolean removeAll(java.util.Collection<?>);
- method public synchronized boolean retainAll(java.util.Collection<?>);
- method public synchronized E set(int, E);
+ method public java.util.ListIterator<E> listIterator(int);
+ method public E remove(int);
+ method public boolean remove(java.lang.Object);
+ method public boolean removeAll(java.util.Collection<?>);
+ method public void replaceAll(java.util.function.UnaryOperator<E>);
+ method public boolean retainAll(java.util.Collection<?>);
+ method public E set(int, E);
method public int size();
+ method public void sort(java.util.Comparator<? super E>);
method public java.util.List<E> subList(int, int);
method public java.lang.Object[] toArray();
method public T[] toArray(T[]);
@@ -61904,8 +62648,11 @@
public class CopyOnWriteArraySet extends java.util.AbstractSet implements java.io.Serializable {
ctor public CopyOnWriteArraySet();
ctor public CopyOnWriteArraySet(java.util.Collection<? extends E>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public java.util.Iterator<E> iterator();
+ method public boolean removeIf(java.util.function.Predicate<? super E>);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class CountDownLatch {
@@ -61916,6 +62663,32 @@
method public long getCount();
}
+ public abstract class CountedCompleter extends java.util.concurrent.ForkJoinTask {
+ ctor protected CountedCompleter(java.util.concurrent.CountedCompleter<?>, int);
+ ctor protected CountedCompleter(java.util.concurrent.CountedCompleter<?>);
+ ctor protected CountedCompleter();
+ method public final void addToPendingCount(int);
+ method public final boolean compareAndSetPendingCount(int, int);
+ method public void complete(T);
+ method public abstract void compute();
+ method public final int decrementPendingCountUnlessZero();
+ method protected final boolean exec();
+ method public final java.util.concurrent.CountedCompleter<?> firstComplete();
+ method public final java.util.concurrent.CountedCompleter<?> getCompleter();
+ method public final int getPendingCount();
+ method public T getRawResult();
+ method public final java.util.concurrent.CountedCompleter<?> getRoot();
+ method public final void helpComplete(int);
+ method public final java.util.concurrent.CountedCompleter<?> nextComplete();
+ method public void onCompletion(java.util.concurrent.CountedCompleter<?>);
+ method public boolean onExceptionalCompletion(java.lang.Throwable, java.util.concurrent.CountedCompleter<?>);
+ method public final void propagateCompletion();
+ method public final void quietlyCompleteRoot();
+ method public final void setPendingCount(int);
+ method protected void setRawResult(T);
+ method public final void tryComplete();
+ }
+
public class CyclicBarrier {
ctor public CyclicBarrier(int, java.lang.Runnable);
ctor public CyclicBarrier(int);
@@ -62006,6 +62779,8 @@
method public static java.util.concurrent.ExecutorService newSingleThreadExecutor(java.util.concurrent.ThreadFactory);
method public static java.util.concurrent.ScheduledExecutorService newSingleThreadScheduledExecutor();
method public static java.util.concurrent.ScheduledExecutorService newSingleThreadScheduledExecutor(java.util.concurrent.ThreadFactory);
+ method public static java.util.concurrent.ExecutorService newWorkStealingPool(int);
+ method public static java.util.concurrent.ExecutorService newWorkStealingPool();
method public static java.util.concurrent.Callable<T> privilegedCallable(java.util.concurrent.Callable<T>);
method public static java.util.concurrent.Callable<T> privilegedCallableUsingCurrentClassLoader(java.util.concurrent.Callable<T>);
method public static java.util.concurrent.ThreadFactory privilegedThreadFactory();
@@ -62019,11 +62794,13 @@
ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean);
method public boolean awaitQuiescence(long, java.util.concurrent.TimeUnit);
method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public static java.util.concurrent.ForkJoinPool commonPool();
method protected int drainTasksTo(java.util.Collection<? super java.util.concurrent.ForkJoinTask<?>>);
method public void execute(java.util.concurrent.ForkJoinTask<?>);
method public void execute(java.lang.Runnable);
method public int getActiveThreadCount();
method public boolean getAsyncMode();
+ method public static int getCommonPoolParallelism();
method public java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory getFactory();
method public int getParallelism();
method public int getPoolSize();
@@ -62061,6 +62838,7 @@
method public static java.util.concurrent.ForkJoinTask<T> adapt(java.lang.Runnable, T);
method public static java.util.concurrent.ForkJoinTask<T> adapt(java.util.concurrent.Callable<? extends T>);
method public boolean cancel(boolean);
+ method public final boolean compareAndSetForkJoinTaskTag(short, short);
method public void complete(V);
method public void completeExceptionally(java.lang.Throwable);
method protected abstract boolean exec();
@@ -62068,6 +62846,7 @@
method public final V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
method public final V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
method public final java.lang.Throwable getException();
+ method public final short getForkJoinTaskTag();
method public static java.util.concurrent.ForkJoinPool getPool();
method public static int getQueuedTaskCount();
method public abstract V getRawResult();
@@ -62086,9 +62865,11 @@
method protected static java.util.concurrent.ForkJoinTask<?> peekNextLocalTask();
method protected static java.util.concurrent.ForkJoinTask<?> pollNextLocalTask();
method protected static java.util.concurrent.ForkJoinTask<?> pollTask();
+ method public final void quietlyComplete();
method public final void quietlyInvoke();
method public final void quietlyJoin();
method public void reinitialize();
+ method public final short setForkJoinTaskTag(short);
method protected abstract void setRawResult(V);
method public boolean tryUnfork();
}
@@ -62162,6 +62943,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
method public E takeFirst() throws java.lang.InterruptedException;
method public E takeLast() throws java.lang.InterruptedException;
@@ -62182,6 +62964,7 @@
method public void put(E) throws java.lang.InterruptedException;
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -62201,6 +62984,7 @@
method public void put(E);
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
method public void transfer(E) throws java.lang.InterruptedException;
method public boolean tryTransfer(E);
@@ -62248,6 +63032,7 @@
method public void put(E);
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -62351,6 +63136,7 @@
method public void put(E) throws java.lang.InterruptedException;
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -62480,112 +63266,136 @@
public class AtomicInteger extends java.lang.Number implements java.io.Serializable {
ctor public AtomicInteger(int);
ctor public AtomicInteger();
+ method public final int accumulateAndGet(int, java.util.function.IntBinaryOperator);
method public final int addAndGet(int);
method public final boolean compareAndSet(int, int);
method public final int decrementAndGet();
method public double doubleValue();
method public float floatValue();
method public final int get();
+ method public final int getAndAccumulate(int, java.util.function.IntBinaryOperator);
method public final int getAndAdd(int);
method public final int getAndDecrement();
method public final int getAndIncrement();
method public final int getAndSet(int);
+ method public final int getAndUpdate(java.util.function.IntUnaryOperator);
method public final int incrementAndGet();
method public int intValue();
method public final void lazySet(int);
method public long longValue();
method public final void set(int);
+ method public final int updateAndGet(java.util.function.IntUnaryOperator);
method public final boolean weakCompareAndSet(int, int);
}
public class AtomicIntegerArray implements java.io.Serializable {
ctor public AtomicIntegerArray(int);
ctor public AtomicIntegerArray(int[]);
+ method public final int accumulateAndGet(int, int, java.util.function.IntBinaryOperator);
method public final int addAndGet(int, int);
method public final boolean compareAndSet(int, int, int);
method public final int decrementAndGet(int);
method public final int get(int);
+ method public final int getAndAccumulate(int, int, java.util.function.IntBinaryOperator);
method public final int getAndAdd(int, int);
method public final int getAndDecrement(int);
method public final int getAndIncrement(int);
method public final int getAndSet(int, int);
+ method public final int getAndUpdate(int, java.util.function.IntUnaryOperator);
method public final int incrementAndGet(int);
method public final void lazySet(int, int);
method public final int length();
method public final void set(int, int);
+ method public final int updateAndGet(int, java.util.function.IntUnaryOperator);
method public final boolean weakCompareAndSet(int, int, int);
}
public abstract class AtomicIntegerFieldUpdater {
ctor protected AtomicIntegerFieldUpdater();
+ method public final int accumulateAndGet(T, int, java.util.function.IntBinaryOperator);
method public int addAndGet(T, int);
method public abstract boolean compareAndSet(T, int, int);
method public int decrementAndGet(T);
method public abstract int get(T);
+ method public final int getAndAccumulate(T, int, java.util.function.IntBinaryOperator);
method public int getAndAdd(T, int);
method public int getAndDecrement(T);
method public int getAndIncrement(T);
method public int getAndSet(T, int);
+ method public final int getAndUpdate(T, java.util.function.IntUnaryOperator);
method public int incrementAndGet(T);
method public abstract void lazySet(T, int);
method public static java.util.concurrent.atomic.AtomicIntegerFieldUpdater<U> newUpdater(java.lang.Class<U>, java.lang.String);
method public abstract void set(T, int);
+ method public final int updateAndGet(T, java.util.function.IntUnaryOperator);
method public abstract boolean weakCompareAndSet(T, int, int);
}
public class AtomicLong extends java.lang.Number implements java.io.Serializable {
ctor public AtomicLong(long);
ctor public AtomicLong();
+ method public final long accumulateAndGet(long, java.util.function.LongBinaryOperator);
method public final long addAndGet(long);
method public final boolean compareAndSet(long, long);
method public final long decrementAndGet();
method public double doubleValue();
method public float floatValue();
method public final long get();
+ method public final long getAndAccumulate(long, java.util.function.LongBinaryOperator);
method public final long getAndAdd(long);
method public final long getAndDecrement();
method public final long getAndIncrement();
method public final long getAndSet(long);
+ method public final long getAndUpdate(java.util.function.LongUnaryOperator);
method public final long incrementAndGet();
method public int intValue();
method public final void lazySet(long);
method public long longValue();
method public final void set(long);
+ method public final long updateAndGet(java.util.function.LongUnaryOperator);
method public final boolean weakCompareAndSet(long, long);
}
public class AtomicLongArray implements java.io.Serializable {
ctor public AtomicLongArray(int);
ctor public AtomicLongArray(long[]);
+ method public final long accumulateAndGet(int, long, java.util.function.LongBinaryOperator);
method public long addAndGet(int, long);
method public final boolean compareAndSet(int, long, long);
method public final long decrementAndGet(int);
method public final long get(int);
+ method public final long getAndAccumulate(int, long, java.util.function.LongBinaryOperator);
method public final long getAndAdd(int, long);
method public final long getAndDecrement(int);
method public final long getAndIncrement(int);
method public final long getAndSet(int, long);
+ method public final long getAndUpdate(int, java.util.function.LongUnaryOperator);
method public final long incrementAndGet(int);
method public final void lazySet(int, long);
method public final int length();
method public final void set(int, long);
+ method public final long updateAndGet(int, java.util.function.LongUnaryOperator);
method public final boolean weakCompareAndSet(int, long, long);
}
public abstract class AtomicLongFieldUpdater {
ctor protected AtomicLongFieldUpdater();
+ method public final long accumulateAndGet(T, long, java.util.function.LongBinaryOperator);
method public long addAndGet(T, long);
method public abstract boolean compareAndSet(T, long, long);
method public long decrementAndGet(T);
method public abstract long get(T);
+ method public final long getAndAccumulate(T, long, java.util.function.LongBinaryOperator);
method public long getAndAdd(T, long);
method public long getAndDecrement(T);
method public long getAndIncrement(T);
method public long getAndSet(T, long);
+ method public final long getAndUpdate(T, java.util.function.LongUnaryOperator);
method public long incrementAndGet(T);
method public abstract void lazySet(T, long);
method public static java.util.concurrent.atomic.AtomicLongFieldUpdater<U> newUpdater(java.lang.Class<U>, java.lang.String);
method public abstract void set(T, long);
+ method public final long updateAndGet(T, java.util.function.LongUnaryOperator);
method public abstract boolean weakCompareAndSet(T, long, long);
}
@@ -62603,34 +63413,46 @@
public class AtomicReference implements java.io.Serializable {
ctor public AtomicReference(V);
ctor public AtomicReference();
+ method public final V accumulateAndGet(V, java.util.function.BinaryOperator<V>);
method public final boolean compareAndSet(V, V);
method public final V get();
+ method public final V getAndAccumulate(V, java.util.function.BinaryOperator<V>);
method public final V getAndSet(V);
+ method public final V getAndUpdate(java.util.function.UnaryOperator<V>);
method public final void lazySet(V);
method public final void set(V);
+ method public final V updateAndGet(java.util.function.UnaryOperator<V>);
method public final boolean weakCompareAndSet(V, V);
}
public class AtomicReferenceArray implements java.io.Serializable {
ctor public AtomicReferenceArray(int);
ctor public AtomicReferenceArray(E[]);
+ method public final E accumulateAndGet(int, E, java.util.function.BinaryOperator<E>);
method public final boolean compareAndSet(int, E, E);
method public final E get(int);
+ method public final E getAndAccumulate(int, E, java.util.function.BinaryOperator<E>);
method public final E getAndSet(int, E);
+ method public final E getAndUpdate(int, java.util.function.UnaryOperator<E>);
method public final void lazySet(int, E);
method public final int length();
method public final void set(int, E);
+ method public final E updateAndGet(int, java.util.function.UnaryOperator<E>);
method public final boolean weakCompareAndSet(int, E, E);
}
public abstract class AtomicReferenceFieldUpdater {
ctor protected AtomicReferenceFieldUpdater();
+ method public final V accumulateAndGet(T, V, java.util.function.BinaryOperator<V>);
method public abstract boolean compareAndSet(T, V, V);
method public abstract V get(T);
+ method public final V getAndAccumulate(T, V, java.util.function.BinaryOperator<V>);
method public V getAndSet(T, V);
+ method public final V getAndUpdate(T, java.util.function.UnaryOperator<V>);
method public abstract void lazySet(T, V);
method public static java.util.concurrent.atomic.AtomicReferenceFieldUpdater<U, W> newUpdater(java.lang.Class<U>, java.lang.Class<W>, java.lang.String);
method public abstract void set(T, V);
+ method public final V updateAndGet(T, java.util.function.UnaryOperator<V>);
method public abstract boolean weakCompareAndSet(T, V, V);
}
@@ -62645,6 +63467,59 @@
method public boolean weakCompareAndSet(V, V, int, int);
}
+ public class DoubleAccumulator extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public DoubleAccumulator(java.util.function.DoubleBinaryOperator, double);
+ method public void accumulate(double);
+ method public double doubleValue();
+ method public float floatValue();
+ method public double get();
+ method public double getThenReset();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ }
+
+ public class DoubleAdder extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public DoubleAdder();
+ method public void add(double);
+ method public double doubleValue();
+ method public float floatValue();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ method public double sum();
+ method public double sumThenReset();
+ }
+
+ public class LongAccumulator extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public LongAccumulator(java.util.function.LongBinaryOperator, long);
+ method public void accumulate(long);
+ method public double doubleValue();
+ method public float floatValue();
+ method public long get();
+ method public long getThenReset();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ }
+
+ public class LongAdder extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public LongAdder();
+ method public void add(long);
+ method public void decrement();
+ method public double doubleValue();
+ method public float floatValue();
+ method public void increment();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ method public long sum();
+ method public long sumThenReset();
+ }
+
+ abstract class Striped64 extends java.lang.Number {
+ }
+
}
package java.util.concurrent.locks {
@@ -62852,6 +63727,34 @@
method public void unlock();
}
+ public class StampedLock implements java.io.Serializable {
+ ctor public StampedLock();
+ method public java.util.concurrent.locks.Lock asReadLock();
+ method public java.util.concurrent.locks.ReadWriteLock asReadWriteLock();
+ method public java.util.concurrent.locks.Lock asWriteLock();
+ method public int getReadLockCount();
+ method public boolean isReadLocked();
+ method public boolean isWriteLocked();
+ method public long readLock();
+ method public long readLockInterruptibly() throws java.lang.InterruptedException;
+ method public long tryConvertToOptimisticRead(long);
+ method public long tryConvertToReadLock(long);
+ method public long tryConvertToWriteLock(long);
+ method public long tryOptimisticRead();
+ method public long tryReadLock();
+ method public long tryReadLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public boolean tryUnlockRead();
+ method public boolean tryUnlockWrite();
+ method public long tryWriteLock();
+ method public long tryWriteLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public void unlock(long);
+ method public void unlockRead(long);
+ method public void unlockWrite(long);
+ method public boolean validate(long);
+ method public long writeLock();
+ method public long writeLockInterruptibly() throws java.lang.InterruptedException;
+ }
+
}
package java.util.function {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 9ebc484..79f7297 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -41,6 +41,14 @@
}
+package android.media.tv {
+
+ public class TvView extends android.view.ViewGroup {
+ method public void requestUnblockContent(android.media.tv.TvContentRating);
+ }
+
+}
+
package android.net {
public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
diff --git a/api/test-current.txt b/api/test-current.txt
index 98a000e..6a54f319 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -38,6 +38,7 @@
field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
field public static final java.lang.String BIND_VOICE_INTERACTION = "android.permission.BIND_VOICE_INTERACTION";
field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
+ field public static final java.lang.String BIND_VR_LISTENER_SERVICE = "android.permission.BIND_VR_LISTENER_SERVICE";
field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN";
@@ -114,6 +115,7 @@
field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
+ field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
@@ -877,6 +879,7 @@
field public static final int nextFocusLeft = 16842977; // 0x10100e1
field public static final int nextFocusRight = 16842978; // 0x10100e2
field public static final int nextFocusUp = 16842979; // 0x10100e3
+ field public static final int nfcAntennaPositionDrawable = 16844063; // 0x101051f
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
field public static final int notificationTimeout = 16843651; // 0x1010383
@@ -3439,7 +3442,7 @@
method public android.view.View getCurrentFocus();
method public android.app.FragmentManager getFragmentManager();
method public android.content.Intent getIntent();
- method public deprecated java.lang.Object getLastNonConfigurationInstance();
+ method public java.lang.Object getLastNonConfigurationInstance();
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
@@ -3540,7 +3543,7 @@
method protected void onRestoreInstanceState(android.os.Bundle);
method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
method protected void onResume();
- method public deprecated java.lang.Object onRetainNonConfigurationInstance();
+ method public java.lang.Object onRetainNonConfigurationInstance();
method protected void onSaveInstanceState(android.os.Bundle);
method public void onSaveInstanceState(android.os.Bundle, android.os.PersistableBundle);
method public boolean onSearchRequested(android.view.SearchEvent);
@@ -3604,7 +3607,7 @@
method public deprecated void setTitleColor(int);
method public void setVisible(boolean);
method public final void setVolumeControlStream(int);
- method public void setVrMode(boolean);
+ method public void setVrModeEnabled(boolean, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
method public boolean shouldShowRequestPermissionRationale(java.lang.String);
method public boolean shouldUpRecreateTask(android.content.Intent);
method public boolean showAssist(android.os.Bundle);
@@ -5258,7 +5261,7 @@
}
public static class NotificationManager.Policy implements android.os.Parcelable {
- ctor public deprecated NotificationManager.Policy(int, int, int);
+ ctor public NotificationManager.Policy(int, int, int);
ctor public NotificationManager.Policy(int, int, int, int);
method public int describeContents();
method public static java.lang.String priorityCategoriesToString(int);
@@ -5887,7 +5890,7 @@
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
- method public java.lang.String getWifiMacAddress();
+ method public java.lang.String getWifiMacAddress(android.content.ComponentName);
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -5980,6 +5983,7 @@
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
+ field public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5; // 0x5
field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
@@ -6373,8 +6377,8 @@
method public long getTxPackets();
method public int getUid();
field public static final int ROAMING_ALL = -1; // 0xffffffff
- field public static final int ROAMING_DEFAULT = 1; // 0x1
- field public static final int ROAMING_ROAMING = 2; // 0x2
+ field public static final int ROAMING_NO = 1; // 0x1
+ field public static final int ROAMING_YES = 2; // 0x2
field public static final int STATE_ALL = -1; // 0xffffffff
field public static final int STATE_DEFAULT = 1; // 0x1
field public static final int STATE_FOREGROUND = 2; // 0x2
@@ -8116,7 +8120,7 @@
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
field public static final java.lang.String DROPBOX_SERVICE = "dropbox";
field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint";
- field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardwareproperties";
+ field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
@@ -8144,7 +8148,9 @@
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";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
@@ -9331,6 +9337,7 @@
field public int flags;
field public int largestWidthLimitDp;
field public java.lang.String manageSpaceActivityName;
+ field public java.lang.String minSdkVersion;
field public java.lang.String nativeLibraryDir;
field public java.lang.String permission;
field public java.lang.String processName;
@@ -9452,13 +9459,20 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
+ method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo, android.os.UserHandle);
+ method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
+ method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
+ 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 boolean startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
}
@@ -9471,6 +9485,18 @@
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 void setActivity(android.content.ComponentName);
+ method public void setChangedSince(long);
+ method public void setPackage(java.lang.String);
+ method public void setQueryFlags(int);
+ field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
+ field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+ field public static final int FLAG_GET_PINNED = 2; // 0x2
}
public class PackageInfo implements android.os.Parcelable {
@@ -9548,6 +9574,7 @@
method public java.lang.String[] getNames() throws java.io.IOException;
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
+ method public void removeSplit(java.lang.String) throws java.io.IOException;
method public void setStagingProgress(float);
}
@@ -9971,6 +9998,59 @@
field public java.lang.String permission;
}
+ public class ShortcutInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.content.ComponentName getActivityComponent();
+ method public android.os.PersistableBundle getExtras();
+ method public java.lang.String getId();
+ method public android.content.Intent getIntent();
+ method public long getLastChangedTimestamp();
+ method public java.lang.String getPackageName();
+ method public java.lang.String getTitle();
+ method public int getWeight();
+ method public boolean hasIconFile();
+ method public boolean hasIconResource();
+ method public boolean hasKeyFieldsOnly();
+ method public boolean isDynamic();
+ method public boolean isPinned();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
+ field public static final int CLONE_REMOVE_FOR_LAUNCHER = 3; // 0x3
+ field public static final int CLONE_REMOVE_NON_KEY_INFO = 4; // 0x4
+ field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
+ field public static final int FLAG_DYNAMIC = 1; // 0x1
+ field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
+ field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
+ field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
+ field public static final int FLAG_PINNED = 2; // 0x2
+ }
+
+ public static class ShortcutInfo.Builder {
+ ctor public ShortcutInfo.Builder(android.content.Context);
+ method public android.content.pm.ShortcutInfo build();
+ method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+ 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 setId(java.lang.String);
+ method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
+ method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
+ method public android.content.pm.ShortcutInfo.Builder setWeight(int);
+ }
+
+ public class ShortcutManager {
+ method public boolean addDynamicShortcut(android.content.pm.ShortcutInfo);
+ method public void deleteAllDynamicShortcuts();
+ method public void deleteDynamicShortcut(java.lang.String);
+ method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
+ method public int getIconMaxDimensions();
+ method public int getMaxDynamicShortcutCount();
+ method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
+ method public long getRateLimitResetTime();
+ method public int getRemainingCallCount();
+ 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);
@@ -10047,7 +10127,6 @@
method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
- method public int getChangingConfigurations();
method public int getColorForState(int[], int);
method public int getDefaultColor();
method public boolean isOpaque();
@@ -10059,6 +10138,7 @@
public abstract class ComplexColor {
ctor public ComplexColor();
+ method public int getChangingConfigurations();
method public abstract int getDefaultColor();
method public boolean isStateful();
}
@@ -10476,7 +10556,6 @@
method public boolean hasNext();
method public java.util.Iterator<android.database.CursorJoiner.Result> iterator();
method public android.database.CursorJoiner.Result next();
- method public void remove();
}
public static final class CursorJoiner.Result extends java.lang.Enum {
@@ -12847,7 +12926,8 @@
method public int getGradientType();
method public int getOpacity();
method public android.graphics.drawable.GradientDrawable.Orientation getOrientation();
- method public boolean isUseLevel();
+ method public int getShape();
+ method public boolean getUseLevel();
method public void setAlpha(int);
method public void setColor(int);
method public void setColor(android.content.res.ColorStateList);
@@ -12966,9 +13046,9 @@
method public void setPaddingMode(int);
method public void setPaddingRelative(int, int, int, int);
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
+ field public static final int INSET_UNDEFINED = -2147483648; // 0x80000000
field public static final int PADDING_MODE_NEST = 0; // 0x0
field public static final int PADDING_MODE_STACK = 1; // 0x1
- field public static final int UNDEFINED_INSET = -2147483648; // 0x80000000
}
public class LevelListDrawable extends android.graphics.drawable.DrawableContainer {
@@ -13709,6 +13789,7 @@
public static abstract class CameraCaptureSession.CaptureCallback {
ctor public CameraCaptureSession.CaptureCallback();
+ method public void onCaptureBufferLost(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.view.Surface, long);
method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
@@ -19165,6 +19246,7 @@
}
public final class GnssClock implements android.os.Parcelable {
+ ctor public GnssClock();
method public int describeContents();
method public double getBiasNanos();
method public double getBiasUncertaintyNanos();
@@ -19205,6 +19287,7 @@
}
public final class GnssMeasurement implements android.os.Parcelable {
+ ctor public GnssMeasurement();
method public int describeContents();
method public double getAccumulatedDeltaRangeMeters();
method public int getAccumulatedDeltaRangeState();
@@ -19247,6 +19330,7 @@
method public void setCn0DbHz(double);
method public void setConstellationType(int);
method public void setMultipathIndicator(int);
+ method public void setPseudorangeRateCorrected(boolean);
method public void setPseudorangeRateMetersPerSecond(double);
method public void setPseudorangeRateUncertaintyMetersPerSecond(double);
method public void setReceivedSvTimeNanos(long);
@@ -19297,6 +19381,7 @@
}
public final class GnssNavigationMessage implements android.os.Parcelable {
+ ctor public GnssNavigationMessage();
method public int describeContents();
method public byte[] getData();
method public int getMessageId();
@@ -19732,7 +19817,7 @@
method public void adjustVolume(int, int);
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int generateAudioSessionId();
- method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
+ method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
method public android.media.AudioDeviceInfo[] getDevices(int);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -19878,7 +19963,7 @@
public static abstract class AudioManager.AudioRecordingCallback {
ctor public AudioManager.AudioRecordingCallback();
- method public void onRecordConfigChanged();
+ method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
}
public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19952,7 +20037,7 @@
method public abstract void onRoutingChanged(android.media.AudioRecord);
}
- public final class AudioRecordConfiguration implements android.os.Parcelable {
+ public final class AudioRecordingConfiguration implements android.os.Parcelable {
method public int describeContents();
method public android.media.AudioDeviceInfo getAudioDevice();
method public int getClientAudioSessionId();
@@ -19960,7 +20045,7 @@
method public android.media.AudioFormat getClientFormat();
method public android.media.AudioFormat getFormat();
method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+ field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
}
public abstract interface AudioRouting {
@@ -20168,37 +20253,129 @@
field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final java.lang.String TAG_APERTURE = "FNumber";
+ field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+ field public static final java.lang.String TAG_ARTIST = "Artist";
+ field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+ field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+ field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+ field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+ field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+ field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+ field public static final java.lang.String TAG_COMPRESSION = "Compression";
+ field public static final java.lang.String TAG_CONTRAST = "Contrast";
+ field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+ field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
field public static final java.lang.String TAG_DATETIME = "DateTime";
field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+ field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+ field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+ field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
field public static final java.lang.String TAG_FLASH = "Flash";
+ field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+ field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+ field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+ field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+ field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+ field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+ field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+ field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+ field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+ field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+ field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+ field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+ field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+ field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+ field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+ field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+ field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+ field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+ field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+ field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+ field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+ field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+ field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+ field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+ field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+ field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
field public static final java.lang.String TAG_ISO = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+ field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
field public static final java.lang.String TAG_MAKE = "Make";
+ field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+ field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
field public static final java.lang.String TAG_MODEL = "Model";
+ field public static final java.lang.String TAG_OECF = "OECF";
field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+ field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+ field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+ field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+ field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+ field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+ field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+ field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+ field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+ field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+ field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+ field public static final java.lang.String TAG_SATURATION = "Saturation";
+ field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+ field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+ field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+ field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+ field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+ field public static final java.lang.String TAG_SOFTWARE = "Software";
+ field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+ field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+ field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+ field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+ field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+ field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+ field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
field public static final java.lang.String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+ field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
field public static final java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+ field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+ field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+ field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+ field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+ field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+ field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+ field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+ field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+ field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
field public static final int WHITEBALANCE_AUTO = 0; // 0x0
field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
}
@@ -20396,6 +20573,7 @@
field public static final int ERROR_NO_KEY = 1; // 0x1
field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
+ field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6
}
public static final class MediaCodec.CryptoInfo {
@@ -20862,6 +21040,7 @@
method public final void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException;
method public final void setDataSource(java.lang.String) throws java.io.IOException;
+ method public final void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public final void setDataSource(java.io.FileDescriptor) throws java.io.IOException;
method public final void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException;
method public void unselectTrack(int);
@@ -20922,6 +21101,7 @@
field public static final java.lang.String KEY_DURATION = "durationUs";
field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
+ field public static final java.lang.String KEY_HDR_STATIC_INFO = "hdr-static-info";
field public static final java.lang.String KEY_HEIGHT = "height";
field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
field public static final java.lang.String KEY_IS_ADTS = "is-adts";
@@ -23031,6 +23211,7 @@
method public boolean onTouchEvent(android.view.MotionEvent);
method public boolean onTrackballEvent(android.view.MotionEvent);
method public abstract boolean onTune(android.net.Uri);
+ method public boolean onTune(android.net.Uri, android.os.Bundle);
method public void onUnblockContent(android.media.tv.TvContentRating);
method public void setOverlayViewEnabled(boolean);
}
@@ -23104,12 +23285,15 @@
method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
method public void setStreamVolume(float);
method public void setTimeShiftPositionCallback(android.media.tv.TvView.TimeShiftPositionCallback);
+ method public void setZOrderMediaOverlay(boolean);
+ method public void setZOrderOnTop(boolean);
method public void timeShiftPause();
method public void timeShiftPlay(java.lang.String, android.net.Uri);
method public void timeShiftResume();
method public void timeShiftSeekTo(long);
method public void timeShiftSetPlaybackParams(android.media.PlaybackParams);
method public void tune(java.lang.String, android.net.Uri);
+ method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
}
public static abstract interface TvView.OnUnhandledInputEventListener {
@@ -29058,6 +29242,7 @@
public class Process {
ctor public Process();
method public static final long getElapsedCpuTime();
+ method public static final int[] getExclusiveCores();
method public static final int getGidForName(java.lang.String);
method public static final long getStartElapsedRealtime();
method public static final long getStartUptimeMillis();
@@ -29343,6 +29528,147 @@
}
+package android.os.health {
+
+ public class HealthStats {
+ method public java.lang.String getDataType();
+ method public long getMeasurement(int);
+ method public int getMeasurementKeyAt(int);
+ method public int getMeasurementKeyCount();
+ method public java.util.Map<java.lang.String, java.lang.Long> getMeasurements(int);
+ method public int getMeasurementsKeyAt(int);
+ method public int getMeasurementsKeyCount();
+ method public java.util.Map<java.lang.String, android.os.health.HealthStats> getStats(int);
+ method public int getStatsKeyAt(int);
+ method public int getStatsKeyCount();
+ method public android.os.health.TimerStat getTimer(int);
+ method public int getTimerCount(int);
+ method public int getTimerKeyAt(int);
+ method public int getTimerKeyCount();
+ method public long getTimerTime(int);
+ method public java.util.Map<java.lang.String, android.os.health.TimerStat> getTimers(int);
+ method public int getTimersKeyAt(int);
+ method public int getTimersKeyCount();
+ method public boolean hasMeasurement(int);
+ method public boolean hasMeasurements(int);
+ method public boolean hasStats(int);
+ method public boolean hasTimer(int);
+ method public boolean hasTimers(int);
+ }
+
+ public final class PackageHealthStats {
+ field public static final int MEASUREMENTS_WAKEUP_ALARMS_COUNT = 40002; // 0x9c42
+ field public static final int STATS_SERVICES = 40001; // 0x9c41
+ }
+
+ public final class PidHealthStats {
+ field public static final int MEASUREMENT_WAKE_NESTING_COUNT = 20001; // 0x4e21
+ field public static final int MEASUREMENT_WAKE_START_MS = 20003; // 0x4e23
+ field public static final int MEASUREMENT_WAKE_SUM_MS = 20002; // 0x4e22
+ }
+
+ public final class ProcessHealthStats {
+ field public static final int MEASUREMENT_ANR_COUNT = 30005; // 0x7535
+ field public static final int MEASUREMENT_CRASHES_COUNT = 30004; // 0x7534
+ field public static final int MEASUREMENT_FOREGROUND_MS = 30006; // 0x7536
+ field public static final int MEASUREMENT_STARTS_COUNT = 30003; // 0x7533
+ field public static final int MEASUREMENT_SYSTEM_TIME_MS = 30002; // 0x7532
+ field public static final int MEASUREMENT_USER_TIME_MS = 30001; // 0x7531
+ }
+
+ public final class ServiceHealthStats {
+ field public static final int MEASUREMENT_LAUNCH_COUNT = 50002; // 0xc352
+ field public static final int MEASUREMENT_START_SERVICE_COUNT = 50001; // 0xc351
+ }
+
+ public class SystemHealthManager {
+ method public static android.os.health.SystemHealthManager from(android.content.Context);
+ method public android.os.health.HealthStats takeMyUidSnapshot();
+ method public android.os.health.HealthStats takeUidSnapshot(int);
+ method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
+ }
+
+ public class TimerStat implements android.os.Parcelable {
+ ctor public TimerStat();
+ ctor public TimerStat(int, long);
+ ctor public TimerStat(android.os.Parcel);
+ method public int describeContents();
+ method public int getCount();
+ method public long getTime();
+ method public void setCount(int);
+ method public void setTime(long);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.health.TimerStat> CREATOR;
+ }
+
+ public final class UidHealthStats {
+ field public static final int MEASUREMENT_BLUETOOTH_IDLE_MS = 10020; // 0x2724
+ field public static final int MEASUREMENT_BLUETOOTH_POWER_MAMS = 10023; // 0x2727
+ field public static final int MEASUREMENT_BLUETOOTH_RX_BYTES = 10052; // 0x2744
+ field public static final int MEASUREMENT_BLUETOOTH_RX_MS = 10021; // 0x2725
+ field public static final int MEASUREMENT_BLUETOOTH_RX_PACKETS = 10058; // 0x274a
+ field public static final int MEASUREMENT_BLUETOOTH_TX_BYTES = 10053; // 0x2745
+ field public static final int MEASUREMENT_BLUETOOTH_TX_MS = 10022; // 0x2726
+ field public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = 10059; // 0x274b
+ field public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = 10046; // 0x273e
+ field public static final int MEASUREMENT_CPU_POWER_MAUS = 10064; // 0x2750
+ field public static final int MEASUREMENT_MOBILE_IDLE_MS = 10024; // 0x2728
+ field public static final int MEASUREMENT_MOBILE_POWER_MAMS = 10027; // 0x272b
+ field public static final int MEASUREMENT_MOBILE_RX_BYTES = 10048; // 0x2740
+ field public static final int MEASUREMENT_MOBILE_RX_MS = 10025; // 0x2729
+ field public static final int MEASUREMENT_MOBILE_RX_PACKETS = 10054; // 0x2746
+ field public static final int MEASUREMENT_MOBILE_TX_BYTES = 10049; // 0x2741
+ field public static final int MEASUREMENT_MOBILE_TX_MS = 10026; // 0x272a
+ field public static final int MEASUREMENT_MOBILE_TX_PACKETS = 10055; // 0x2747
+ field public static final int MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = 10045; // 0x273d
+ field public static final int MEASUREMENT_REALTIME_BATTERY_MS = 10001; // 0x2711
+ field public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = 10003; // 0x2713
+ field public static final int MEASUREMENT_SYSTEM_CPU_TIME_US = 10063; // 0x274f
+ field public static final int MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT = 10047; // 0x273f
+ field public static final int MEASUREMENT_UPTIME_BATTERY_MS = 10002; // 0x2712
+ field public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = 10004; // 0x2714
+ field public static final int MEASUREMENT_USER_CPU_TIME_US = 10062; // 0x274e
+ field public static final int MEASUREMENT_WIFI_FULL_LOCK_MS = 10029; // 0x272d
+ field public static final int MEASUREMENT_WIFI_IDLE_MS = 10016; // 0x2720
+ field public static final int MEASUREMENT_WIFI_MULTICAST_MS = 10031; // 0x272f
+ field public static final int MEASUREMENT_WIFI_POWER_MAMS = 10019; // 0x2723
+ field public static final int MEASUREMENT_WIFI_RUNNING_MS = 10028; // 0x272c
+ field public static final int MEASUREMENT_WIFI_RX_BYTES = 10050; // 0x2742
+ field public static final int MEASUREMENT_WIFI_RX_MS = 10017; // 0x2721
+ field public static final int MEASUREMENT_WIFI_RX_PACKETS = 10056; // 0x2748
+ field public static final int MEASUREMENT_WIFI_TX_BYTES = 10051; // 0x2743
+ field public static final int MEASUREMENT_WIFI_TX_MS = 10018; // 0x2722
+ field public static final int MEASUREMENT_WIFI_TX_PACKETS = 10057; // 0x2749
+ field public static final int STATS_PACKAGES = 10015; // 0x271f
+ field public static final int STATS_PIDS = 10013; // 0x271d
+ field public static final int STATS_PROCESSES = 10014; // 0x271e
+ field public static final int TIMERS_JOBS = 10010; // 0x271a
+ field public static final int TIMERS_SENSORS = 10012; // 0x271c
+ field public static final int TIMERS_SYNCS = 10009; // 0x2719
+ field public static final int TIMERS_WAKELOCKS_DRAW = 10008; // 0x2718
+ field public static final int TIMERS_WAKELOCKS_FULL = 10005; // 0x2715
+ field public static final int TIMERS_WAKELOCKS_PARTIAL = 10006; // 0x2716
+ field public static final int TIMERS_WAKELOCKS_WINDOW = 10007; // 0x2717
+ field public static final int TIMER_AUDIO = 10032; // 0x2730
+ field public static final int TIMER_BLUETOOTH_SCAN = 10037; // 0x2735
+ field public static final int TIMER_CAMERA = 10035; // 0x2733
+ field public static final int TIMER_FLASHLIGHT = 10034; // 0x2732
+ field public static final int TIMER_FOREGROUND_ACTIVITY = 10036; // 0x2734
+ field public static final int TIMER_GPS_SENSOR = 10011; // 0x271b
+ field public static final int TIMER_MOBILE_RADIO_ACTIVE = 10061; // 0x274d
+ field public static final int TIMER_PROCESS_STATE_BACKGROUND_MS = 10042; // 0x273a
+ field public static final int TIMER_PROCESS_STATE_CACHED_MS = 10043; // 0x273b
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_MS = 10041; // 0x2739
+ field public static final int TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS = 10039; // 0x2737
+ field public static final int TIMER_PROCESS_STATE_TOP_MS = 10038; // 0x2736
+ field public static final int TIMER_PROCESS_STATE_TOP_SLEEPING_MS = 10040; // 0x2738
+ field public static final int TIMER_VIBRATOR = 10044; // 0x273c
+ field public static final int TIMER_VIDEO = 10033; // 0x2731
+ field public static final int TIMER_WIFI_SCAN = 10030; // 0x272e
+ }
+
+}
+
package android.os.storage {
public abstract class OnObbStateChangeListener {
@@ -30507,6 +30833,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";
@@ -30529,6 +30856,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
@@ -31730,6 +32058,7 @@
public static final class DocumentsContract.Root {
field public static final java.lang.String COLUMN_AVAILABLE_BYTES = "available_bytes";
+ field public static final java.lang.String COLUMN_CAPACITY_BYTES = "capacity_bytes";
field public static final java.lang.String COLUMN_DOCUMENT_ID = "document_id";
field public static final java.lang.String COLUMN_FLAGS = "flags";
field public static final java.lang.String COLUMN_ICON = "icon";
@@ -32184,6 +32513,7 @@
field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+ field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
@@ -32221,6 +32551,7 @@
field public static final java.lang.String AUTO_TIME = "auto_time";
field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+ field public static final java.lang.String BOOT_COUNT = "boot_count";
field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DATA_ROAMING = "data_roaming";
@@ -34437,8 +34768,9 @@
package android.service.notification {
public class Condition implements android.os.Parcelable {
- ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int);
+ ctor public Condition(android.net.Uri, java.lang.String, int);
ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
+ ctor public Condition(android.os.Parcel);
method public android.service.notification.Condition copy();
method public int describeContents();
method public static boolean isValidId(android.net.Uri, java.lang.String);
@@ -34469,6 +34801,7 @@
method public final void notifyConditions(android.service.notification.Condition...);
method public android.os.IBinder onBind(android.content.Intent);
method public abstract void onConnected();
+ method public void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
@@ -34503,7 +34836,6 @@
method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
method public final void requestUnbind() throws android.os.RemoteException;
method public final void setNotificationsShown(java.lang.String[]);
- field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
@@ -34798,6 +35130,17 @@
}
+package android.service.vr {
+
+ public abstract class VrListenerService extends android.app.Service {
+ ctor public VrListenerService();
+ method public static final boolean isVrModePackageEnabled(android.content.Context, android.content.ComponentName);
+ method public android.os.IBinder onBind(android.content.Intent);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
+ }
+
+}
+
package android.service.wallpaper {
public abstract class WallpaperService extends android.app.Service {
@@ -35790,9 +36133,11 @@
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
method public void postDialContinue(boolean);
+ method public void pullExternalCall();
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 void sendCallEvent(java.lang.String, android.os.Bundle);
method public void splitFromConference();
method public void stopDtmfTone();
method public void swapConference();
@@ -35806,6 +36151,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
}
@@ -35816,6 +36162,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);
@@ -35846,6 +36193,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
@@ -35865,6 +36213,7 @@
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
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
field public static final int PROPERTY_WORK_CALL = 32; // 0x20
}
@@ -35979,15 +36328,19 @@
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 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 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);
@@ -36011,9 +36364,12 @@
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 = 33554432; // 0x2000000
+ 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
field public static final int CAPABILITY_HOLD = 1; // 0x1
+ field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -36027,6 +36383,7 @@
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_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
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";
@@ -36036,6 +36393,7 @@
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
}
@@ -36113,7 +36471,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;
@@ -36149,6 +36509,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);
@@ -36299,6 +36660,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();
@@ -36315,6 +36677,7 @@
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 onDestroyed(android.telecom.RemoteConnection);
method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -36412,6 +36775,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
@@ -38497,7 +38861,6 @@
method public boolean hasNext();
method public java.util.Iterator<java.lang.String> iterator();
method public java.lang.String next();
- method public void remove();
method public void setString(java.lang.String);
}
@@ -40109,6 +40472,7 @@
method public int describeContents();
method public static android.util.LocaleList forLanguageTags(java.lang.String);
method public java.util.Locale get(int);
+ method public static android.util.LocaleList getAdjustedDefault();
method public static android.util.LocaleList getDefault();
method public static android.util.LocaleList getEmptyLocaleList();
method public java.util.Locale getFirstMatch(java.lang.String[]);
@@ -50646,6 +51010,7 @@
public abstract interface Iterable {
method public default void forEach(java.util.function.Consumer<? super T>);
method public abstract java.util.Iterator<T> iterator();
+ method public default java.util.Spliterator<T> spliterator();
}
public class LinkageError extends java.lang.Error {
@@ -57066,6 +57431,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class ArrayList extends java.util.AbstractList implements java.lang.Cloneable java.util.List java.util.RandomAccess java.io.Serializable {
@@ -57076,7 +57442,9 @@
method public void ensureCapacity(int);
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
+ method public boolean removeIf(java.util.function.Predicate<? super E>);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public void trimToSize();
}
@@ -57159,6 +57527,24 @@
method public static int hashCode(float[]);
method public static int hashCode(double[]);
method public static int hashCode(java.lang.Object[]);
+ method public static void parallelSort(byte[]);
+ method public static void parallelSort(byte[], int, int);
+ method public static void parallelSort(char[]);
+ method public static void parallelSort(char[], int, int);
+ method public static void parallelSort(short[]);
+ method public static void parallelSort(short[], int, int);
+ method public static void parallelSort(int[]);
+ method public static void parallelSort(int[], int, int);
+ method public static void parallelSort(long[]);
+ method public static void parallelSort(long[], int, int);
+ method public static void parallelSort(float[]);
+ method public static void parallelSort(float[], int, int);
+ method public static void parallelSort(double[]);
+ method public static void parallelSort(double[], int, int);
+ method public static void parallelSort(T[]);
+ method public static void parallelSort(T[], int, int);
+ method public static void parallelSort(T[], java.util.Comparator<? super T>);
+ method public static void parallelSort(T[], int, int, java.util.Comparator<? super T>);
method public static void sort(int[]);
method public static void sort(int[], int, int);
method public static void sort(long[]);
@@ -57177,6 +57563,14 @@
method public static void sort(java.lang.Object[], int, int);
method public static void sort(T[], java.util.Comparator<? super T>);
method public static void sort(T[], int, int, java.util.Comparator<? super T>);
+ method public static java.util.Spliterator<T> spliterator(T[]);
+ method public static java.util.Spliterator<T> spliterator(T[], int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[]);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[]);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[]);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int);
method public static java.lang.String toString(long[]);
method public static java.lang.String toString(int[]);
method public static java.lang.String toString(short[]);
@@ -57338,6 +57732,7 @@
method public abstract java.util.Iterator<E> iterator();
method public abstract boolean remove(java.lang.Object);
method public abstract boolean removeAll(java.util.Collection<?>);
+ method public default boolean removeIf(java.util.function.Predicate<? super E>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract int size();
method public abstract java.lang.Object[] toArray();
@@ -57407,7 +57802,23 @@
public abstract interface Comparator {
method public abstract int compare(T, T);
+ method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+ method public static java.util.Comparator<T> comparing(java.util.function.Function<? super T, ? extends U>);
+ method public static java.util.Comparator<T> comparingDouble(java.util.function.ToDoubleFunction<? super T>);
+ method public static java.util.Comparator<T> comparingInt(java.util.function.ToIntFunction<? super T>);
+ method public static java.util.Comparator<T> comparingLong(java.util.function.ToLongFunction<? super T>);
method public abstract boolean equals(java.lang.Object);
+ method public static java.util.Comparator<T> naturalOrder();
+ method public static java.util.Comparator<T> nullsFirst(java.util.Comparator<? super T>);
+ method public static java.util.Comparator<T> nullsLast(java.util.Comparator<? super T>);
+ method public static java.util.Comparator<T> reverseOrder();
+ method public default java.util.Comparator<T> reversed();
+ method public default java.util.Comparator<T> thenComparing(java.util.Comparator<? super T>);
+ method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>, java.util.Comparator<? super U>);
+ method public default java.util.Comparator<T> thenComparing(java.util.function.Function<? super T, ? extends U>);
+ method public default java.util.Comparator<T> thenComparingDouble(java.util.function.ToDoubleFunction<? super T>);
+ method public default java.util.Comparator<T> thenComparingInt(java.util.function.ToIntFunction<? super T>);
+ method public default java.util.Comparator<T> thenComparingLong(java.util.function.ToLongFunction<? super T>);
}
public class ConcurrentModificationException extends java.lang.RuntimeException {
@@ -57504,6 +57915,17 @@
method public abstract int size();
}
+ public class DoubleSummaryStatistics implements java.util.function.DoubleConsumer {
+ ctor public DoubleSummaryStatistics();
+ method public void accept(double);
+ method public void combine(java.util.DoubleSummaryStatistics);
+ method public final double getAverage();
+ method public final long getCount();
+ method public final double getMax();
+ method public final double getMin();
+ method public final double getSum();
+ }
+
public class DuplicateFormatFlagsException extends java.util.IllegalFormatException {
ctor public DuplicateFormatFlagsException(java.lang.String);
method public java.lang.String getFlags();
@@ -57649,6 +58071,7 @@
method public java.lang.Object clone();
method public java.util.Iterator<E> iterator();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class Hashtable extends java.util.Dictionary implements java.lang.Cloneable java.util.Map java.io.Serializable {
@@ -57726,15 +58149,27 @@
ctor public InputMismatchException(java.lang.String);
}
+ public class IntSummaryStatistics implements java.util.function.IntConsumer {
+ ctor public IntSummaryStatistics();
+ method public void accept(int);
+ method public void combine(java.util.IntSummaryStatistics);
+ method public final double getAverage();
+ method public final long getCount();
+ method public final int getMax();
+ method public final int getMin();
+ method public final long getSum();
+ }
+
public class InvalidPropertiesFormatException extends java.io.IOException {
ctor public InvalidPropertiesFormatException(java.lang.Throwable);
ctor public InvalidPropertiesFormatException(java.lang.String);
}
public abstract interface Iterator {
+ method public default void forEachRemaining(java.util.function.Consumer<? super E>);
method public abstract boolean hasNext();
method public abstract E next();
- method public abstract void remove();
+ method public default void remove();
}
public class LinkedHashMap extends java.util.HashMap implements java.util.Map {
@@ -57781,6 +58216,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public abstract interface List implements java.util.Collection {
@@ -57916,25 +58352,51 @@
enum_constant public static final java.util.Locale.Category FORMAT;
}
+ public class LongSummaryStatistics implements java.util.function.IntConsumer java.util.function.LongConsumer {
+ ctor public LongSummaryStatistics();
+ method public void accept(int);
+ method public void accept(long);
+ method public void combine(java.util.LongSummaryStatistics);
+ method public final double getAverage();
+ method public final long getCount();
+ method public final long getMax();
+ method public final long getMin();
+ method public final long getSum();
+ }
+
public abstract interface Map {
method public abstract void clear();
+ method public default V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public default V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public default V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public abstract boolean containsKey(java.lang.Object);
method public abstract boolean containsValue(java.lang.Object);
method public abstract java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public abstract boolean equals(java.lang.Object);
method public default void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public abstract V get(java.lang.Object);
+ method public default V getOrDefault(java.lang.Object, V);
method public abstract int hashCode();
method public abstract boolean isEmpty();
method public abstract java.util.Set<K> keySet();
+ method public default V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public abstract V put(K, V);
method public abstract void putAll(java.util.Map<? extends K, ? extends V>);
+ method public default V putIfAbsent(K, V);
method public abstract V remove(java.lang.Object);
+ method public default boolean remove(java.lang.Object, java.lang.Object);
+ method public default boolean replace(K, V, V);
+ method public default V replace(K, V);
+ method public default void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public abstract int size();
method public abstract java.util.Collection<V> values();
}
public static abstract interface Map.Entry {
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey();
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByKey(java.util.Comparator<? super K>);
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByValue();
+ method public static java.util.Comparator<java.util.Map.Entry<K, V>> comparingByValue(java.util.Comparator<? super V>);
method public abstract boolean equals(java.lang.Object);
method public abstract K getKey();
method public abstract V getValue();
@@ -58085,9 +58547,35 @@
method public long orElseThrow(java.util.function.Supplier<X>) throws java.lang.Throwable;
}
+ public abstract interface PrimitiveIterator implements java.util.Iterator {
+ method public abstract void forEachRemaining(T_CONS);
+ }
+
+ public static abstract interface PrimitiveIterator.OfDouble implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public default java.lang.Double next();
+ method public abstract double nextDouble();
+ }
+
+ public static abstract interface PrimitiveIterator.OfInt implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public default java.lang.Integer next();
+ method public abstract int nextInt();
+ }
+
+ public static abstract interface PrimitiveIterator.OfLong implements java.util.PrimitiveIterator {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public default java.lang.Long next();
+ method public abstract long nextLong();
+ }
+
public class PriorityQueue extends java.util.AbstractQueue implements java.io.Serializable {
ctor public PriorityQueue();
ctor public PriorityQueue(int);
+ ctor public PriorityQueue(java.util.Comparator<? super E>);
ctor public PriorityQueue(int, java.util.Comparator<? super E>);
ctor public PriorityQueue(java.util.Collection<? extends E>);
ctor public PriorityQueue(java.util.PriorityQueue<? extends E>);
@@ -58098,6 +58586,7 @@
method public E peek();
method public E poll();
method public int size();
+ method public final java.util.Spliterator<E> spliterator();
}
public class Properties extends java.util.Hashtable {
@@ -58256,7 +58745,6 @@
method public short nextShort();
method public short nextShort(int);
method public int radix();
- method public void remove();
method public java.util.Scanner reset();
method public java.util.Scanner skip(java.util.regex.Pattern);
method public java.util.Scanner skip(java.lang.String);
@@ -58341,6 +58829,111 @@
method public abstract java.util.SortedSet<E> tailSet(E);
}
+ public abstract interface Spliterator {
+ method public abstract int characteristics();
+ method public abstract long estimateSize();
+ method public default void forEachRemaining(java.util.function.Consumer<? super T>);
+ method public default java.util.Comparator<? super T> getComparator();
+ method public default long getExactSizeIfKnown();
+ method public default boolean hasCharacteristics(int);
+ method public abstract boolean tryAdvance(java.util.function.Consumer<? super T>);
+ method public abstract java.util.Spliterator<T> trySplit();
+ field public static final int CONCURRENT = 4096; // 0x1000
+ field public static final int DISTINCT = 1; // 0x1
+ field public static final int IMMUTABLE = 1024; // 0x400
+ field public static final int NONNULL = 256; // 0x100
+ field public static final int ORDERED = 16; // 0x10
+ field public static final int SIZED = 64; // 0x40
+ field public static final int SORTED = 4; // 0x4
+ field public static final int SUBSIZED = 16384; // 0x4000
+ }
+
+ public static abstract interface Spliterator.OfDouble implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.DoubleConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract boolean tryAdvance(java.util.function.DoubleConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Double>);
+ method public abstract java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract interface Spliterator.OfInt implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.IntConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract boolean tryAdvance(java.util.function.IntConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Integer>);
+ method public abstract java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract interface Spliterator.OfLong implements java.util.Spliterator.OfPrimitive {
+ method public default void forEachRemaining(java.util.function.LongConsumer);
+ method public default void forEachRemaining(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract boolean tryAdvance(java.util.function.LongConsumer);
+ method public default boolean tryAdvance(java.util.function.Consumer<? super java.lang.Long>);
+ method public abstract java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract interface Spliterator.OfPrimitive implements java.util.Spliterator {
+ method public default void forEachRemaining(T_CONS);
+ method public abstract boolean tryAdvance(T_CONS);
+ method public abstract T_SPLITR trySplit();
+ }
+
+ public final class Spliterators {
+ method public static java.util.Spliterator.OfDouble emptyDoubleSpliterator();
+ method public static java.util.Spliterator.OfInt emptyIntSpliterator();
+ method public static java.util.Spliterator.OfLong emptyLongSpliterator();
+ method public static java.util.Spliterator<T> emptySpliterator();
+ method public static java.util.Iterator<T> iterator(java.util.Spliterator<? extends T>);
+ method public static java.util.PrimitiveIterator.OfInt iterator(java.util.Spliterator.OfInt);
+ method public static java.util.PrimitiveIterator.OfLong iterator(java.util.Spliterator.OfLong);
+ method public static java.util.PrimitiveIterator.OfDouble iterator(java.util.Spliterator.OfDouble);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int);
+ method public static java.util.Spliterator<T> spliterator(java.lang.Object[], int, int, int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int);
+ method public static java.util.Spliterator.OfInt spliterator(int[], int, int, int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int);
+ method public static java.util.Spliterator.OfLong spliterator(long[], int, int, int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int);
+ method public static java.util.Spliterator.OfDouble spliterator(double[], int, int, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Collection<? extends T>, int);
+ method public static java.util.Spliterator<T> spliterator(java.util.Iterator<? extends T>, long, int);
+ method public static java.util.Spliterator.OfInt spliterator(java.util.PrimitiveIterator.OfInt, long, int);
+ method public static java.util.Spliterator.OfLong spliterator(java.util.PrimitiveIterator.OfLong, long, int);
+ method public static java.util.Spliterator.OfDouble spliterator(java.util.PrimitiveIterator.OfDouble, long, int);
+ method public static java.util.Spliterator<T> spliteratorUnknownSize(java.util.Iterator<? extends T>, int);
+ method public static java.util.Spliterator.OfInt spliteratorUnknownSize(java.util.PrimitiveIterator.OfInt, int);
+ method public static java.util.Spliterator.OfLong spliteratorUnknownSize(java.util.PrimitiveIterator.OfLong, int);
+ method public static java.util.Spliterator.OfDouble spliteratorUnknownSize(java.util.PrimitiveIterator.OfDouble, int);
+ }
+
+ public static abstract class Spliterators.AbstractDoubleSpliterator implements java.util.Spliterator.OfDouble {
+ ctor protected Spliterators.AbstractDoubleSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfDouble trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractIntSpliterator implements java.util.Spliterator.OfInt {
+ ctor protected Spliterators.AbstractIntSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfInt trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractLongSpliterator implements java.util.Spliterator.OfLong {
+ ctor protected Spliterators.AbstractLongSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator.OfLong trySplit();
+ }
+
+ public static abstract class Spliterators.AbstractSpliterator implements java.util.Spliterator {
+ ctor protected Spliterators.AbstractSpliterator(long, int);
+ method public int characteristics();
+ method public long estimateSize();
+ method public java.util.Spliterator<T> trySplit();
+ }
+
public class Stack extends java.util.Vector {
ctor public Stack();
method public boolean empty();
@@ -58350,6 +58943,15 @@
method public synchronized int search(java.lang.Object);
}
+ public final class StringJoiner {
+ ctor public StringJoiner(java.lang.CharSequence);
+ ctor public StringJoiner(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence);
+ method public java.util.StringJoiner add(java.lang.CharSequence);
+ method public int length();
+ method public java.util.StringJoiner merge(java.util.StringJoiner);
+ method public java.util.StringJoiner setEmptyValue(java.lang.CharSequence);
+ }
+
public class StringTokenizer implements java.util.Enumeration {
ctor public StringTokenizer(java.lang.String, java.lang.String, boolean);
ctor public StringTokenizer(java.lang.String, java.lang.String);
@@ -58471,6 +59073,7 @@
method public E pollFirst();
method public E pollLast();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
method public java.util.SortedSet<E> subSet(E, E);
method public java.util.NavigableSet<E> tailSet(E, boolean);
@@ -58524,9 +59127,11 @@
method public synchronized void removeAllElements();
method public synchronized boolean removeElement(java.lang.Object);
method public synchronized void removeElementAt(int);
+ method public synchronized boolean removeIf(java.util.function.Predicate<? super E>);
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
field protected int elementCount;
@@ -58574,6 +59179,7 @@
method public void put(E) throws java.lang.InterruptedException;
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -58637,6 +59243,78 @@
ctor public CancellationException(java.lang.String);
}
+ public class CompletableFuture implements java.util.concurrent.CompletionStage java.util.concurrent.Future {
+ ctor public CompletableFuture();
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Void> allOf(java.util.concurrent.CompletableFuture<?>...);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Object> anyOf(java.util.concurrent.CompletableFuture<?>...);
+ method public java.util.concurrent.CompletableFuture<U> applyToEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public java.util.concurrent.CompletableFuture<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public java.util.concurrent.CompletableFuture<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>, java.util.concurrent.Executor);
+ method public boolean cancel(boolean);
+ method public boolean complete(T);
+ method public boolean completeExceptionally(java.lang.Throwable);
+ method public static java.util.concurrent.CompletableFuture<U> completedFuture(U);
+ method public java.util.concurrent.CompletableFuture<T> exceptionally(java.util.function.Function<java.lang.Throwable, ? extends T>);
+ method public T get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+ method public T get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+ method public T getNow(T);
+ method public int getNumberOfDependents();
+ method public java.util.concurrent.CompletableFuture<U> handle(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>, java.util.concurrent.Executor);
+ method public boolean isCancelled();
+ method public boolean isCompletedExceptionally();
+ method public boolean isDone();
+ method public T join();
+ method public void obtrudeException(java.lang.Throwable);
+ method public void obtrudeValue(T);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBoth(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEither(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Void> runAsync(java.lang.Runnable);
+ method public static java.util.concurrent.CompletableFuture<java.lang.Void> runAsync(java.lang.Runnable, java.util.concurrent.Executor);
+ method public static java.util.concurrent.CompletableFuture<U> supplyAsync(java.util.function.Supplier<U>);
+ method public static java.util.concurrent.CompletableFuture<U> supplyAsync(java.util.function.Supplier<U>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAccept(java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBoth(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<U> thenApply(java.util.function.Function<? super T, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>);
+ method public java.util.concurrent.CompletableFuture<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<V> thenCombine(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public java.util.concurrent.CompletableFuture<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public java.util.concurrent.CompletableFuture<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<U> thenCompose(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public java.util.concurrent.CompletableFuture<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public java.util.concurrent.CompletableFuture<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRun(java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRunAsync(java.lang.Runnable);
+ method public java.util.concurrent.CompletableFuture<java.lang.Void> thenRunAsync(java.lang.Runnable, java.util.concurrent.Executor);
+ method public java.util.concurrent.CompletableFuture<T> toCompletableFuture();
+ method public java.util.concurrent.CompletableFuture<T> whenComplete(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public java.util.concurrent.CompletableFuture<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public java.util.concurrent.CompletableFuture<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>, java.util.concurrent.Executor);
+ }
+
+ public static abstract interface CompletableFuture.AsynchronousCompletionTask {
+ }
+
+ public class CompletionException extends java.lang.RuntimeException {
+ ctor protected CompletionException();
+ ctor protected CompletionException(java.lang.String);
+ ctor public CompletionException(java.lang.String, java.lang.Throwable);
+ ctor public CompletionException(java.lang.Throwable);
+ }
+
public abstract interface CompletionService {
method public abstract java.util.concurrent.Future<V> poll();
method public abstract java.util.concurrent.Future<V> poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
@@ -58645,20 +59323,130 @@
method public abstract java.util.concurrent.Future<V> take() throws java.lang.InterruptedException;
}
+ public abstract interface CompletionStage {
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> acceptEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> acceptEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<U> applyToEither(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public abstract java.util.concurrent.CompletionStage<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>);
+ method public abstract java.util.concurrent.CompletionStage<U> applyToEitherAsync(java.util.concurrent.CompletionStage<? extends T>, java.util.function.Function<? super T, U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<T> exceptionally(java.util.function.Function<java.lang.Throwable, ? extends T>);
+ method public abstract java.util.concurrent.CompletionStage<U> handle(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> handleAsync(java.util.function.BiFunction<? super T, java.lang.Throwable, ? extends U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterBoth(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterBothAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterEither(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> runAfterEitherAsync(java.util.concurrent.CompletionStage<?>, java.lang.Runnable, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAccept(java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptAsync(java.util.function.Consumer<? super T>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBoth(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenAcceptBothAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiConsumer<? super T, ? super U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<U> thenApply(java.util.function.Function<? super T, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenApplyAsync(java.util.function.Function<? super T, ? extends U>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<V> thenCombine(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public abstract java.util.concurrent.CompletionStage<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>);
+ method public abstract java.util.concurrent.CompletionStage<V> thenCombineAsync(java.util.concurrent.CompletionStage<? extends U>, java.util.function.BiFunction<? super T, ? super U, ? extends V>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<U> thenCompose(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>);
+ method public abstract java.util.concurrent.CompletionStage<U> thenComposeAsync(java.util.function.Function<? super T, ? extends java.util.concurrent.CompletionStage<U>>, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenRun(java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenRunAsync(java.lang.Runnable);
+ method public abstract java.util.concurrent.CompletionStage<java.lang.Void> thenRunAsync(java.lang.Runnable, java.util.concurrent.Executor);
+ method public abstract java.util.concurrent.CompletableFuture<T> toCompletableFuture();
+ method public abstract java.util.concurrent.CompletionStage<T> whenComplete(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public abstract java.util.concurrent.CompletionStage<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>);
+ method public abstract java.util.concurrent.CompletionStage<T> whenCompleteAsync(java.util.function.BiConsumer<? super T, ? super java.lang.Throwable>, java.util.concurrent.Executor);
+ }
+
public class ConcurrentHashMap extends java.util.AbstractMap implements java.util.concurrent.ConcurrentMap java.io.Serializable {
ctor public ConcurrentHashMap();
ctor public ConcurrentHashMap(int);
ctor public ConcurrentHashMap(java.util.Map<? extends K, ? extends V>);
ctor public ConcurrentHashMap(int, float);
ctor public ConcurrentHashMap(int, float, int);
+ method public V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public boolean contains(java.lang.Object);
method public java.util.Enumeration<V> elements();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+ method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public void forEach(long, java.util.function.BiConsumer<? super K, ? super V>);
+ method public void forEach(long, java.util.function.BiFunction<? super K, ? super V, ? extends U>, java.util.function.Consumer<? super U>);
+ method public void forEachEntry(long, java.util.function.Consumer<? super java.util.Map.Entry<K, V>>);
+ method public void forEachEntry(long, java.util.function.Function<java.util.Map.Entry<K, V>, ? extends U>, java.util.function.Consumer<? super U>);
+ method public void forEachKey(long, java.util.function.Consumer<? super K>);
+ method public void forEachKey(long, java.util.function.Function<? super K, ? extends U>, java.util.function.Consumer<? super U>);
+ method public void forEachValue(long, java.util.function.Consumer<? super V>);
+ method public void forEachValue(long, java.util.function.Function<? super V, ? extends U>, java.util.function.Consumer<? super U>);
+ method public V getOrDefault(java.lang.Object, V);
+ method public java.util.concurrent.ConcurrentHashMap.KeySetView<K, V> keySet(V);
method public java.util.Enumeration<K> keys();
+ method public long mappingCount();
+ method public V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
+ method public static java.util.concurrent.ConcurrentHashMap.KeySetView<K, java.lang.Boolean> newKeySet();
+ method public static java.util.concurrent.ConcurrentHashMap.KeySetView<K, java.lang.Boolean> newKeySet(int);
method public V putIfAbsent(K, V);
+ method public U reduce(long, java.util.function.BiFunction<? super K, ? super V, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public java.util.Map.Entry<K, V> reduceEntries(long, java.util.function.BiFunction<java.util.Map.Entry<K, V>, java.util.Map.Entry<K, V>, ? extends java.util.Map.Entry<K, V>>);
+ method public U reduceEntries(long, java.util.function.Function<java.util.Map.Entry<K, V>, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public double reduceEntriesToDouble(long, java.util.function.ToDoubleFunction<java.util.Map.Entry<K, V>>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceEntriesToInt(long, java.util.function.ToIntFunction<java.util.Map.Entry<K, V>>, int, java.util.function.IntBinaryOperator);
+ method public long reduceEntriesToLong(long, java.util.function.ToLongFunction<java.util.Map.Entry<K, V>>, long, java.util.function.LongBinaryOperator);
+ method public K reduceKeys(long, java.util.function.BiFunction<? super K, ? super K, ? extends K>);
+ method public U reduceKeys(long, java.util.function.Function<? super K, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public double reduceKeysToDouble(long, java.util.function.ToDoubleFunction<? super K>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceKeysToInt(long, java.util.function.ToIntFunction<? super K>, int, java.util.function.IntBinaryOperator);
+ method public long reduceKeysToLong(long, java.util.function.ToLongFunction<? super K>, long, java.util.function.LongBinaryOperator);
+ method public double reduceToDouble(long, java.util.function.ToDoubleBiFunction<? super K, ? super V>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceToInt(long, java.util.function.ToIntBiFunction<? super K, ? super V>, int, java.util.function.IntBinaryOperator);
+ method public long reduceToLong(long, java.util.function.ToLongBiFunction<? super K, ? super V>, long, java.util.function.LongBinaryOperator);
+ method public V reduceValues(long, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
+ method public U reduceValues(long, java.util.function.Function<? super V, ? extends U>, java.util.function.BiFunction<? super U, ? super U, ? extends U>);
+ method public double reduceValuesToDouble(long, java.util.function.ToDoubleFunction<? super V>, double, java.util.function.DoubleBinaryOperator);
+ method public int reduceValuesToInt(long, java.util.function.ToIntFunction<? super V>, int, java.util.function.IntBinaryOperator);
+ method public long reduceValuesToLong(long, java.util.function.ToLongFunction<? super V>, long, java.util.function.LongBinaryOperator);
method public boolean remove(java.lang.Object, java.lang.Object);
method public boolean replace(K, V, V);
method public V replace(K, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public U search(long, java.util.function.BiFunction<? super K, ? super V, ? extends U>);
+ method public U searchEntries(long, java.util.function.Function<java.util.Map.Entry<K, V>, ? extends U>);
+ method public U searchKeys(long, java.util.function.Function<? super K, ? extends U>);
+ method public U searchValues(long, java.util.function.Function<? super V, ? extends U>);
+ }
+
+ static abstract class ConcurrentHashMap.CollectionView implements java.util.Collection java.io.Serializable {
+ method public final void clear();
+ method public abstract boolean contains(java.lang.Object);
+ method public final boolean containsAll(java.util.Collection<?>);
+ method public java.util.concurrent.ConcurrentHashMap<K, V> getMap();
+ method public final boolean isEmpty();
+ method public abstract java.util.Iterator<E> iterator();
+ method public abstract boolean remove(java.lang.Object);
+ method public final boolean removeAll(java.util.Collection<?>);
+ method public final boolean retainAll(java.util.Collection<?>);
+ method public final int size();
+ method public final java.lang.Object[] toArray();
+ method public final T[] toArray(T[]);
+ method public final java.lang.String toString();
+ }
+
+ public static class ConcurrentHashMap.KeySetView extends java.util.concurrent.ConcurrentHashMap.CollectionView implements java.io.Serializable java.util.Set {
+ method public boolean add(K);
+ method public boolean addAll(java.util.Collection<? extends K>);
+ method public boolean contains(java.lang.Object);
+ method public void forEach(java.util.function.Consumer<? super K>);
+ method public V getMappedValue();
+ method public java.util.Iterator<K> iterator();
+ method public boolean remove(java.lang.Object);
+ method public java.util.Spliterator<K> spliterator();
}
public class ConcurrentLinkedDeque extends java.util.AbstractCollection implements java.util.Deque java.io.Serializable {
@@ -58688,6 +59476,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class ConcurrentLinkedQueue extends java.util.AbstractQueue implements java.util.Queue java.io.Serializable {
@@ -58698,6 +59487,7 @@
method public E peek();
method public E poll();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public abstract interface ConcurrentMap implements java.util.Map {
@@ -58729,6 +59519,9 @@
method public K ceilingKey(K);
method public java.util.concurrent.ConcurrentSkipListMap<K, V> clone();
method public java.util.Comparator<? super K> comparator();
+ method public V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.NavigableSet<K> descendingKeySet();
method public java.util.concurrent.ConcurrentNavigableMap<K, V> descendingMap();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
@@ -58736,6 +59529,8 @@
method public K firstKey();
method public java.util.Map.Entry<K, V> floorEntry(K);
method public K floorKey(K);
+ method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public V getOrDefault(java.lang.Object, V);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> headMap(K, boolean);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> headMap(K);
method public java.util.Map.Entry<K, V> higherEntry(K);
@@ -58744,6 +59539,7 @@
method public K lastKey();
method public java.util.Map.Entry<K, V> lowerEntry(K);
method public K lowerKey(K);
+ method public V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public java.util.NavigableSet<K> navigableKeySet();
method public java.util.Map.Entry<K, V> pollFirstEntry();
method public java.util.Map.Entry<K, V> pollLastEntry();
@@ -58751,6 +59547,7 @@
method public boolean remove(java.lang.Object, java.lang.Object);
method public boolean replace(K, V, V);
method public V replace(K, V);
+ method public void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> subMap(K, boolean, K, boolean);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> subMap(K, K);
method public java.util.concurrent.ConcurrentNavigableMap<K, V> tailMap(K, boolean);
@@ -58778,6 +59575,7 @@
method public E pollFirst();
method public E pollLast();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public java.util.NavigableSet<E> subSet(E, boolean, E, boolean);
method public java.util.NavigableSet<E> subSet(E, E);
method public java.util.NavigableSet<E> tailSet(E, boolean);
@@ -58788,31 +59586,34 @@
ctor public CopyOnWriteArrayList();
ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
ctor public CopyOnWriteArrayList(E[]);
- method public synchronized boolean add(E);
- method public synchronized void add(int, E);
- method public synchronized boolean addAll(java.util.Collection<? extends E>);
- method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
- method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
- method public synchronized boolean addIfAbsent(E);
- method public synchronized void clear();
+ method public boolean add(E);
+ method public void add(int, E);
+ method public boolean addAll(java.util.Collection<? extends E>);
+ method public boolean addAll(int, java.util.Collection<? extends E>);
+ method public int addAllAbsent(java.util.Collection<? extends E>);
+ method public boolean addIfAbsent(E);
+ method public void clear();
method public java.lang.Object clone();
method public boolean contains(java.lang.Object);
method public boolean containsAll(java.util.Collection<?>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
- method public int indexOf(E, int);
method public int indexOf(java.lang.Object);
+ method public int indexOf(E, int);
method public boolean isEmpty();
method public java.util.Iterator<E> iterator();
- method public int lastIndexOf(E, int);
method public int lastIndexOf(java.lang.Object);
- method public java.util.ListIterator<E> listIterator(int);
+ method public int lastIndexOf(E, int);
method public java.util.ListIterator<E> listIterator();
- method public synchronized E remove(int);
- method public synchronized boolean remove(java.lang.Object);
- method public synchronized boolean removeAll(java.util.Collection<?>);
- method public synchronized boolean retainAll(java.util.Collection<?>);
- method public synchronized E set(int, E);
+ method public java.util.ListIterator<E> listIterator(int);
+ method public E remove(int);
+ method public boolean remove(java.lang.Object);
+ method public boolean removeAll(java.util.Collection<?>);
+ method public void replaceAll(java.util.function.UnaryOperator<E>);
+ method public boolean retainAll(java.util.Collection<?>);
+ method public E set(int, E);
method public int size();
+ method public void sort(java.util.Comparator<? super E>);
method public java.util.List<E> subList(int, int);
method public java.lang.Object[] toArray();
method public T[] toArray(T[]);
@@ -58821,8 +59622,11 @@
public class CopyOnWriteArraySet extends java.util.AbstractSet implements java.io.Serializable {
ctor public CopyOnWriteArraySet();
ctor public CopyOnWriteArraySet(java.util.Collection<? extends E>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public java.util.Iterator<E> iterator();
+ method public boolean removeIf(java.util.function.Predicate<? super E>);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
}
public class CountDownLatch {
@@ -58833,6 +59637,32 @@
method public long getCount();
}
+ public abstract class CountedCompleter extends java.util.concurrent.ForkJoinTask {
+ ctor protected CountedCompleter(java.util.concurrent.CountedCompleter<?>, int);
+ ctor protected CountedCompleter(java.util.concurrent.CountedCompleter<?>);
+ ctor protected CountedCompleter();
+ method public final void addToPendingCount(int);
+ method public final boolean compareAndSetPendingCount(int, int);
+ method public void complete(T);
+ method public abstract void compute();
+ method public final int decrementPendingCountUnlessZero();
+ method protected final boolean exec();
+ method public final java.util.concurrent.CountedCompleter<?> firstComplete();
+ method public final java.util.concurrent.CountedCompleter<?> getCompleter();
+ method public final int getPendingCount();
+ method public T getRawResult();
+ method public final java.util.concurrent.CountedCompleter<?> getRoot();
+ method public final void helpComplete(int);
+ method public final java.util.concurrent.CountedCompleter<?> nextComplete();
+ method public void onCompletion(java.util.concurrent.CountedCompleter<?>);
+ method public boolean onExceptionalCompletion(java.lang.Throwable, java.util.concurrent.CountedCompleter<?>);
+ method public final void propagateCompletion();
+ method public final void quietlyCompleteRoot();
+ method public final void setPendingCount(int);
+ method protected void setRawResult(T);
+ method public final void tryComplete();
+ }
+
public class CyclicBarrier {
ctor public CyclicBarrier(int, java.lang.Runnable);
ctor public CyclicBarrier(int);
@@ -58923,6 +59753,8 @@
method public static java.util.concurrent.ExecutorService newSingleThreadExecutor(java.util.concurrent.ThreadFactory);
method public static java.util.concurrent.ScheduledExecutorService newSingleThreadScheduledExecutor();
method public static java.util.concurrent.ScheduledExecutorService newSingleThreadScheduledExecutor(java.util.concurrent.ThreadFactory);
+ method public static java.util.concurrent.ExecutorService newWorkStealingPool(int);
+ method public static java.util.concurrent.ExecutorService newWorkStealingPool();
method public static java.util.concurrent.Callable<T> privilegedCallable(java.util.concurrent.Callable<T>);
method public static java.util.concurrent.Callable<T> privilegedCallableUsingCurrentClassLoader(java.util.concurrent.Callable<T>);
method public static java.util.concurrent.ThreadFactory privilegedThreadFactory();
@@ -58936,11 +59768,13 @@
ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean);
method public boolean awaitQuiescence(long, java.util.concurrent.TimeUnit);
method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public static java.util.concurrent.ForkJoinPool commonPool();
method protected int drainTasksTo(java.util.Collection<? super java.util.concurrent.ForkJoinTask<?>>);
method public void execute(java.util.concurrent.ForkJoinTask<?>);
method public void execute(java.lang.Runnable);
method public int getActiveThreadCount();
method public boolean getAsyncMode();
+ method public static int getCommonPoolParallelism();
method public java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory getFactory();
method public int getParallelism();
method public int getPoolSize();
@@ -58978,6 +59812,7 @@
method public static java.util.concurrent.ForkJoinTask<T> adapt(java.lang.Runnable, T);
method public static java.util.concurrent.ForkJoinTask<T> adapt(java.util.concurrent.Callable<? extends T>);
method public boolean cancel(boolean);
+ method public final boolean compareAndSetForkJoinTaskTag(short, short);
method public void complete(V);
method public void completeExceptionally(java.lang.Throwable);
method protected abstract boolean exec();
@@ -58985,6 +59820,7 @@
method public final V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
method public final V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
method public final java.lang.Throwable getException();
+ method public final short getForkJoinTaskTag();
method public static java.util.concurrent.ForkJoinPool getPool();
method public static int getQueuedTaskCount();
method public abstract V getRawResult();
@@ -59003,9 +59839,11 @@
method protected static java.util.concurrent.ForkJoinTask<?> peekNextLocalTask();
method protected static java.util.concurrent.ForkJoinTask<?> pollNextLocalTask();
method protected static java.util.concurrent.ForkJoinTask<?> pollTask();
+ method public final void quietlyComplete();
method public final void quietlyInvoke();
method public final void quietlyJoin();
method public void reinitialize();
+ method public final short setForkJoinTaskTag(short);
method protected abstract void setRawResult(V);
method public boolean tryUnfork();
}
@@ -59079,6 +59917,7 @@
method public E removeLast();
method public boolean removeLastOccurrence(java.lang.Object);
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
method public E takeFirst() throws java.lang.InterruptedException;
method public E takeLast() throws java.lang.InterruptedException;
@@ -59099,6 +59938,7 @@
method public void put(E) throws java.lang.InterruptedException;
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -59118,6 +59958,7 @@
method public void put(E);
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
method public void transfer(E) throws java.lang.InterruptedException;
method public boolean tryTransfer(E);
@@ -59165,6 +60006,7 @@
method public void put(E);
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -59268,6 +60110,7 @@
method public void put(E) throws java.lang.InterruptedException;
method public int remainingCapacity();
method public int size();
+ method public java.util.Spliterator<E> spliterator();
method public E take() throws java.lang.InterruptedException;
}
@@ -59397,112 +60240,136 @@
public class AtomicInteger extends java.lang.Number implements java.io.Serializable {
ctor public AtomicInteger(int);
ctor public AtomicInteger();
+ method public final int accumulateAndGet(int, java.util.function.IntBinaryOperator);
method public final int addAndGet(int);
method public final boolean compareAndSet(int, int);
method public final int decrementAndGet();
method public double doubleValue();
method public float floatValue();
method public final int get();
+ method public final int getAndAccumulate(int, java.util.function.IntBinaryOperator);
method public final int getAndAdd(int);
method public final int getAndDecrement();
method public final int getAndIncrement();
method public final int getAndSet(int);
+ method public final int getAndUpdate(java.util.function.IntUnaryOperator);
method public final int incrementAndGet();
method public int intValue();
method public final void lazySet(int);
method public long longValue();
method public final void set(int);
+ method public final int updateAndGet(java.util.function.IntUnaryOperator);
method public final boolean weakCompareAndSet(int, int);
}
public class AtomicIntegerArray implements java.io.Serializable {
ctor public AtomicIntegerArray(int);
ctor public AtomicIntegerArray(int[]);
+ method public final int accumulateAndGet(int, int, java.util.function.IntBinaryOperator);
method public final int addAndGet(int, int);
method public final boolean compareAndSet(int, int, int);
method public final int decrementAndGet(int);
method public final int get(int);
+ method public final int getAndAccumulate(int, int, java.util.function.IntBinaryOperator);
method public final int getAndAdd(int, int);
method public final int getAndDecrement(int);
method public final int getAndIncrement(int);
method public final int getAndSet(int, int);
+ method public final int getAndUpdate(int, java.util.function.IntUnaryOperator);
method public final int incrementAndGet(int);
method public final void lazySet(int, int);
method public final int length();
method public final void set(int, int);
+ method public final int updateAndGet(int, java.util.function.IntUnaryOperator);
method public final boolean weakCompareAndSet(int, int, int);
}
public abstract class AtomicIntegerFieldUpdater {
ctor protected AtomicIntegerFieldUpdater();
+ method public final int accumulateAndGet(T, int, java.util.function.IntBinaryOperator);
method public int addAndGet(T, int);
method public abstract boolean compareAndSet(T, int, int);
method public int decrementAndGet(T);
method public abstract int get(T);
+ method public final int getAndAccumulate(T, int, java.util.function.IntBinaryOperator);
method public int getAndAdd(T, int);
method public int getAndDecrement(T);
method public int getAndIncrement(T);
method public int getAndSet(T, int);
+ method public final int getAndUpdate(T, java.util.function.IntUnaryOperator);
method public int incrementAndGet(T);
method public abstract void lazySet(T, int);
method public static java.util.concurrent.atomic.AtomicIntegerFieldUpdater<U> newUpdater(java.lang.Class<U>, java.lang.String);
method public abstract void set(T, int);
+ method public final int updateAndGet(T, java.util.function.IntUnaryOperator);
method public abstract boolean weakCompareAndSet(T, int, int);
}
public class AtomicLong extends java.lang.Number implements java.io.Serializable {
ctor public AtomicLong(long);
ctor public AtomicLong();
+ method public final long accumulateAndGet(long, java.util.function.LongBinaryOperator);
method public final long addAndGet(long);
method public final boolean compareAndSet(long, long);
method public final long decrementAndGet();
method public double doubleValue();
method public float floatValue();
method public final long get();
+ method public final long getAndAccumulate(long, java.util.function.LongBinaryOperator);
method public final long getAndAdd(long);
method public final long getAndDecrement();
method public final long getAndIncrement();
method public final long getAndSet(long);
+ method public final long getAndUpdate(java.util.function.LongUnaryOperator);
method public final long incrementAndGet();
method public int intValue();
method public final void lazySet(long);
method public long longValue();
method public final void set(long);
+ method public final long updateAndGet(java.util.function.LongUnaryOperator);
method public final boolean weakCompareAndSet(long, long);
}
public class AtomicLongArray implements java.io.Serializable {
ctor public AtomicLongArray(int);
ctor public AtomicLongArray(long[]);
+ method public final long accumulateAndGet(int, long, java.util.function.LongBinaryOperator);
method public long addAndGet(int, long);
method public final boolean compareAndSet(int, long, long);
method public final long decrementAndGet(int);
method public final long get(int);
+ method public final long getAndAccumulate(int, long, java.util.function.LongBinaryOperator);
method public final long getAndAdd(int, long);
method public final long getAndDecrement(int);
method public final long getAndIncrement(int);
method public final long getAndSet(int, long);
+ method public final long getAndUpdate(int, java.util.function.LongUnaryOperator);
method public final long incrementAndGet(int);
method public final void lazySet(int, long);
method public final int length();
method public final void set(int, long);
+ method public final long updateAndGet(int, java.util.function.LongUnaryOperator);
method public final boolean weakCompareAndSet(int, long, long);
}
public abstract class AtomicLongFieldUpdater {
ctor protected AtomicLongFieldUpdater();
+ method public final long accumulateAndGet(T, long, java.util.function.LongBinaryOperator);
method public long addAndGet(T, long);
method public abstract boolean compareAndSet(T, long, long);
method public long decrementAndGet(T);
method public abstract long get(T);
+ method public final long getAndAccumulate(T, long, java.util.function.LongBinaryOperator);
method public long getAndAdd(T, long);
method public long getAndDecrement(T);
method public long getAndIncrement(T);
method public long getAndSet(T, long);
+ method public final long getAndUpdate(T, java.util.function.LongUnaryOperator);
method public long incrementAndGet(T);
method public abstract void lazySet(T, long);
method public static java.util.concurrent.atomic.AtomicLongFieldUpdater<U> newUpdater(java.lang.Class<U>, java.lang.String);
method public abstract void set(T, long);
+ method public final long updateAndGet(T, java.util.function.LongUnaryOperator);
method public abstract boolean weakCompareAndSet(T, long, long);
}
@@ -59520,34 +60387,46 @@
public class AtomicReference implements java.io.Serializable {
ctor public AtomicReference(V);
ctor public AtomicReference();
+ method public final V accumulateAndGet(V, java.util.function.BinaryOperator<V>);
method public final boolean compareAndSet(V, V);
method public final V get();
+ method public final V getAndAccumulate(V, java.util.function.BinaryOperator<V>);
method public final V getAndSet(V);
+ method public final V getAndUpdate(java.util.function.UnaryOperator<V>);
method public final void lazySet(V);
method public final void set(V);
+ method public final V updateAndGet(java.util.function.UnaryOperator<V>);
method public final boolean weakCompareAndSet(V, V);
}
public class AtomicReferenceArray implements java.io.Serializable {
ctor public AtomicReferenceArray(int);
ctor public AtomicReferenceArray(E[]);
+ method public final E accumulateAndGet(int, E, java.util.function.BinaryOperator<E>);
method public final boolean compareAndSet(int, E, E);
method public final E get(int);
+ method public final E getAndAccumulate(int, E, java.util.function.BinaryOperator<E>);
method public final E getAndSet(int, E);
+ method public final E getAndUpdate(int, java.util.function.UnaryOperator<E>);
method public final void lazySet(int, E);
method public final int length();
method public final void set(int, E);
+ method public final E updateAndGet(int, java.util.function.UnaryOperator<E>);
method public final boolean weakCompareAndSet(int, E, E);
}
public abstract class AtomicReferenceFieldUpdater {
ctor protected AtomicReferenceFieldUpdater();
+ method public final V accumulateAndGet(T, V, java.util.function.BinaryOperator<V>);
method public abstract boolean compareAndSet(T, V, V);
method public abstract V get(T);
+ method public final V getAndAccumulate(T, V, java.util.function.BinaryOperator<V>);
method public V getAndSet(T, V);
+ method public final V getAndUpdate(T, java.util.function.UnaryOperator<V>);
method public abstract void lazySet(T, V);
method public static java.util.concurrent.atomic.AtomicReferenceFieldUpdater<U, W> newUpdater(java.lang.Class<U>, java.lang.Class<W>, java.lang.String);
method public abstract void set(T, V);
+ method public final V updateAndGet(T, java.util.function.UnaryOperator<V>);
method public abstract boolean weakCompareAndSet(T, V, V);
}
@@ -59562,6 +60441,59 @@
method public boolean weakCompareAndSet(V, V, int, int);
}
+ public class DoubleAccumulator extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public DoubleAccumulator(java.util.function.DoubleBinaryOperator, double);
+ method public void accumulate(double);
+ method public double doubleValue();
+ method public float floatValue();
+ method public double get();
+ method public double getThenReset();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ }
+
+ public class DoubleAdder extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public DoubleAdder();
+ method public void add(double);
+ method public double doubleValue();
+ method public float floatValue();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ method public double sum();
+ method public double sumThenReset();
+ }
+
+ public class LongAccumulator extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public LongAccumulator(java.util.function.LongBinaryOperator, long);
+ method public void accumulate(long);
+ method public double doubleValue();
+ method public float floatValue();
+ method public long get();
+ method public long getThenReset();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ }
+
+ public class LongAdder extends java.util.concurrent.atomic.Striped64 implements java.io.Serializable {
+ ctor public LongAdder();
+ method public void add(long);
+ method public void decrement();
+ method public double doubleValue();
+ method public float floatValue();
+ method public void increment();
+ method public int intValue();
+ method public long longValue();
+ method public void reset();
+ method public long sum();
+ method public long sumThenReset();
+ }
+
+ abstract class Striped64 extends java.lang.Number {
+ }
+
}
package java.util.concurrent.locks {
@@ -59769,6 +60701,34 @@
method public void unlock();
}
+ public class StampedLock implements java.io.Serializable {
+ ctor public StampedLock();
+ method public java.util.concurrent.locks.Lock asReadLock();
+ method public java.util.concurrent.locks.ReadWriteLock asReadWriteLock();
+ method public java.util.concurrent.locks.Lock asWriteLock();
+ method public int getReadLockCount();
+ method public boolean isReadLocked();
+ method public boolean isWriteLocked();
+ method public long readLock();
+ method public long readLockInterruptibly() throws java.lang.InterruptedException;
+ method public long tryConvertToOptimisticRead(long);
+ method public long tryConvertToReadLock(long);
+ method public long tryConvertToWriteLock(long);
+ method public long tryOptimisticRead();
+ method public long tryReadLock();
+ method public long tryReadLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public boolean tryUnlockRead();
+ method public boolean tryUnlockWrite();
+ method public long tryWriteLock();
+ method public long tryWriteLock(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+ method public void unlock(long);
+ method public void unlockRead(long);
+ method public void unlockWrite(long);
+ method public boolean validate(long);
+ method public long writeLock();
+ method public long writeLockInterruptibly() throws java.lang.InterruptedException;
+ }
+
}
package java.util.function {
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 115224c..2f55373 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -43,6 +43,14 @@
}
+package android.media.tv {
+
+ public class TvView extends android.view.ViewGroup {
+ method public void requestUnblockContent(android.media.tv.TvContentRating);
+ }
+
+}
+
package android.net {
public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
@@ -201,14 +209,6 @@
}
-package android.service.notification {
-
- public abstract class ConditionProviderService extends android.app.Service {
- method public void onRequestConditions(int);
- }
-
-}
-
package android.test.mock {
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index df0e5fc..e6c5768 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -577,7 +577,7 @@
return;
}
List<ResolveInfo> activities = pm.queryIntentActivities(intent, mimeType, 0,
- mUserId);
+ mUserId).getList();
if (activities == null || activities.size() <= 0) {
System.err.println("Error: Intent does not match any activities: "
+ intent);
@@ -606,7 +606,7 @@
new File(mProfileFile),
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_READ_WRITE);
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + mProfileFile);
System.err.println("Consider using a file under /data/local/tmp/");
@@ -903,7 +903,7 @@
fd = openForSystemServer(file,
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_READ_WRITE);
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + filename);
System.err.println("Consider using a file under /data/local/tmp/");
@@ -992,7 +992,7 @@
new File(profileFile),
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_READ_WRITE);
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + profileFile);
System.err.println("Consider using a file under /data/local/tmp/");
@@ -1052,7 +1052,7 @@
fd = openForSystemServer(file,
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_READ_WRITE);
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + heapFile);
System.err.println("Consider using a file under /data/local/tmp/");
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 8f361ce..ea53e59 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -68,8 +68,7 @@
// ---------------------------------------------------------------------------
-BootAnimation::BootAnimation() : Thread(false), mZip(NULL)
-{
+BootAnimation::BootAnimation() : Thread(false), mZip(NULL), mClockEnabled(true) {
mSession = new SurfaceComposerClient();
}
@@ -450,6 +449,69 @@
return true;
}
+// The time glyphs are stored in a single image of height 64 pixels. Each digit is 40 pixels wide,
+// and the colon character is half that at 20 pixels. The glyph order is '0123456789:'.
+// We render 24 hour time.
+void BootAnimation::drawTime(const Texture& clockTex, const int yPos) {
+ static constexpr char TIME_FORMAT[] = "%H:%M";
+ static constexpr int TIME_LENGTH = sizeof(TIME_FORMAT);
+
+ static constexpr int DIGIT_HEIGHT = 64;
+ static constexpr int DIGIT_WIDTH = 40;
+ static constexpr int COLON_WIDTH = DIGIT_WIDTH / 2;
+ static constexpr int TIME_WIDTH = (DIGIT_WIDTH * 4) + COLON_WIDTH;
+
+ if (clockTex.h < DIGIT_HEIGHT || clockTex.w < (10 * DIGIT_WIDTH + COLON_WIDTH)) {
+ ALOGE("Clock texture is too small; abandoning boot animation clock");
+ mClockEnabled = false;
+ return;
+ }
+
+ time_t rawtime;
+ time(&rawtime);
+ struct tm* timeInfo = localtime(&rawtime);
+
+ char timeBuff[TIME_LENGTH];
+ size_t length = strftime(timeBuff, TIME_LENGTH, TIME_FORMAT, timeInfo);
+
+ if (length != TIME_LENGTH - 1) {
+ ALOGE("Couldn't format time; abandoning boot animation clock");
+ mClockEnabled = false;
+ return;
+ }
+
+ glEnable(GL_BLEND); // Allow us to draw on top of the animation
+ glBindTexture(GL_TEXTURE_2D, clockTex.name);
+
+ int xPos = (mWidth - TIME_WIDTH) / 2;
+ int cropRect[4] = { 0, DIGIT_HEIGHT, DIGIT_WIDTH, -DIGIT_HEIGHT };
+
+ for (int i = 0; i < TIME_LENGTH - 1; i++) {
+ char c = timeBuff[i];
+ int width = DIGIT_WIDTH;
+ int pos = c - '0'; // Position in the character list
+ if (pos < 0 || pos > 10) {
+ continue;
+ }
+ if (c == ':') {
+ width = COLON_WIDTH;
+ }
+
+ // Crop the texture to only the pixels in the current glyph
+ int left = pos * DIGIT_WIDTH;
+ cropRect[0] = left;
+ cropRect[2] = width;
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
+
+ glDrawTexiOES(xPos, yPos, 0, width, DIGIT_HEIGHT);
+
+ xPos += width;
+ }
+
+ glDisable(GL_BLEND); // Return to the animation's default behaviour
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
bool BootAnimation::movie()
{
String8 desString;
@@ -477,7 +539,12 @@
if (endl == NULL) break;
String8 line(s, endl - s);
const char* l = line.string();
- int fps, width, height, count, pause;
+ int fps = 0;
+ int width = 0;
+ int height = 0;
+ int count = 0;
+ int pause = 0;
+ int clockPosY = -1;
char path[ANIM_ENTRY_NAME_MAX];
char color[7] = "000000"; // default to black if unspecified
@@ -487,14 +554,15 @@
animation.width = width;
animation.height = height;
animation.fps = fps;
- }
- else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {
- // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color);
+ } else if (sscanf(l, " %c %d %d %s #%6s %d",
+ &pathType, &count, &pause, path, color, &clockPosY) >= 4) {
+ // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPosY=%d", pathType, count, pause, path, color, clockPosY);
Animation::Part part;
part.playUntilComplete = pathType == 'c';
part.count = count;
part.pause = pause;
part.path = path;
+ part.clockPosY = clockPosY;
part.audioFile = NULL;
if (!parseColor(color, part.backgroundColor)) {
ALOGE("> invalid color '#%s'", color);
@@ -556,6 +624,8 @@
mZip->endIteration(cookie);
+ // Blend required to draw time on top of animation frames.
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
@@ -569,6 +639,12 @@
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ bool clockTextureInitialized = false;
+ if (mClockEnabled) {
+ clockTextureInitialized = (initTexture(&mClock, mAssets, "images/clock64.png") == NO_ERROR);
+ mClockEnabled = clockTextureInitialized;
+ }
+
const int xc = (mWidth - animation.width) / 2;
const int yc = ((mHeight - animation.height) / 2);
nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -629,6 +705,10 @@
// which is equivalent to mHeight - (yc + animation.height)
glDrawTexiOES(xc, mHeight - (yc + animation.height),
0, animation.width, animation.height);
+ if (mClockEnabled && part.clockPosY >= 0) {
+ drawTime(mClock, part.clockPosY);
+ }
+
eglSwapBuffers(mDisplay, mSurface);
nsecs_t now = systemTime();
@@ -665,6 +745,10 @@
}
}
+ if (clockTextureInitialized) {
+ glDeleteTextures(1, &mClock.name);
+ }
+
return false;
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index f968b25..83e2b38 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -67,8 +67,10 @@
}
};
struct Part {
- int count;
- int pause;
+ int count; // The number of times this part should repeat, 0 for infinite
+ int pause; // The number of frames to pause for at the end of this part
+ 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;
SortedVector<Frame> frames;
bool playUntilComplete;
@@ -86,6 +88,7 @@
bool android();
bool readFile(const char* name, String8& outString);
bool movie();
+ void drawTime(const Texture& clockTex, const int yPos);
void checkExit();
@@ -93,6 +96,7 @@
sp<AudioPlayer> mAudioPlayer;
AssetManager mAssets;
Texture mAndroid[2];
+ Texture mClock;
int mWidth;
int mHeight;
EGLDisplay mDisplay;
@@ -101,6 +105,7 @@
sp<SurfaceControl> mFlingerSurfaceControl;
sp<Surface> mFlingerSurface;
ZipFileRO *mZip;
+ bool mClockEnabled;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index c469ae4..7bf073b 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -67,30 +67,6 @@
}
}
-static status_t vinfoToPixelFormat(const fb_var_screeninfo& vinfo,
- uint32_t* bytespp, uint32_t* f)
-{
-
- switch (vinfo.bits_per_pixel) {
- case 16:
- *f = PIXEL_FORMAT_RGB_565;
- *bytespp = 2;
- break;
- case 24:
- *f = PIXEL_FORMAT_RGB_888;
- *bytespp = 3;
- break;
- case 32:
- // TODO: do better decoding of vinfo here
- *f = PIXEL_FORMAT_RGBX_8888;
- *bytespp = 4;
- break;
- default:
- return BAD_VALUE;
- }
- return NO_ERROR;
-}
-
static status_t notifyMediaScanner(const char* fileName) {
String8 cmd("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://");
String8 fileUrl("\"");
@@ -147,7 +123,7 @@
png = true;
}
}
-
+
if (fd == -1) {
usage(pname);
return 1;
@@ -195,28 +171,6 @@
s = screenshot.getStride();
f = screenshot.getFormat();
size = screenshot.getSize();
- } else {
- const char* fbpath = "/dev/graphics/fb0";
- int fb = open(fbpath, O_RDONLY);
- if (fb >= 0) {
- struct fb_var_screeninfo vinfo;
- if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) {
- uint32_t bytespp;
- if (vinfoToPixelFormat(vinfo, &bytespp, &f) == NO_ERROR) {
- size_t offset = (vinfo.xoffset + vinfo.yoffset*vinfo.xres) * bytespp;
- w = vinfo.xres;
- h = vinfo.yres;
- s = vinfo.xres;
- size = w*h*bytespp;
- mapsize = offset + size;
- mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0);
- if (mapbase != MAP_FAILED) {
- base = (void const *)((char const *)mapbase + offset);
- }
- }
- }
- close(fb);
- }
}
if (base != NULL) {
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 844063c..8e31d32 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -16,6 +16,7 @@
package android.animation;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ConstantState;
import java.util.ArrayList;
@@ -50,7 +51,7 @@
* A set of flags which identify the type of configuration changes that can affect this
* Animator. Used by the Animator cache.
*/
- int mChangingConfigurations = 0;
+ @Config int mChangingConfigurations = 0;
/**
* If this animator is inflated from a constant state, keep a reference to it so that
@@ -344,7 +345,7 @@
* @see android.content.pm.ActivityInfo
* @hide
*/
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations;
}
@@ -358,7 +359,7 @@
* @see android.content.pm.ActivityInfo
* @hide
*/
- public void setChangingConfigurations(int configs) {
+ public void setChangingConfigurations(@Config int configs) {
mChangingConfigurations = configs;
}
@@ -368,7 +369,7 @@
* This method is called while loading the animator.
* @hide
*/
- public void appendChangingConfigurations(int configs) {
+ public void appendChangingConfigurations(@Config int configs) {
mChangingConfigurations |= configs;
}
@@ -564,7 +565,7 @@
private static class AnimatorConstantState extends ConstantState<Animator> {
final Animator mAnimator;
- int mChangingConf;
+ @Config int mChangingConf;
public AnimatorConstantState(Animator animator) {
mAnimator = animator;
@@ -574,7 +575,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConf;
}
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 20d71a6..7d5931f 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -16,7 +16,10 @@
package android.animation;
import android.annotation.AnimatorRes;
+import android.annotation.AnyRes;
+import android.annotation.NonNull;
import android.content.Context;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ConfigurationBoundResourceCache;
import android.content.res.ConstantState;
import android.content.res.Resources;
@@ -108,7 +111,7 @@
float pathErrorScale) throws NotFoundException {
final ConfigurationBoundResourceCache<Animator> animatorCache = resources
.getAnimatorCache();
- Animator animator = animatorCache.getInstance(id, theme);
+ Animator animator = animatorCache.getInstance(id, resources, theme);
if (animator != null) {
if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
@@ -157,7 +160,7 @@
final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
.getStateListAnimatorCache();
final Theme theme = context.getTheme();
- StateListAnimator animator = cache.getInstance(id, theme);
+ StateListAnimator animator = cache.getInstance(id, resources, theme);
if (animator != null) {
return animator;
}
@@ -1062,7 +1065,7 @@
return anim;
}
- private static int getChangingConfigs(Resources resources, int id) {
+ private static @Config int getChangingConfigs(@NonNull Resources resources, @AnyRes int id) {
synchronized (sTmpTypedValue) {
resources.getValue(id, sTmpTypedValue, true);
return sTmpTypedValue.changingConfigurations;
diff --git a/core/java/android/animation/StateListAnimator.java b/core/java/android/animation/StateListAnimator.java
index d49e914..b6d6910 100644
--- a/core/java/android/animation/StateListAnimator.java
+++ b/core/java/android/animation/StateListAnimator.java
@@ -16,6 +16,7 @@
package android.animation;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ConstantState;
import android.util.StateSet;
import android.view.View;
@@ -53,7 +54,7 @@
private WeakReference<View> mViewRef;
private StateListAnimatorConstantState mConstantState;
private AnimatorListenerAdapter mAnimatorListener;
- private int mChangingConfigurations;
+ private @Config int mChangingConfigurations;
public StateListAnimator() {
initAnimatorListener();
@@ -223,7 +224,7 @@
* @see android.content.pm.ActivityInfo
* @hide
*/
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations;
}
@@ -237,7 +238,7 @@
* @see android.content.pm.ActivityInfo
* @hide
*/
- public void setChangingConfigurations(int configs) {
+ public void setChangingConfigurations(@Config int configs) {
mChangingConfigurations = configs;
}
@@ -247,7 +248,7 @@
* This method is called while loading the animator.
* @hide
*/
- public void appendChangingConfigurations(int configs) {
+ public void appendChangingConfigurations(@Config int configs) {
mChangingConfigurations |= configs;
}
@@ -309,7 +310,7 @@
final StateListAnimator mAnimator;
- int mChangingConf;
+ @Config int mChangingConf;
public StateListAnimatorConstantState(StateListAnimator animator) {
mAnimator = animator;
@@ -318,7 +319,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConf;
}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 5ab2c1d..663f297 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -972,7 +972,14 @@
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
startAnimation();
- setCurrentFraction(mSeekFraction == -1 ? 0 : mSeekFraction);
+ if (mSeekFraction == -1) {
+ // No seek, start at play time 0. Note that the reason we are not using fraction 0
+ // is because for animations with 0 duration, we want to be consistent with pre-N
+ // behavior: skip to the final value immediately.
+ setCurrentPlayTime(0);
+ } else {
+ setCurrentFraction(mSeekFraction);
+ }
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b87e9fa2..a0a599e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1976,15 +1976,13 @@
* normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
* function returns null.
*
- * @return Returns the object previously returned by
- * {@link #onRetainNonConfigurationInstance()}.
- *
- * @deprecated Use the new {@link Fragment} API
+ * <p><strong>Note:</strong> For most cases you should use the {@link Fragment} API
* {@link Fragment#setRetainInstance(boolean)} instead; this is also
- * available on older platforms through the Android compatibility package.
+ * available on older platforms through the Android support libraries.
+ *
+ * @return the object previously returned by {@link #onRetainNonConfigurationInstance()}
*/
@Nullable
- @Deprecated
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
@@ -2035,12 +2033,12 @@
* guarantee for {@link android.os.AsyncTask#doInBackground} since that is
* running in a separate thread.)
*
- * @return Return any Object holding the desired state to propagate to the
- * next activity instance.
- *
- * @deprecated Use the new {@link Fragment} API
+ * <p><strong>Note:</strong> For most cases you should use the {@link Fragment} API
* {@link Fragment#setRetainInstance(boolean)} instead; this is also
- * available on older platforms through the Android compatibility package.
+ * available on older platforms through the Android support libraries.
+ *
+ * @return any Object holding the desired state to propagate to the
+ * next activity instance
*/
public Object onRetainNonConfigurationInstance() {
return null;
@@ -6177,14 +6175,24 @@
/**
* Enable or disable virtual reality (VR) mode.
*
- * <p>VR mode is a hint to Android system services to switch to modes optimized for
- * high-performance stereoscopic rendering.</p>
+ * <p>VR mode is a hint to Android system services to switch to a mode optimized for
+ * high-performance stereoscopic rendering. This mode will be enabled while this Activity has
+ * focus.</p>
*
* @param enabled {@code true} to enable this mode.
+ * @param requestedComponent the name of the component to use as a
+ * {@link android.service.vr.VrListenerService} while VR mode is enabled.
+ *
+ * @throws android.content.pm.PackageManager.NameNotFoundException;
*/
- public void setVrMode(boolean enabled) {
+ public void setVrModeEnabled(boolean enabled, @NonNull ComponentName requestedComponent)
+ throws PackageManager.NameNotFoundException {
try {
- ActivityManagerNative.getDefault().setVrMode(mToken, enabled);
+ if (ActivityManagerNative.getDefault().setVrMode(mToken, enabled, requestedComponent)
+ != 0) {
+ throw new PackageManager.NameNotFoundException(
+ requestedComponent.flattenToString());
+ }
} catch (RemoteException e) {
// pass
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a4e5b90..2d33a2c 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1575,6 +1575,7 @@
Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
dest.writeInt(numActivities);
dest.writeInt(numRunning);
+ dest.writeInt(isDockable ? 1 : 0);
}
public void readFromParcel(Parcel source) {
@@ -1590,6 +1591,7 @@
description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
numActivities = source.readInt();
numRunning = source.readInt();
+ isDockable = source.readInt() != 0;
}
public static final Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
@@ -3327,6 +3329,23 @@
}
}
+ /**
+ * Logs out current current foreground user by switching to the system user and stopping the
+ * user being switched from.
+ * @hide
+ */
+ public static void logoutCurrentUser() {
+ int currentUser = ActivityManager.getCurrentUser();
+ if (currentUser != UserHandle.USER_SYSTEM) {
+ try {
+ ActivityManagerNative.getDefault().switchUser(UserHandle.USER_SYSTEM);
+ ActivityManagerNative.getDefault().stopUser(currentUser, /* force= */ false, null);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
/** {@hide} */
public static final int FLAG_OR_STOPPED = 1 << 0;
/** {@hide} */
@@ -3371,6 +3390,15 @@
}
}
+ /** {@hide} */
+ public boolean isVrModePackageEnabled(ComponentName component) {
+ try {
+ return ActivityManagerNative.getDefault().isVrModePackageEnabled(component);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Perform a system dump of various state associated with the given application
* package name. This call blocks while the dump is being performed, so should
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ff7f70d..811a05b 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -838,6 +838,12 @@
resizePinnedStack(bounds, tempPinnedTaskBounds);
return true;
}
+ case SWAP_DOCKED_AND_FULLSCREEN_STACK: {
+ data.enforceInterface(IActivityManager.descriptor);
+ swapDockedAndFullscreenStack();
+ reply.writeNoException();
+ return true;
+ }
case RESIZE_DOCKED_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final boolean hasBounds = data.readInt() != 0;
@@ -2301,7 +2307,7 @@
case KEYGUARD_GOING_AWAY_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
- keyguardGoingAway(data.readInt() != 0, data.readInt() != 0);
+ keyguardGoingAway(data.readInt());
reply.writeNoException();
return true;
}
@@ -2902,8 +2908,18 @@
data.enforceInterface(IActivityManager.descriptor);
final IBinder token = data.readStrongBinder();
final boolean enable = data.readInt() == 1;
- setVrMode(token, enable);
+ final ComponentName packageName = ComponentName.CREATOR.createFromParcel(data);
+ int res = setVrMode(token, enable, packageName);
reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+ case IS_VR_PACKAGE_ENABLED_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final ComponentName packageName = ComponentName.CREATOR.createFromParcel(data);
+ boolean res = isVrModePackageEnabled(packageName);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
return true;
}
case IS_APP_FOREGROUND_TRANSACTION: {
@@ -3885,6 +3901,17 @@
reply.recycle();
}
@Override
+ public void swapDockedAndFullscreenStack() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(SWAP_DOCKED_AND_FULLSCREEN_STACK, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ @Override
public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds)
@@ -5897,13 +5924,12 @@
reply.recycle();
}
- public void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade) throws RemoteException {
+ public void keyguardGoingAway(int flags)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeInt(disableWindowAnimations ? 1 : 0);
- data.writeInt(keyguardGoingToNotificationShade ? 1 : 0);
+ data.writeInt(flags);
mRemote.transact(KEYGUARD_GOING_AWAY_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -6247,16 +6273,34 @@
return res;
}
- public void setVrMode(IBinder token, boolean enabled) throws RemoteException {
+ public int setVrMode(IBinder token, boolean enabled, ComponentName packageName)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
data.writeInt(enabled ? 1 : 0);
+ packageName.writeToParcel(data, 0);
mRemote.transact(SET_VR_MODE_TRANSACTION, data, reply, 0);
reply.readException();
+ int res = reply.readInt();
data.recycle();
reply.recycle();
+ return res;
+ }
+
+ public boolean isVrModePackageEnabled(ComponentName packageName)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ packageName.writeToParcel(data, 0);
+ mRemote.transact(IS_VR_PACKAGE_ENABLED_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res == 1;
}
@Override
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1bc33b8..060ac5e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -113,6 +113,7 @@
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SamplingProfilerIntegration;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -193,7 +194,7 @@
private ContextImpl mSystemContext;
- static IPackageManager sPackageManager;
+ static volatile IPackageManager sPackageManager;
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
@@ -218,7 +219,7 @@
// set of instantiated backup agents, keyed by package name
final ArrayMap<String, BackupAgent> mBackupAgents = new ArrayMap<String, BackupAgent>();
/** Reference to singleton {@link ActivityThread} */
- private static ActivityThread sCurrentActivityThread;
+ private static volatile ActivityThread sCurrentActivityThread;
Instrumentation mInstrumentation;
String mInstrumentationPackageName = null;
String mInstrumentationAppDir = null;
@@ -296,7 +297,7 @@
final GcIdler mGcIdler = new GcIdler();
boolean mGcIdlerScheduled = false;
- static Handler sMainThreadHandler; // set once in main()
+ static volatile Handler sMainThreadHandler; // set once in main()
Bundle mCoreSettings = null;
@@ -1792,24 +1793,9 @@
* Resources if one has already been created.
*/
Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
- String[] libDirs, int displayId, Configuration overrideConfiguration,
- LoadedApk pkgInfo) {
- return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
- displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(),
- pkgInfo.getClassLoader());
- }
-
- /**
- * Creates a new top level resources for the given package. Will always create a new
- * Resources, regardless if one has already been created.
- */
- Resources getNewTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
- String[] libDirs, int displayId, Configuration overrideConfiguration,
- LoadedApk pkgInfo) {
- mResourcesManager.removeTopLevelResources(
- resDir, displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo());
- return getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
- displayId, overrideConfiguration, pkgInfo);
+ String[] libDirs, int displayId, LoadedApk pkgInfo) {
+ return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs,
+ displayId, null, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader());
}
final Handler getHandler() {
@@ -2623,7 +2609,7 @@
}
ContextImpl appContext = ContextImpl.createActivityContext(
- this, r.packageInfo, displayId, r.overrideConfig);
+ this, r.packageInfo, r.token, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
@@ -3480,14 +3466,9 @@
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
- r.tmpConfig.setTo(r.newConfig);
- if (r.overrideConfig != null) {
- r.tmpConfig.updateFrom(r.overrideConfig);
- }
+ performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
- + r.activityInfo.name + " with newConfig " + r.tmpConfig);
- performConfigurationChanged(r.activity, r.tmpConfig, REPORT_TO_ACTIVITY);
- freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
+ + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
r.newConfig = null;
}
if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
@@ -3721,10 +3702,9 @@
} catch (RemoteException ex) {
if (ex instanceof TransactionTooLargeException
&& activity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
- Log.e(TAG, "App tried sending too much data in instance state", ex);
+ Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
return;
}
-
throw ex.rethrowFromSystemServer();
}
}
@@ -3833,14 +3813,10 @@
}
}
if (r.newConfig != null) {
- r.tmpConfig.setTo(r.newConfig);
- if (r.overrideConfig != null) {
- r.tmpConfig.updateFrom(r.overrideConfig);
- }
+ performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Updating activity vis "
- + r.activityInfo.name + " with new config " + r.tmpConfig);
- performConfigurationChanged(r.activity, r.tmpConfig, REPORT_TO_ACTIVITY);
- freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
+ + r.activityInfo.name + " with new config "
+ + r.activity.mCurrentConfig);
r.newConfig = null;
}
} else {
@@ -4545,8 +4521,44 @@
return callbacks;
}
- private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config,
- boolean reportToActivity) {
+ /**
+ * Updates the configuration for an Activity. The ActivityClientRecord's
+ * {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for
+ * that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering
+ * the updated Configuration.
+ * @param r ActivityClientRecord representing the Activity.
+ * @param newBaseConfig The new configuration to use. This may be augmented with
+ * {@link ActivityClientRecord#overrideConfig}.
+ * @param reportToActivity true if the change should be reported to the Activity's callback.
+ */
+ private void performConfigurationChangedForActivity(ActivityClientRecord r,
+ Configuration newBaseConfig,
+ boolean reportToActivity) {
+ r.tmpConfig.setTo(newBaseConfig);
+ if (r.overrideConfig != null) {
+ r.tmpConfig.updateFrom(r.overrideConfig);
+ }
+ performConfigurationChanged(r.activity, r.token, r.tmpConfig, r.overrideConfig,
+ reportToActivity);
+ freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
+ }
+
+ /**
+ * Decides whether to update an Activity's configuration and whether to tell the
+ * Activity/Component about it.
+ * @param cb The component callback to notify of configuration change.
+ * @param activityToken The Activity binder token for which this configuration change happened.
+ * If the change is global, this is null.
+ * @param newConfig The new configuration.
+ * @param overrideConfig The override config that differentiates the Activity's configuration
+ * from the base global configuration.
+ * @param reportToActivity Notify the Activity of the change.
+ */
+ private void performConfigurationChanged(ComponentCallbacks2 cb,
+ IBinder activityToken,
+ Configuration newConfig,
+ Configuration overrideConfig,
+ boolean reportToActivity) {
// Only for Activity objects, check that they actually call up to their
// superclass implementation. ComponentCallbacks2 is an interface, so
// we check the runtime type and act accordingly.
@@ -4563,7 +4575,7 @@
// If the new config is the same as the config this Activity
// is already running with then don't bother calling
// onConfigurationChanged
- int diff = activity.mCurrentConfig.diff(config);
+ int diff = activity.mCurrentConfig.diff(newConfig);
if (diff != 0) {
// If this activity doesn't handle any of the config changes then don't bother
// calling onConfigurationChanged as we're going to destroy it.
@@ -4578,21 +4590,31 @@
}
}
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Config callback " + cb
- + ": shouldChangeConfig=" + shouldChangeConfig);
+ if (DEBUG_CONFIGURATION) {
+ Slog.v(TAG, "Config callback " + cb + ": shouldChangeConfig=" + shouldChangeConfig);
+ }
+
if (shouldChangeConfig) {
+ if (activityToken != null) {
+ // We only update an Activity's configuration if this is not a global
+ // configuration change. This must also be done before the callback,
+ // or else we violate the contract that the new resources are available
+ // in {@link ComponentCallbacks2#onConfigurationChanged(Configuration)}.
+ mResourcesManager.updateResourcesForActivity(activityToken, overrideConfig);
+ }
+
if (reportToActivity) {
- cb.onConfigurationChanged(config);
+ cb.onConfigurationChanged(newConfig);
}
if (activity != null) {
if (reportToActivity && !activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + activity.getLocalClassName() +
- " did not call through to super.onConfigurationChanged()");
+ " did not call through to super.onConfigurationChanged()");
}
activity.mConfigChangeFlags = 0;
- activity.mCurrentConfig = new Configuration(config);
+ activity.mCurrentConfig = new Configuration(newConfig);
}
}
}
@@ -4609,7 +4631,8 @@
mCompatConfiguration = new Configuration();
}
mCompatConfiguration.setTo(mConfiguration);
- if (mResourcesManager.applyCompatConfiguration(displayDensity, mCompatConfiguration)) {
+ if (mResourcesManager.applyCompatConfigurationLocked(displayDensity,
+ mCompatConfiguration)) {
config = mCompatConfiguration;
}
return config;
@@ -4661,7 +4684,8 @@
if (callbacks != null) {
final int N = callbacks.size();
for (int i=0; i<N; i++) {
- performConfigurationChanged(callbacks.get(i), config, REPORT_TO_ACTIVITY);
+ performConfigurationChanged(callbacks.get(i), null, config, null,
+ REPORT_TO_ACTIVITY);
}
}
}
@@ -4687,15 +4711,8 @@
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
+ r.activityInfo.name + ", with callback=" + reportToActivity);
- r.tmpConfig.setTo(mCompatConfiguration);
- if (data.overrideConfig != null) {
- r.overrideConfig = data.overrideConfig;
- r.tmpConfig.updateFrom(data.overrideConfig);
- }
- performConfigurationChanged(r.activity, r.tmpConfig, reportToActivity);
-
- freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(mCompatConfiguration));
-
+ r.overrideConfig = data.overrideConfig;
+ performConfigurationChangedForActivity(r, mCompatConfiguration, reportToActivity);
mSomeActivitiesChanged = true;
}
@@ -5032,21 +5049,25 @@
*/
TimeZone.setDefault(null);
- /*
- * Initialize the default locales in this process for the reasons we set the time zone.
- *
- * We do this through ResourcesManager, since we need to do locale negotiation.
- */
- mResourcesManager.setDefaultLocalesLocked(data.config.getLocales());
+ synchronized (mResourcesManager) {
+ /*
+ * Initialize the default locales in this process for the reasons we set the time zone.
+ *
+ * We do this through ResourcesManager, since we need to do locale negotiation.
+ */
+ mResourcesManager.setDefaultLocalesLocked(data.config.getLocales());
- /*
- * Update the system configuration since its preloaded and might not
- * reflect configuration changes. The configuration object passed
- * in AppBindData can be safely assumed to be up to date
- */
- mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
- mCurDefaultDisplayDpi = data.config.densityDpi;
- applyCompatConfiguration(mCurDefaultDisplayDpi);
+ /*
+ * Update the system configuration since its preloaded and might not
+ * reflect configuration changes. The configuration object passed
+ * in AppBindData can be safely assumed to be up to date
+ */
+ mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+ mCurDefaultDisplayDpi = data.config.densityDpi;
+
+ // This calls mResourcesManager so keep it within the synchronized block.
+ applyCompatConfiguration(mCurDefaultDisplayDpi);
+ }
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
@@ -5264,9 +5285,8 @@
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
- List<ProviderInfo> providers = data.providers;
- if (providers != null) {
- installContentProviders(app, providers);
+ if (!ArrayUtils.isEmpty(data.providers)) {
+ installContentProviders(app, data.providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 455f869..b08142ad 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -985,11 +985,16 @@
/**
* Gets information about the next alarm clock currently scheduled.
*
- * The alarm clocks considered are those scheduled by {@link #setAlarmClock}
- * from any package of the calling user.
+ * The alarm clocks considered are those scheduled by any application
+ * using the {@link #setAlarmClock} method.
+ *
+ * @return An {@link AlarmClockInfo} object describing the next upcoming alarm
+ * clock event that will occur. If there are no alarm clock events currently
+ * scheduled, this method will return {@code null}.
*
* @see #setAlarmClock
* @see AlarmClockInfo
+ * @see #ACTION_NEXT_ALARM_CLOCK_CHANGED
*/
public AlarmClockInfo getNextAlarmClock() {
return getNextAlarmClock(UserHandle.myUserId());
@@ -998,11 +1003,16 @@
/**
* Gets information about the next alarm clock currently scheduled.
*
- * The alarm clocks considered are those scheduled by {@link #setAlarmClock}
- * from any package of the given {@parm userId}.
+ * The alarm clocks considered are those scheduled by any application
+ * using the {@link #setAlarmClock} method within the given user.
+ *
+ * @return An {@link AlarmClockInfo} object describing the next upcoming alarm
+ * clock event that will occur within the given user. If there are no alarm clock
+ * events currently scheduled in that user, this method will return {@code null}.
*
* @see #setAlarmClock
* @see AlarmClockInfo
+ * @see #ACTION_NEXT_ALARM_CLOCK_CHANGED
*
* @hide
*/
@@ -1015,7 +1025,7 @@
}
/**
- * An immutable description of an alarm clock.
+ * An immutable description of a scheduled "alarm clock" event.
*
* @see AlarmManager#setAlarmClock
* @see AlarmManager#getNextAlarmClock
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 82c4c51..64586a6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1267,8 +1267,15 @@
/** @hide */
public void setUserRestriction(int code, boolean restricted, IBinder token) {
+ setUserRestriction(code, restricted, token, /*exceptionPackages*/null);
+ }
+
+ /** @hide */
+ public void setUserRestriction(int code, boolean restricted, IBinder token,
+ String[] exceptionPackages) {
try {
- mService.setUserRestriction(code, restricted, token, mContext.getUserId());
+ mService.setUserRestriction(code, restricted, token, mContext.getUserId(),
+ exceptionPackages);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index c841111..ca05091 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -275,7 +275,7 @@
public List<PermissionInfo> queryPermissionsByGroup(String group, int flags)
throws NameNotFoundException {
try {
- List<PermissionInfo> pi = mPM.queryPermissionsByGroup(group, flags);
+ List<PermissionInfo> pi = mPM.queryPermissionsByGroup(group, flags).getList();
if (pi != null) {
return pi;
}
@@ -304,7 +304,7 @@
@Override
public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
try {
- return mPM.getAllPermissionGroups(flags);
+ return mPM.getAllPermissionGroups(flags).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -441,7 +441,12 @@
@Override
public FeatureInfo[] getSystemAvailableFeatures() {
try {
- return mPM.getSystemAvailableFeatures();
+ final List<FeatureInfo> list = mPM.getSystemAvailableFeatures().getList();
+ final FeatureInfo[] res = new FeatureInfo[list.size()];
+ for (int i = 0; i < res.length; i++) {
+ res[i] = list.get(i);
+ }
+ return res;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -772,7 +777,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags,
- userId);
+ userId).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -802,9 +807,10 @@
}
try {
- return mPM.queryIntentActivityOptions(caller, specifics,
- specificTypes, intent, intent.resolveTypeIfNeeded(resolver),
- flags, mContext.getUserId());
+ return mPM
+ .queryIntentActivityOptions(caller, specifics, specificTypes, intent,
+ intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId())
+ .getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -820,7 +826,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags,
- userId);
+ userId).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -851,7 +857,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags,
- userId);
+ userId).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -867,7 +873,8 @@
Intent intent, int flags, int userId) {
try {
return mPM.queryIntentContentProviders(intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId);
+ intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId)
+ .getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -926,7 +933,7 @@
public List<InstrumentationInfo> queryInstrumentation(
String targetPackage, int flags) {
try {
- return mPM.queryInstrumentation(targetPackage, flags);
+ return mPM.queryInstrumentation(targetPackage, flags).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1175,7 +1182,7 @@
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
- null, mContext.mPackageInfo);
+ mContext.mPackageInfo);
if (r != null) {
return r;
}
@@ -1543,9 +1550,9 @@
}
@Override
- public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains) {
+ public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {
try {
- mPM.verifyIntentFilter(id, verificationCode, outFailedDomains);
+ mPM.verifyIntentFilter(id, verificationCode, failedDomains);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1572,7 +1579,7 @@
@Override
public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
try {
- return mPM.getIntentFilterVerifications(packageName);
+ return mPM.getIntentFilterVerifications(packageName).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1581,7 +1588,7 @@
@Override
public List<IntentFilter> getAllIntentFilters(String packageName) {
try {
- return mPM.getAllIntentFilters(packageName);
+ return mPM.getAllIntentFilters(packageName).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1892,31 +1899,21 @@
throw e.rethrowFromSystemServer();
}
}
+
@Override
public void addPackageToPreferred(String packageName) {
- try {
- mPM.addPackageToPreferred(packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ Log.w(TAG, "addPackageToPreferred() is a no-op");
}
@Override
public void removePackageFromPreferred(String packageName) {
- try {
- mPM.removePackageFromPreferred(packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ Log.w(TAG, "removePackageFromPreferred() is a no-op");
}
@Override
public List<PackageInfo> getPreferredPackages(int flags) {
- try {
- return mPM.getPreferredPackages(flags);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ Log.w(TAG, "getPreferredPackages() is a no-op");
+ return Collections.emptyList();
}
@Override
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 1f1f318..cd4ace6 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -42,7 +42,7 @@
* @param name The name of the rule.
* @param owner The Condition Provider service that owns this rule.
* @param conditionId A representation of the state that should cause the Condition Provider
- * service to apply the interruption filter.
+ * service to apply the given interruption filter.
* @param interruptionFilter The interruption filter defines which notifications are allowed to
* interrupt the user (e.g. via sound & vibration) while this rule
* is active.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e76f991..32ace14 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1994,9 +1994,10 @@
}
static ContextImpl createActivityContext(ActivityThread mainThread,
- LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) {
+ LoadedApk packageInfo, IBinder activityToken, int displayId,
+ Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
- return new ContextImpl(null, mainThread, packageInfo, null, null, 0,
+ return new ContextImpl(null, mainThread, packageInfo, activityToken, null, 0,
null, overrideConfiguration, displayId);
}
@@ -2054,10 +2055,16 @@
|| overrideConfiguration != null
|| (compatInfo != null && compatInfo.applicationScale
!= resources.getCompatibilityInfo().applicationScale)) {
- resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
- packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
- packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
- overrideConfiguration, compatInfo, packageInfo.getClassLoader());
+ resources = mResourcesManager.getResources(
+ activityToken,
+ packageInfo.getResDir(),
+ packageInfo.getSplitResDirs(),
+ packageInfo.getOverlayDirs(),
+ packageInfo.getApplicationInfo().sharedLibraryFiles,
+ displayId,
+ overrideConfiguration,
+ compatInfo,
+ packageInfo.getClassLoader());
}
}
mResources = resources;
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index bbf1607..83dc506 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -125,9 +125,8 @@
@Nullable OnDateSetListener listener, int year, int month, int dayOfMonth) {
this(context, themeResId);
- mDateSetListener = listener;
-
mDatePicker.updateDate(year, month, dayOfMonth);
+ mDateSetListener = listener;
}
static @StyleRes int resolveDialogTheme(@NonNull Context context, @StyleRes int themeResId) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 70bff80..8e87e26 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -33,6 +33,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
@@ -150,6 +151,12 @@
boolean preserveWindows, boolean animate) throws RemoteException;
/**
+ * Moves all tasks from the docked stack in the fullscreen stack and puts the top task of the
+ * fullscreen stack into the docked stack.
+ */
+ public void swapDockedAndFullscreenStack() throws RemoteException;
+
+ /**
* Resizes the docked stack, and all other stacks as the result of the dock stack bounds change.
*
* @param dockedBounds The bounds for the docked stack.
@@ -468,8 +475,13 @@
public void keyguardWaitingForActivityDrawn() throws RemoteException;
- public void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade) throws RemoteException;
+ /**
+ * Notify the system that the keyguard is going away.
+ *
+ * @param flags See {@link android.view.WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
+ * etc.
+ */
+ public void keyguardGoingAway(int flags) throws RemoteException;
public boolean shouldUpRecreateTask(IBinder token, String destAffinity)
throws RemoteException;
@@ -599,7 +611,10 @@
public void enterPictureInPicture(IBinder token) throws RemoteException;
- public void setVrMode(IBinder token, boolean enabled) throws RemoteException;
+ public int setVrMode(IBinder token, boolean enabled, ComponentName packageName)
+ throws RemoteException;
+
+ public boolean isVrModePackageEnabled(ComponentName packageName) throws RemoteException;
public boolean isAppForeground(int uid) throws RemoteException;
@@ -993,4 +1008,6 @@
int SET_LENIENT_BACKGROUND_CHECK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+368;
int GET_MEMORY_TRIM_LEVEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+369;
int RESIZE_PINNED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 370;
+ int IS_VR_PACKAGE_ENABLED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 371;
+ int SWAP_DOCKED_AND_FULLSCREEN_STACK = IBinder.FIRST_CALL_TRANSACTION + 372;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 5697924..7a69c62 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -80,7 +80,7 @@
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
void setInterruptionFilter(String pkg, int interruptionFilter);
- void setImportanceFromAssistant(in INotificationListener token, String key, int importance, CharSequence explanation);
+ void setImportanceFromRankerService(in INotificationListener token, String key, int importance, CharSequence explanation);
ComponentName getEffectsSuppressor();
boolean matchesCallFilter(in Bundle extras);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index cd17078..b8fc323 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -283,9 +283,9 @@
synchronized (this) {
mClassLoader = createOrUpdateClassLoaderLocked(addedPaths);
if (mResources != null) {
- mResources = mActivityThread.getNewTopLevelResources(mResDir, mSplitResDirs,
+ mResources = mActivityThread.getTopLevelResources(mResDir, mSplitResDirs,
mOverlayDirs, mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
- null /*overrideConfiguration*/, this);
+ this);
}
}
}
@@ -668,7 +668,7 @@
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,
- mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
+ mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, this);
}
return mResources;
}
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index 181c907..70a5e15 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -19,6 +19,7 @@
import com.android.internal.R;
import com.android.internal.app.MediaRouteDialogPresenter;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.TypedArray;
@@ -279,7 +280,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == mRemoteIndicator;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2c34371..88c8964 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -47,6 +47,7 @@
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
+import android.text.style.CharacterStyle;
import android.text.style.RelativeSizeSpan;
import android.text.style.TextAppearanceSpan;
import android.util.Log;
@@ -1664,17 +1665,22 @@
SpannableStringBuilder builder = new SpannableStringBuilder(ss.toString());
for (Object span : spans) {
Object resultSpan = span;
- if (span instanceof TextAppearanceSpan) {
- TextAppearanceSpan originalSpan = (TextAppearanceSpan) span;
+ if (resultSpan instanceof CharacterStyle) {
+ resultSpan = ((CharacterStyle) span).getUnderlying();
+ }
+ if (resultSpan instanceof TextAppearanceSpan) {
+ TextAppearanceSpan originalSpan = (TextAppearanceSpan) resultSpan;
resultSpan = new TextAppearanceSpan(
originalSpan.getFamily(),
originalSpan.getTextStyle(),
-1,
originalSpan.getTextColor(),
originalSpan.getLinkTextColor());
- } else if (span instanceof RelativeSizeSpan
- || span instanceof AbsoluteSizeSpan) {
+ } else if (resultSpan instanceof RelativeSizeSpan
+ || resultSpan instanceof AbsoluteSizeSpan) {
continue;
+ } else {
+ resultSpan = span;
}
builder.setSpan(resultSpan, ss.getSpanStart(span), ss.getSpanEnd(span),
ss.getSpanFlags(span));
@@ -2994,7 +3000,6 @@
contentView.setViewVisibility(R.id.chronometer, View.GONE);
contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
contentView.setViewVisibility(R.id.header_content_info, View.GONE);
- contentView.setViewVisibility(R.id.number_of_children, View.GONE);
contentView.setViewVisibility(R.id.sub_text_divider, View.GONE);
contentView.setViewVisibility(R.id.content_info_divider, View.GONE);
contentView.setViewVisibility(R.id.time_divider, View.GONE);
@@ -3095,7 +3100,6 @@
private void bindNotificationHeader(RemoteViews contentView) {
bindSmallIcon(contentView);
- bindChildCountColor(contentView);
bindHeaderAppName(contentView);
bindHeaderSubText(contentView);
bindContentInfo(contentView);
@@ -3104,10 +3108,6 @@
bindProfileBadge(contentView);
}
- private void bindChildCountColor(RemoteViews contentView) {
- contentView.setTextColor(R.id.number_of_children, resolveColor());
- }
-
private void bindContentInfo(RemoteViews contentView) {
boolean visible = false;
if (mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
@@ -3597,37 +3597,53 @@
}
/**
+ * Removes RemoteViews that were created for compatibility from {@param n}, if they did not
+ * change.
+ *
+ * @return {@param n}, if no stripping is needed, otherwise a stripped clone of {@param n}.
+ *
* @hide
*/
- public static void stripForDelivery(Notification n) {
+ public static Notification maybeCloneStrippedForDelivery(Notification n) {
String templateClass = n.extras.getString(EXTRA_TEMPLATE);
- if (TextUtils.isEmpty(templateClass)) {
- return;
- }
+
// Only strip views for known Styles because we won't know how to
// re-create them otherwise.
- if (getNotificationStyleClass(templateClass) == null) {
- return;
+ if (!TextUtils.isEmpty(templateClass)
+ && getNotificationStyleClass(templateClass) == null) {
+ return n;
}
- // Get rid of unmodified BuilderRemoteViews.
- if (n.contentView instanceof BuilderRemoteViews &&
+
+ // Only strip unmodified BuilderRemoteViews.
+ boolean stripContentView = n.contentView instanceof BuilderRemoteViews &&
n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
- n.contentView.getSequenceNumber()) {
- n.contentView = null;
- n.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
- }
- if (n.bigContentView instanceof BuilderRemoteViews &&
+ n.contentView.getSequenceNumber();
+ boolean stripBigContentView = n.bigContentView instanceof BuilderRemoteViews &&
n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
- n.bigContentView.getSequenceNumber()) {
- n.bigContentView = null;
- n.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
- }
- if (n.headsUpContentView instanceof BuilderRemoteViews &&
+ n.bigContentView.getSequenceNumber();
+ boolean stripHeadsUpContentView = n.headsUpContentView instanceof BuilderRemoteViews &&
n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
- n.headsUpContentView.getSequenceNumber()) {
- n.headsUpContentView = null;
- n.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
+ n.headsUpContentView.getSequenceNumber();
+
+ // Nothing to do here, no need to clone.
+ if (!stripContentView && !stripBigContentView && !stripHeadsUpContentView) {
+ return n;
}
+
+ Notification clone = n.clone();
+ if (stripContentView) {
+ clone.contentView = null;
+ clone.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
+ }
+ if (stripBigContentView) {
+ clone.bigContentView = null;
+ clone.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
+ }
+ if (stripHeadsUpContentView) {
+ clone.headsUpContentView = null;
+ clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
+ }
+ return clone;
}
private int getBaseLayoutResource() {
@@ -4206,7 +4222,8 @@
int i=0;
final float density = mBuilder.mContext.getResources().getDisplayMetrics().density;
int topPadding = (int) (5 * density);
- int bottomPadding = (int) (13 * density);
+ int bottomPadding = mBuilder.mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_bottom);
boolean first = true;
while (i < mTexts.size() && i < rowIds.length) {
CharSequence str = mTexts.get(i);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 057a4e94..10aa7eb 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -145,7 +145,6 @@
public static final String ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL
= "android.app.action.INTERRUPTION_FILTER_CHANGED_INTERNAL";
-
/** @hide */
@IntDef({INTERRUPTION_FILTER_NONE, INTERRUPTION_FILTER_PRIORITY, INTERRUPTION_FILTER_ALARMS,
INTERRUPTION_FILTER_ALL, INTERRUPTION_FILTER_UNKNOWN})
@@ -259,8 +258,7 @@
}
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
- final Notification copy = notification.clone();
- Builder.stripForDelivery(copy);
+ final Notification copy = Builder.maybeCloneStrippedForDelivery(notification);
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
copy, idOut, user.getIdentifier());
@@ -662,7 +660,7 @@
/**
* Notification policy configuration. Represents user-preferences for notification
- * filtering and prioritization.
+ * filtering.
*/
public static class Policy implements android.os.Parcelable {
/** Reminder notifications are prioritized. */
@@ -707,13 +705,13 @@
*/
public static final int SUPPRESSED_EFFECTS_UNSET = -1;
/**
- * Whether notification suppressed by DND should not interruption visually when the screen
- * is off.
+ * Whether notifications suppressed by DND should not interrupt visually (e.g. with
+ * notification lights or by turning the screen on) when the screen is off.
*/
public static final int SUPPRESSED_EFFECT_SCREEN_OFF = 1 << 0;
/**
- * Whether notification suppressed by DND should not interruption visually when the screen
- * is on.
+ * Whether notifications suppressed by DND should not interrupt visually when the screen
+ * is on (e.g. by peeking onto the screen).
*/
public static final int SUPPRESSED_EFFECT_SCREEN_ON = 1 << 1;
@@ -728,13 +726,27 @@
*/
public final int suppressedVisualEffects;
-
- @Deprecated
+ /**
+ * Constructs a policy for Do Not Disturb priority mode behavior.
+ *
+ * @param priorityCategories bitmask of categories of notifications that can bypass DND.
+ * @param priorityCallSenders which callers can bypass DND.
+ * @param priorityMessageSenders which message senders can bypass DND.
+ */
public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders) {
this(priorityCategories, priorityCallSenders, priorityMessageSenders,
SUPPRESSED_EFFECTS_UNSET);
}
+ /**
+ * Constructs a policy for Do Not Disturb priority mode behavior.
+ *
+ * @param priorityCategories bitmask of categories of notifications that can bypass DND.
+ * @param priorityCallSenders which callers can bypass DND.
+ * @param priorityMessageSenders which message senders can bypass DND.
+ * @param suppressedVisualEffects which visual interruptions should be suppressed from
+ * notifications that are filtered by DND.
+ */
public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
int suppressedVisualEffects) {
this.priorityCategories = priorityCategories;
@@ -865,7 +877,6 @@
return new Policy[size];
}
};
-
}
/**
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a6612f6..54d813d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -18,13 +18,17 @@
import static android.app.ActivityThread.DEBUG_CONFIGURATION;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.ResourcesImpl;
import android.content.res.ResourcesKey;
import android.hardware.display.DisplayManagerGlobal;
+import android.os.IBinder;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.LocaleList;
@@ -33,11 +37,16 @@
import android.util.Slog;
import android.view.Display;
import android.view.DisplayAdjustments;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
-import java.util.List;
+import java.util.Objects;
+import java.util.WeakHashMap;
+import java.util.function.Predicate;
/** @hide */
public class ResourcesManager {
@@ -45,18 +54,56 @@
private static final boolean DEBUG = false;
private static ResourcesManager sResourcesManager;
- private final ArrayMap<ResourcesKey, WeakReference<Resources>> mActiveResources =
- new ArrayMap<>();
- private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>> mDisplays =
- new ArrayMap<>();
+
+ /**
+ * Predicate that returns true if a WeakReference is gc'ed.
+ */
+ private static final Predicate<WeakReference<Resources>> sEmptyReferencePredicate =
+ new Predicate<WeakReference<Resources>>() {
+ @Override
+ public boolean test(WeakReference<Resources> weakRef) {
+ return weakRef == null || weakRef.get() == null;
+ }
+ };
private String[] mSystemLocales = {};
- private final HashSet<String> mNonSystemLocales = new HashSet<String>();
+ private final HashSet<String> mNonSystemLocales = new HashSet<>();
private boolean mHasNonSystemLocales = false;
- CompatibilityInfo mResCompatibilityInfo;
+ /**
+ * The global compatibility settings.
+ */
+ private CompatibilityInfo mResCompatibilityInfo;
- Configuration mResConfiguration;
+ /**
+ * The global configuration upon which all Resources are based. Multi-window Resources
+ * apply their overrides to this configuration.
+ */
+ private final Configuration mResConfiguration = new Configuration();
+
+ /**
+ * A mapping of ResourceImpls and their configurations. These are heavy weight objects
+ * which should be reused as much as possible.
+ */
+ private final ArrayMap<ResourcesKey, WeakReference<ResourcesImpl>> mResourceImpls =
+ new ArrayMap<>();
+
+ /**
+ * A list of Resource references that can be reused.
+ */
+ private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
+
+ /**
+ * Each Activity may have only one Resources object.
+ */
+ private final WeakHashMap<IBinder, WeakReference<Resources>> mActivityResourceReferences =
+ new WeakHashMap<>();
+
+ /**
+ * A cache of DisplayId to DisplayAdjustments.
+ */
+ private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>> mDisplays =
+ new ArrayMap<>();
public static ResourcesManager getInstance() {
synchronized (ResourcesManager.class) {
@@ -75,7 +122,11 @@
return getDisplayMetricsLocked(Display.DEFAULT_DISPLAY);
}
- DisplayMetrics getDisplayMetricsLocked(int displayId) {
+ /**
+ * Protected so that tests can override and returns something a fixed value.
+ */
+ @VisibleForTesting
+ protected DisplayMetrics getDisplayMetricsLocked(int displayId) {
DisplayMetrics dm = new DisplayMetrics();
final Display display =
getAdjustedDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -87,12 +138,12 @@
return dm;
}
- final void applyNonDefaultDisplayMetricsToConfigurationLocked(
- DisplayMetrics dm, Configuration config) {
+ private static void applyNonDefaultDisplayMetricsToConfiguration(
+ @NonNull DisplayMetrics dm, @NonNull Configuration config) {
config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
config.densityDpi = dm.densityDpi;
- config.screenWidthDp = (int)(dm.widthPixels / dm.density);
- config.screenHeightDp = (int)(dm.heightPixels / dm.density);
+ config.screenWidthDp = (int) (dm.widthPixels / dm.density);
+ config.screenHeightDp = (int) (dm.heightPixels / dm.density);
int sl = Configuration.resetScreenLayout(config.screenLayout);
if (dm.widthPixels > dm.heightPixels) {
config.orientation = Configuration.ORIENTATION_LANDSCAPE;
@@ -109,8 +160,8 @@
config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
}
- public boolean applyCompatConfiguration(int displayDensity,
- Configuration compatConfiguration) {
+ public boolean applyCompatConfigurationLocked(int displayDensity,
+ @NonNull Configuration compatConfiguration) {
if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
return true;
@@ -125,7 +176,8 @@
* @param displayId display Id.
* @param displayAdjustments display adjustments.
*/
- public Display getAdjustedDisplay(final int displayId, DisplayAdjustments displayAdjustments) {
+ public Display getAdjustedDisplay(final int displayId,
+ @Nullable DisplayAdjustments displayAdjustments) {
final DisplayAdjustments displayAdjustmentsCopy = (displayAdjustments != null)
? new DisplayAdjustments(displayAdjustments) : new DisplayAdjustments();
final Pair<Integer, DisplayAdjustments> key =
@@ -152,76 +204,42 @@
}
/**
- * Creates the top level Resources for applications with the given compatibility info.
+ * Creates an AssetManager from the paths within the ResourcesKey.
*
- * @param resDir the resource directory.
- * @param splitResDirs split resource directories.
- * @param overlayDirs the resource overlay directories.
- * @param libDirs the shared library resource dirs this app references.
- * @param displayId display Id.
- * @param overrideConfiguration override configurations.
- * @param compatInfo the compatibility info. Must not be null.
- * @param classLoader the class loader for the resource package
- */
- Resources getTopLevelResources(String resDir, String[] splitResDirs,
- String[] overlayDirs, String[] libDirs, int displayId,
- Configuration overrideConfiguration, CompatibilityInfo compatInfo,
- ClassLoader classLoader) {
- final float scale = compatInfo.applicationScale;
- Configuration overrideConfigCopy = (overrideConfiguration != null)
- ? new Configuration(overrideConfiguration) : null;
- ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
- Resources r;
- final boolean findSystemLocales;
- final boolean hasNonSystemLocales;
- synchronized (this) {
- // Resources is app scale dependent.
- if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
-
- WeakReference<Resources> wr = mActiveResources.get(key);
- r = wr != null ? wr.get() : null;
- //if (r != null) Log.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
- if (r != null && r.getAssets().isUpToDate()) {
- if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir
- + ": appScale=" + r.getCompatibilityInfo().applicationScale
- + " key=" + key + " overrideConfig=" + overrideConfiguration);
- return r;
- }
- findSystemLocales = (mSystemLocales.length == 0);
- hasNonSystemLocales = mHasNonSystemLocales;
- }
-
- //if (r != null) {
- // Log.w(TAG, "Throwing away out-of-date resources!!!! "
- // + r + " " + resDir);
- //}
-
+ * This can be overridden in tests so as to avoid creating a real AssetManager with
+ * real APK paths.
+ * @param key The key containing the resource paths to add to the AssetManager.
+ * @return a new AssetManager.
+ */
+ @VisibleForTesting
+ protected AssetManager createAssetManager(@NonNull final ResourcesKey key) {
AssetManager assets = new AssetManager();
+
// resDir can be null if the 'android' package is creating a new Resources object.
// This is fine, since each AssetManager automatically loads the 'android' package
// already.
- if (resDir != null) {
- if (assets.addAssetPath(resDir) == 0) {
+ if (key.mResDir != null) {
+ if (assets.addAssetPath(key.mResDir) == 0) {
return null;
}
}
- if (splitResDirs != null) {
- for (String splitResDir : splitResDirs) {
+ if (key.mSplitResDirs != null) {
+ for (final String splitResDir : key.mSplitResDirs) {
if (assets.addAssetPath(splitResDir) == 0) {
return null;
}
}
}
- if (overlayDirs != null) {
- for (String idmapPath : overlayDirs) {
+ if (key.mOverlayDirs != null) {
+ for (final String idmapPath : key.mOverlayDirs) {
assets.addOverlayPath(idmapPath);
}
}
- if (libDirs != null) {
- for (String libDir : libDirs) {
+ if (key.mLibDirs != null) {
+ for (final String libDir : key.mLibDirs) {
if (libDir.endsWith(".apk")) {
// Avoid opening files we know do not have resources,
// like code-only .jar files.
@@ -232,16 +250,17 @@
}
}
}
+ return assets;
+ }
- //Log.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
- DisplayMetrics dm = getDisplayMetricsLocked(displayId);
+ private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) {
Configuration config;
- final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ final boolean isDefaultDisplay = (key.mDisplayId == Display.DEFAULT_DISPLAY);
final boolean hasOverrideConfig = key.hasOverrideConfiguration();
if (!isDefaultDisplay || hasOverrideConfig) {
config = new Configuration(getConfiguration());
if (!isDefaultDisplay) {
- applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
+ applyNonDefaultDisplayMetricsToConfiguration(dm, config);
}
if (hasOverrideConfig) {
config.updateFrom(key.mOverrideConfiguration);
@@ -250,15 +269,212 @@
} else {
config = getConfiguration();
}
- r = new Resources(assets, dm, config, compatInfo, classLoader);
- if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
- + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale);
+ return config;
+ }
- final String[] systemLocales = (
- findSystemLocales ?
- AssetManager.getSystem().getLocales() :
- null);
- final String[] nonSystemLocales = assets.getNonSystemLocales();
+
+ private ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key) {
+ AssetManager assets = createAssetManager(key);
+ DisplayMetrics dm = getDisplayMetricsLocked(key.mDisplayId);
+ Configuration config = generateConfig(key, dm);
+ ResourcesImpl impl = new ResourcesImpl(assets, dm, config, key.mCompatInfo);
+ if (DEBUG) {
+ Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
+ }
+ return impl;
+ }
+
+ /**
+ * Finds a cached ResourcesImpl object that matches the given ResourcesKey.
+ *
+ * @param key The key to match.
+ * @return a ResourcesImpl if the key matches a cache entry, null otherwise.
+ */
+ private ResourcesImpl findResourcesImplForKey(@NonNull ResourcesKey key) {
+ WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.get(key);
+ ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+ if (impl != null && impl.getAssets().isUpToDate()) {
+ return impl;
+ }
+ return null;
+ }
+
+ /**
+ * Find the ResourcesKey that this ResourcesImpl object is associated with.
+ * @return the ResourcesKey or null if none was found.
+ */
+ private ResourcesKey findKeyForResourceImpl(@NonNull ResourcesImpl resourceImpl) {
+ final int refCount = mResourceImpls.size();
+ for (int i = 0; i < refCount; i++) {
+ WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+ if (impl != null && resourceImpl == impl) {
+ return mResourceImpls.keyAt(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets an existing Resources object tied to this Activity, or creates one if it doesn't exist
+ * or the class loader is different.
+ */
+ private Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
+ @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl) {
+ // This is a request tied to an Activity, meaning we will need to update all
+ // Activity related Resources to match this configuration.
+ WeakReference<Resources> weakResourceRef = mActivityResourceReferences.get(activityToken);
+ Resources resources = weakResourceRef != null ? weakResourceRef.get() : null;
+ if (resources == null || !Objects.equals(resources.getClassLoader(), classLoader)) {
+ resources = new Resources(classLoader);
+ mActivityResourceReferences.put(activityToken, new WeakReference<>(resources));
+ if (DEBUG) {
+ Slog.d(TAG, "- creating new ref=" + resources);
+ }
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "- using existing ref=" + resources);
+ }
+ }
+
+ if (resources.getImpl() != impl) {
+ if (DEBUG) {
+ Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
+ }
+
+ // Setting an impl is expensive because we update all ThemeImpl references.
+ // too.
+ resources.setImpl(impl);
+ }
+ return resources;
+ }
+
+ /**
+ * Gets an existing Resources object if the class loader and ResourcesImpl are the same,
+ * otherwise creates a new Resources object.
+ */
+ private Resources getOrCreateResourcesLocked(@NonNull ClassLoader classLoader,
+ @NonNull ResourcesImpl impl) {
+ // Find an existing Resources that has this ResourcesImpl set.
+ final int refCount = mResourceReferences.size();
+ for (int i = 0; i < refCount; i++) {
+ WeakReference<Resources> weakResourceRef = mResourceReferences.get(i);
+ Resources resources = weakResourceRef != null ? weakResourceRef.get() : null;
+ if (resources != null &&
+ Objects.equals(resources.getClassLoader(), classLoader) &&
+ resources.getImpl() == impl) {
+ if (DEBUG) {
+ Slog.d(TAG, "- using existing ref=" + resources);
+ }
+ return resources;
+ }
+ }
+
+ // Create a new Resources reference and use the existing ResourcesImpl object.
+ Resources resources = new Resources(classLoader);
+ resources.setImpl(impl);
+ mResourceReferences.add(new WeakReference<>(resources));
+ if (DEBUG) {
+ Slog.d(TAG, "- creating new ref=" + resources);
+ Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
+ }
+ return resources;
+ }
+
+ /**
+ * Gets or creates a new Resources object associated with the IBinder token. References returned
+ * by this method live as long as the Activity, meaning they can be cached and used by the
+ * Activity even after a configuration change. If any other parameter is changed
+ * (resDir, splitResDirs, overrideConfig) for a given Activity, the same Resources object
+ * is updated and handed back to the caller. However, changing the class loader will result in a
+ * new Resources object.
+ * <p/>
+ * If activityToken is null, a cached Resources object will be returned if it matches the
+ * input parameters. Otherwise a new Resources object that satisfies these parameters is
+ * returned.
+ *
+ * @param activityToken Represents an Activity. If null, global resources are assumed.
+ * @param resDir The base resource path. Can be null (only framework resources will be loaded).
+ * @param splitResDirs An array of split resource paths. Can be null.
+ * @param overlayDirs An array of overlay paths. Can be null.
+ * @param libDirs An array of resource library paths. Can be null.
+ * @param displayId The ID of the display for which to create the resources.
+ * @param overrideConfig The configuration to apply on top of the base configuration. Can be
+ * null. Mostly used with Activities that are in multi-window which may override width and
+ * height properties from the base config.
+ * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is
+ * {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}.
+ * @param classLoader The class loader to use when inflating Resources. If null, the
+ * {@link ClassLoader#getSystemClassLoader()} is used.
+ * @return a Resources object from which to access resources.
+ */
+ public Resources getResources(@Nullable IBinder activityToken,
+ @Nullable String resDir,
+ @Nullable String[] splitResDirs,
+ @Nullable String[] overlayDirs,
+ @Nullable String[] libDirs,
+ int displayId,
+ @Nullable Configuration overrideConfig,
+ @NonNull CompatibilityInfo compatInfo,
+ @Nullable ClassLoader classLoader) {
+ final ResourcesKey key = new ResourcesKey(
+ resDir,
+ splitResDirs,
+ overlayDirs,
+ libDirs,
+ displayId,
+ overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+ compatInfo);
+
+ classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+
+ final boolean findSystemLocales;
+ final boolean hasNonSystemLocales;
+ synchronized (this) {
+ findSystemLocales = (mSystemLocales.length == 0);
+ hasNonSystemLocales = mHasNonSystemLocales;
+
+ if (DEBUG) {
+ Throwable here = new Throwable();
+ here.fillInStackTrace();
+ Slog.w(TAG, "!! Get resources for activity=" + activityToken + " key=" + key, here);
+ }
+
+ if (activityToken != null) {
+ ResourcesImpl resourcesImpl = findResourcesImplForKey(key);
+ if (resourcesImpl != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "- using existing impl=" + resourcesImpl);
+ }
+ return getOrCreateResourcesForActivityLocked(activityToken, classLoader,
+ resourcesImpl);
+ }
+
+ // We will create the ResourcesImpl object outside of holding this lock.
+
+ } else {
+ // Clean up any dead references so they don't pile up.
+ ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
+
+ // Not tied to an Activity, find a shared Resources that has the right ResourcesImpl
+ ResourcesImpl resourcesImpl = findResourcesImplForKey(key);
+ if (resourcesImpl != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "- using existing impl=" + resourcesImpl);
+ }
+ return getOrCreateResourcesLocked(classLoader, resourcesImpl);
+ }
+
+ // We will create the ResourcesImpl object outside of holding this lock.
+ }
+ }
+
+ // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
+ ResourcesImpl resourcesImpl = createResourcesImpl(key);
+
+ final String[] systemLocales = findSystemLocales
+ ? AssetManager.getSystem().getLocales() : null;
+ final String[] nonSystemLocales = resourcesImpl.getAssets().getNonSystemLocales();
// Avoid checking for non-pseudo-locales if we already know there were some from a previous
// Resources. The default value (for when hasNonSystemLocales is true) doesn't matter,
// since mHasNonSystemLocales will also be true, and thus isPseudoLocalesOnly would not be
@@ -267,41 +483,75 @@
LocaleList.isPseudoLocalesOnly(nonSystemLocales);
synchronized (this) {
- WeakReference<Resources> wr = mActiveResources.get(key);
- Resources existing = wr != null ? wr.get() : null;
- if (existing != null && existing.getAssets().isUpToDate()) {
- // Someone else already created the resources while we were
- // unlocked; go ahead and use theirs.
- r.getAssets().close();
- return existing;
- }
-
- // XXX need to remove entries when weak references go away
- mActiveResources.put(key, new WeakReference<>(r));
if (mSystemLocales.length == 0) {
mSystemLocales = systemLocales;
}
mNonSystemLocales.addAll(Arrays.asList(nonSystemLocales));
mHasNonSystemLocales = mHasNonSystemLocales || !isPseudoLocalesOnly;
- if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size());
- return r;
+
+ ResourcesImpl existingResourcesImpl = findResourcesImplForKey(key);
+ if (existingResourcesImpl != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
+ + " new impl=" + resourcesImpl);
+ }
+ resourcesImpl.getAssets().close();
+ resourcesImpl = existingResourcesImpl;
+ } else {
+ // Add this ResourcesImpl to the cache.
+ mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
+ }
+
+ final Resources resources;
+ if (activityToken != null) {
+ resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
+ resourcesImpl);
+ } else {
+ resources = getOrCreateResourcesLocked(classLoader, resourcesImpl);
+ }
+ return resources;
}
}
/**
- * Removes the top level Resources for applications with the given compatibility info.
- * @see #getTopLevelResources(String, String[], String[], String[], int, Configuration, CompatibilityInfo, ClassLoader)
+ * Updates an Activity's Resources object with overrideConfig. The Resources object
+ * that was previously returned by
+ * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration,
+ * CompatibilityInfo, ClassLoader)} is
+ * still valid and will have the updated configuration.
+ * @param activityToken The Activity token.
+ * @param overrideConfig The configuration override to update.
*/
- void removeTopLevelResources(String resDir, int displayId, Configuration overrideConfiguration,
- CompatibilityInfo compatInfo) {
- final float scale = compatInfo.applicationScale;
- final Configuration overrideConfigCopy = (overrideConfiguration != null)
- ? new Configuration(overrideConfiguration) : null;
- final ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
- mActiveResources.remove(key);
+ public void updateResourcesForActivity(@NonNull IBinder activityToken,
+ @Nullable Configuration overrideConfig) {
+ final ClassLoader classLoader;
+ final ResourcesKey oldKey;
+ synchronized (this) {
+ // Extract the ResourcesKey that was last used to create the Resources for this
+ // activity.
+ WeakReference<Resources> weakResRef = mActivityResourceReferences.get(activityToken);
+ final Resources resources = weakResRef != null ? weakResRef.get() : null;
+ if (resources == null) {
+ Slog.e(TAG, "can't update resources for uncached activity " + activityToken);
+ return;
+ }
+
+ classLoader = resources.getClassLoader();
+ oldKey = findKeyForResourceImpl(resources.getImpl());
+ if (oldKey == null) {
+ Slog.e(TAG, "can't find ResourcesKey for resources impl=" + resources.getImpl());
+ return;
+ }
+ }
+
+ // Update the Resources object with the new override config and all of the existing
+ // settings.
+ getResources(activityToken, oldKey.mResDir, oldKey.mSplitResDirs, oldKey.mOverlayDirs,
+ oldKey.mLibDirs, oldKey.mDisplayId, overrideConfig, oldKey.mCompatInfo,
+ classLoader);
}
- /* package */ void setDefaultLocalesLocked(LocaleList locales) {
+ /* package */ void setDefaultLocalesLocked(@NonNull LocaleList locales) {
final int bestLocale;
if (mHasNonSystemLocales) {
bestLocale = locales.getFirstMatchIndexWithEnglishSupported(mNonSystemLocales);
@@ -315,11 +565,8 @@
LocaleList.setDefault(locales, bestLocale);
}
- final boolean applyConfigurationToResourcesLocked(Configuration config,
- CompatibilityInfo compat) {
- if (mResConfiguration == null) {
- mResConfiguration = new Configuration();
- }
+ public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
+ @Nullable CompatibilityInfo compat) {
if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+ mResConfiguration.seq + ", newSeq=" + config.seq);
@@ -366,9 +613,9 @@
Configuration tmpConfig = null;
- for (int i = mActiveResources.size() - 1; i >= 0; i--) {
- ResourcesKey key = mActiveResources.keyAt(i);
- Resources r = mActiveResources.valueAt(i).get();
+ for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
+ ResourcesKey key = mResourceImpls.keyAt(i);
+ ResourcesImpl r = mResourceImpls.valueAt(i).get();
if (r != null) {
if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ r + " config to: " + localeAdjustedConfig);
@@ -383,7 +630,7 @@
tmpConfig.setTo(localeAdjustedConfig);
if (!isDefaultDisplay) {
dm = getDisplayMetricsLocked(displayId);
- applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
+ applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
}
if (hasOverrideConfiguration) {
tmpConfig.updateFrom(key.mOverrideConfiguration);
@@ -396,11 +643,10 @@
// + " " + r + ": " + r.getConfiguration());
} else {
//Slog.i(TAG, "Removing old resources " + v.getKey());
- mActiveResources.removeAt(i);
+ mResourceImpls.removeAt(i);
}
}
return changes != 0;
}
-
-}
+}
\ No newline at end of file
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index b1c5fd8..6c0b69c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -37,7 +37,9 @@
import android.content.IRestrictionsManager;
import android.content.RestrictionsManager;
import android.content.pm.ILauncherApps;
+import android.content.pm.IShortcutService;
import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutManager;
import android.content.res.Resources;
import android.hardware.ConsumerIrManager;
import android.hardware.ISerialManager;
@@ -101,6 +103,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
+import android.os.health.SystemHealthManager;
import android.os.storage.StorageManager;
import android.print.IPrintManager;
import android.print.PrintManager;
@@ -271,9 +274,9 @@
}});
registerService(Context.DROPBOX_SERVICE, DropBoxManager.class,
- new StaticServiceFetcher<DropBoxManager>() {
+ new CachedServiceFetcher<DropBoxManager>() {
@Override
- public DropBoxManager createService() {
+ public DropBoxManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.DROPBOX_SERVICE);
IDropBoxManagerService service = IDropBoxManagerService.Stub.asInterface(b);
if (service == null) {
@@ -283,7 +286,7 @@
// DROPBOX_SERVICE is registered.
return null;
}
- return new DropBoxManager(service);
+ return new DropBoxManager(ctx, service);
}});
registerService(Context.INPUT_SERVICE, InputManager.class,
@@ -745,9 +748,23 @@
@Override
public SoundTriggerManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE);
- Log.i(TAG, "Creating new instance of SoundTriggerManager object.");
return new SoundTriggerManager(ctx, ISoundTriggerService.Stub.asInterface(b));
}});
+
+ registerService(Context.SHORTCUT_SERVICE, ShortcutManager.class,
+ new CachedServiceFetcher<ShortcutManager>() {
+ @Override
+ public ShortcutManager createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(Context.SHORTCUT_SERVICE);
+ return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
+ }});
+
+ registerService(Context.SYSTEM_HEALTH_SERVICE, SystemHealthManager.class,
+ new CachedServiceFetcher<SystemHealthManager>() {
+ @Override
+ public SystemHealthManager createService(ContextImpl ctx) {
+ return new SystemHealthManager();
+ }});
}
/**
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 6bc03f7..52e5272 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -49,7 +49,6 @@
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;
@@ -263,7 +262,8 @@
static class Globals extends IWallpaperManagerCallback.Stub {
private IWallpaperManager mService;
- private Bitmap mWallpaper;
+ private Bitmap mCachedWallpaper;
+ private int mCachedWallpaperUserId;
private Bitmap mDefaultWallpaper;
Globals(Looper looper) {
@@ -296,33 +296,34 @@
throw e.rethrowFromSystemServer();
}
}
- if (mWallpaper != null) {
- return mWallpaper;
+ if (mCachedWallpaper != null && mCachedWallpaperUserId == userId) {
+ return mCachedWallpaper;
}
- if (mDefaultWallpaper != null) {
- return mDefaultWallpaper;
- }
- mWallpaper = null;
+ mCachedWallpaper = null;
+ mCachedWallpaperUserId = 0;
try {
- mWallpaper = getCurrentWallpaperLocked(userId);
+ mCachedWallpaper = getCurrentWallpaperLocked(userId);
+ mCachedWallpaperUserId = userId;
} catch (OutOfMemoryError e) {
Log.w(TAG, "No memory load current wallpaper", e);
}
- if (returnDefault) {
- if (mWallpaper == null) {
- mDefaultWallpaper = getDefaultWallpaperLocked(context);
- return mDefaultWallpaper;
- } else {
- mDefaultWallpaper = null;
- }
+ if (mCachedWallpaper != null) {
+ return mCachedWallpaper;
}
- return mWallpaper;
+ if (returnDefault) {
+ if (mDefaultWallpaper == null) {
+ mDefaultWallpaper = getDefaultWallpaperLocked(context);
+ }
+ return mDefaultWallpaper;
+ }
+ return null;
}
}
public void forgetLoadedWallpaper() {
synchronized (this) {
- mWallpaper = null;
+ mCachedWallpaper = null;
+ mCachedWallpaperUserId = 0;
mDefaultWallpaper = null;
}
}
@@ -918,8 +919,8 @@
* image for restore to a future device; {@code false} otherwise.
* @param which Flags indicating which wallpaper(s) to configure with the new imagery.
*
- * @see #FLAG_SET_LOCK_WALLPAPER
- * @see #FLAG_SET_SYSTEM_WALLPAPER
+ * @see #FLAG_SET_LOCK
+ * @see #FLAG_SET_SYSTEM
*
* @return An integer ID assigned to the newly active wallpaper; or zero on failure.
*
@@ -1037,8 +1038,8 @@
* image for restore to a future device; {@code false} otherwise.
* @param which Flags indicating which wallpaper(s) to configure with the new imagery.
*
- * @see #FLAG_SET_LOCK_WALLPAPER
- * @see #FLAG_SET_SYSTEM_WALLPAPER
+ * @see #FLAG_SET_LOCK
+ * @see #FLAG_SET_SYSTEM
*
* @throws IOException
*/
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index a34e8551e..3be2cdc 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -284,10 +284,11 @@
public static final String EXTRA_BUGREPORT_HASH = "android.app.extra.BUGREPORT_HASH";
/**
- * An {@code int} failure code representing the reason of the bugreport failure.
+ * An {@code int} failure code representing the reason of the bugreport failure. One of
+ * {@link #BUGREPORT_FAILURE_FAILED_COMPLETING}
+ * or {@link #BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE}
*
* @see #ACTION_BUGREPORT_FAILED
- * @see #BUGREPORT_FAILURE_FAILED_COMPLETING, #BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
* @hide
*/
public static final String EXTRA_BUGREPORT_FAILURE_REASON =
@@ -305,9 +306,24 @@
BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
})
public @interface BugreportFailureCode {}
- /** Bugreport completion process failed. */
+
+ /**
+ * Bugreport completion process failed.
+ *
+ * <p>If this error code is received, the requesting of bugreport can be retried.
+ * @see DevicePolicyManager#requestBugreport
+ */
public static final int BUGREPORT_FAILURE_FAILED_COMPLETING = 0;
- /** Bugreport is no longer available for collection. */
+
+ /**
+ * Bugreport has been created, but is no longer available for collection.
+ *
+ * <p>This error likely occurs because the user of the device hasn't consented to share
+ * the bugreport for a long period after its creation.
+ *
+ * <p>If this error code is received, the requesting of bugreport can be retried.
+ * @see DevicePolicyManager#requestBugreport
+ */
public static final int BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE = 1;
/** @hide */
@@ -598,7 +614,8 @@
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
* @param failureCode int containing failure code. One of
- * #BUGREPORT_FAILURE_FAILED_COMPLETING or #BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE
+ * {@link #BUGREPORT_FAILURE_FAILED_COMPLETING}
+ * or {@link #BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE}
* @see DevicePolicyManager#requestBugreport
*/
public void onBugreportFailed(Context context, Intent intent,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a1ad590..ec1e3e6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -344,6 +344,68 @@
= "android.app.action.PROVISION_FINALIZATION";
/**
+ * Action: Bugreport sharing with device owner has been accepted by the user.
+ *
+ * @hide
+ */
+ public static final String ACTION_BUGREPORT_SHARING_ACCEPTED =
+ "com.android.server.action.BUGREPORT_SHARING_ACCEPTED";
+
+ /**
+ * Action: Bugreport sharing with device owner has been declined by the user.
+ *
+ * @hide
+ */
+ public static final String ACTION_BUGREPORT_SHARING_DECLINED =
+ "com.android.server.action.BUGREPORT_SHARING_DECLINED";
+
+ /**
+ * Action: Bugreport has been collected and is dispatched to {@link DevicePolicyManagerService}.
+ *
+ * @hide
+ */
+ public static final String ACTION_REMOTE_BUGREPORT_DISPATCH =
+ "android.intent.action.REMOTE_BUGREPORT_DISPATCH";
+
+ /**
+ * Extra for shared bugreport's SHA-256 hash.
+ *
+ * @hide
+ */
+ public static final String EXTRA_REMOTE_BUGREPORT_HASH =
+ "android.intent.extra.REMOTE_BUGREPORT_HASH";
+
+ /**
+ * Extra for remote bugreport notification shown type.
+ *
+ * @hide
+ */
+ public static final String EXTRA_BUGREPORT_NOTIFICATION_TYPE =
+ "android.app.extra.bugreport_notification_type";
+
+ /**
+ * Notification type for a started remote bugreport flow.
+ *
+ * @hide
+ */
+ public static final int NOTIFICATION_BUGREPORT_STARTED = 1;
+
+ /**
+ * Notification type for a bugreport that has already been accepted to be shared, but is still
+ * being taken.
+ *
+ * @hide
+ */
+ public static final int NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED = 2;
+
+ /**
+ * Notification type for a bugreport that has been taken and can be shared or declined.
+ *
+ * @hide
+ */
+ public static final int NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED = 3;
+
+ /**
* A {@link android.os.Parcelable} extra of type {@link android.os.PersistableBundle} that
* allows a mobile device management application or NFC programmer application which starts
* managed provisioning to pass data to the management application instance after provisioning.
@@ -2425,6 +2487,12 @@
public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4;
/**
+ * Result code for {@link #getStorageEncryptionStatus}:
+ * indicating that encryption is active and the encryption key is tied to the user.
+ */
+ public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5;
+
+ /**
* Activity action: begin the process of encrypting data on the device. This activity should
* be launched after using {@link #setStorageEncryption} to request encryption be activated.
* After resuming from this activity, use {@link #getStorageEncryption}
@@ -2565,7 +2633,7 @@
public int getStorageEncryptionStatus(int userHandle) {
if (mService != null) {
try {
- return mService.getStorageEncryptionStatus(userHandle);
+ return mService.getStorageEncryptionStatus(mContext.getPackageName(), userHandle);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2712,18 +2780,19 @@
* certificate and corresponding private key. All apps within the profile will be able to access
* the certificate and use the private key, given direct user approval.
*
- * <p>The caller of this API may grant itself access to the credential immediately, without user
- * approval. It is a best practice not to request this unless strictly necessary since it opens
- * up additional security vulnerabilities.
+ * <p>The caller of this API may grant itself access to the certificate and private key
+ * immediately, without user approval. It is a best practice not to request this unless strictly
+ * necessary since it opens up additional security vulnerabilities.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
- * {@code null} if calling from a delegated certificate installer.
+ * {@code null} if calling from a delegated certificate installer.
* @param privKey The private key to install.
* @param cert The certificate to install.
* @param alias The private key alias under which to install the certificate. If a certificate
- * with that alias already exists, it will be overwritten.
+ * with that alias already exists, it will be overwritten.
* @param requestAccess {@code true} to request that the calling app be granted access to the
- * credentials immediately. Otherwise, access to the credentials will be gated by user approval.
+ * credentials immediately. Otherwise, access to the credentials will be gated by user
+ * approval.
* @return {@code true} if the keys were installed, {@code false} otherwise.
*/
public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
@@ -2744,13 +2813,13 @@
}
/**
- * Called by a device or profile owner, or delegated certificate installer, to remove all user
- * credentials installed under a given alias.
+ * Called by a device or profile owner, or delegated certificate installer, to remove a
+ * certificate and private key pair installed under a given alias.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
- * {@code null} if calling from a delegated certificate installer.
+ * {@code null} if calling from a delegated certificate installer.
* @param alias The private key alias under which the certificate is installed.
- * @return {@code true} if the certificate alias no longer exists, {@code false} otherwise.
+ * @return {@code true} if the private key alias no longer exists, {@code false} otherwise.
*/
public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
try {
@@ -3660,7 +3729,9 @@
* be hidden, it will not show up in recents, will not be able to show toasts or dialogs
* or ring the device.
*
- * <p>The package must already be installed.
+ * <p>The package must already be installed. If the package is uninstalled while suspended
+ * the package will no longer be suspended. The admin can block this by using
+ * {@link #setUninstallBlocked}.
*
* @param admin The name of the admin component to check.
* @param packageNames The package names to suspend or unsuspend.
@@ -5480,14 +5551,15 @@
/**
* Called by device owner to get the MAC address of the Wi-Fi device.
*
+ * @param admin Which device owner this request is associated with.
* @return the MAC address of the Wi-Fi device, or null when the information is not
* available. (For example, Wi-Fi hasn't been enabled, or the device doesn't support Wi-Fi.)
*
* <p>The address will be in the {@code XX:XX:XX:XX:XX:XX} format.
*/
- public String getWifiMacAddress() {
+ public String getWifiMacAddress(@NonNull ComponentName admin) {
try {
- return mService.getWifiMacAddress();
+ return mService.getWifiMacAddress(admin);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 0a0d77d..61b40d4 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -16,6 +16,8 @@
package android.app.admin;
+import android.os.UserHandle;
+
import java.util.List;
/**
@@ -69,4 +71,13 @@
* @return true if the uid is an active admin with the given policy.
*/
public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
+
+ /**
+ * Checks if a given package has a device or a profile owner for the given user.
+ * </br><em>Does <b>not</b> support negative userIds like {@link UserHandle#USER_ALL}</em>
+ * @param packageName The package to check
+ * @param userId the userId to check for.
+ * @return true if package has a device or profile owner, false otherwise.
+ */
+ public abstract boolean hasDeviceOwnerOrProfileOwner(String packageName, int userId);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b7a16aa..c38496d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -91,7 +91,7 @@
int setStorageEncryption(in ComponentName who, boolean encrypt);
boolean getStorageEncryption(in ComponentName who, int userHandle);
- int getStorageEncryptionStatus(int userHandle);
+ int getStorageEncryptionStatus(in String callerPackage, int userHandle);
boolean requestBugreport(in ComponentName who);
@@ -262,7 +262,7 @@
List<String> getKeepUninstalledPackages(in ComponentName admin);
boolean isManagedProfile(in ComponentName admin);
boolean isSystemOnlyUser(in ComponentName admin);
- String getWifiMacAddress();
+ String getWifiMacAddress(in ComponentName admin);
void reboot(in ComponentName admin);
void setShortSupportMessage(in ComponentName admin, in String message);
diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java
index 30c11ef..f256a95 100644
--- a/core/java/android/app/backup/WallpaperBackupHelper.java
+++ b/core/java/android/app/backup/WallpaperBackupHelper.java
@@ -120,6 +120,7 @@
* need to be backed up, write them to the data stream, and fill in newState with the
* state as it exists now.
*/
+ @Override
public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) {
performBackup_checked(oldState, data, newState, mFiles, mKeys);
@@ -130,6 +131,7 @@
* magic wallpaper file, take specific action to determine whether it is suitable for
* the current device.
*/
+ @Override
public void restoreEntity(BackupDataInputStream data) {
final String key = data.getKey();
if (isKeyInList(key, mKeys)) {
@@ -174,12 +176,8 @@
}
// We passed the acceptable-dimensions test (if any), so we're going to
- // use the restored image.
- // TODO: spin a service to copy the restored image to sd/usb storage,
- // 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));
+ // use the restored image. That comes last, when we are done restoring
+ // both the pixels and the metadata.
}
} else if (key.equals(WALLPAPER_INFO_KEY)) {
// XML file containing wallpaper info
@@ -188,4 +186,20 @@
}
}
}
+
+ /**
+ * Hook for the agent to call this helper upon completion of the restore. We do this
+ * upon completion so that we know both the imagery and the wallpaper info have
+ * been emplaced without requiring either or relying on ordering.
+ */
+ public void onRestoreFinished() {
+ final File f = new File(STAGE_FILE);
+ if (f.exists()) {
+ // TODO: spin a service to copy the restored image to sd/usb storage,
+ // 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));
+ }
+ }
}
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 039c9d7c..bbfec41 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -536,6 +536,9 @@
* {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor
* for content changes, you need to schedule a new JobInfo observing the same URIs
* before you finish execution of the JobService handling the most recent changes.</p>
+ * <p>Because because setting this property is not compatible with periodic or
+ * persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
+ * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
* @param uri The content: URI to monitor.
*/
public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 9f1a9cf0..6d5c81b 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -16,6 +16,7 @@
package android.app.usage;
+import android.annotation.IntDef;
import android.content.Context;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
@@ -29,6 +30,9 @@
import dalvik.system.CloseGuard;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Class providing enumeration over buckets of network usage statistics. {@link NetworkStats} objects
* are returned as results to various queries in {@link NetworkStatsManager}.
@@ -119,6 +123,11 @@
* aggregated (e.g. time or state) some values may be equal across all buckets.
*/
public static class Bucket {
+ /** @hide */
+ @IntDef({STATE_ALL, STATE_DEFAULT, STATE_FOREGROUND})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
/**
* Combined usage across all states.
*/
@@ -149,20 +158,34 @@
*/
public static final int UID_TETHERING = TrafficStats.UID_TETHERING;
+ /** @hide */
+ @IntDef({ROAMING_ALL, ROAMING_NO, ROAMING_YES})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Roaming {}
+
/**
- * Combined usage across all roaming states.
+ * Combined usage across all roaming states. Covers both roaming and non-roaming usage.
*/
public static final int ROAMING_ALL = -1;
/**
- * Usage not accounted for in any other roaming state.
+ * Usage that occurs on a home, non-roaming network.
+ *
+ * <p>Any cellular usage in this bucket was incurred while the device was connected to a
+ * tower owned or operated by the user's wireless carrier, or a tower that the user's
+ * wireless carrier has indicated should be treated as a home network regardless.
+ *
+ * <p>This is also the default value for network types that do not support roaming.
*/
- public static final int ROAMING_DEFAULT = 0x1;
+ public static final int ROAMING_NO = 0x1;
/**
- * Roaming usage.
+ * Usage that occurs on a roaming network.
+ *
+ * <p>Any cellular usage in this bucket as incurred while the device was roaming on another
+ * carrier's network, for which additional charges may apply.
*/
- public static final int ROAMING_ROAMING = 0x2;
+ public static final int ROAMING_YES = 0x2;
/**
* Special TAG value matching any tag.
@@ -185,7 +208,7 @@
private long mTxBytes;
private long mTxPackets;
- private static int convertState(int networkStatsSet) {
+ private static @State int convertState(int networkStatsSet) {
switch (networkStatsSet) {
case android.net.NetworkStats.SET_ALL : return STATE_ALL;
case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT;
@@ -210,11 +233,11 @@
return tag;
}
- private static int convertRoaming(int roaming) {
+ private static @Roaming int convertRoaming(int roaming) {
switch (roaming) {
case android.net.NetworkStats.ROAMING_ALL : return ROAMING_ALL;
- case android.net.NetworkStats.ROAMING_DEFAULT : return ROAMING_DEFAULT;
- case android.net.NetworkStats.ROAMING_ROAMING : return ROAMING_ROAMING;
+ case android.net.NetworkStats.ROAMING_NO: return ROAMING_NO;
+ case android.net.NetworkStats.ROAMING_YES: return ROAMING_YES;
}
return 0;
}
@@ -252,7 +275,7 @@
* </ul>
* @return Usage state.
*/
- public int getState() {
+ public @State int getState() {
return mState;
}
@@ -260,11 +283,11 @@
* Roaming state. One of the following values:<p/>
* <ul>
* <li>{@link #ROAMING_ALL}</li>
- * <li>{@link #ROAMING_DEFAULT}</li>
- * <li>{@link #ROAMING_ROAMING}</li>
+ * <li>{@link #ROAMING_NO}</li>
+ * <li>{@link #ROAMING_YES}</li>
* </ul>
*/
- public int getRoaming() {
+ public @Roaming int getRoaming() {
return mRoaming;
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 4e1b6e0..441f188 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -2440,6 +2440,28 @@
}
}
+ /** {@hide} */
+ public void putCache(Uri key, Bundle value) {
+ try {
+ getContentService().putCache(mContext.getPackageName(), key, value,
+ mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** {@hide} */
+ public Bundle getCache(Uri key) {
+ try {
+ final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key,
+ mContext.getUserId());
+ if (bundle != null) bundle.setClassLoader(mContext.getClassLoader());
+ return bundle;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Returns sampling percentage for a given duration.
*
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b935b25..ccb0552 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2680,6 +2680,7 @@
RADIO_SERVICE,
HARDWARE_PROPERTIES_SERVICE,
//@hide: SOUND_TRIGGER_SERVICE,
+ SHORTCUT_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -2741,7 +2742,7 @@
* <dt> {@link #NETWORK_STATS_SERVICE} ("netstats")
* <dd> A {@link android.app.usage.NetworkStatsManager NetworkStatsManager} for querying network
* usage statistics.
- * <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardwareproperties")
+ * <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties")
* <dd> A {@link android.os.HardwarePropertiesManager} for accessing hardware properties.
* </dl>
*
@@ -3573,7 +3574,24 @@
*
* @see #getSystemService
*/
- public static final String HARDWARE_PROPERTIES_SERVICE = "hardwareproperties";
+ public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
+
+ /**
+ * TODO Javadoc
+ *
+ * @see #getSystemService
+ * @see android.content.pm.ShortcutManager
+ */
+ public static final String SHORTCUT_SERVICE = "shortcut";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.os.health.SystemHealthManager} for accessing system health (battery, power,
+ * memory, etc) metrics.
+ *
+ * @see #getSystemService
+ */
+ public static final String SYSTEM_HEALTH_SERVICE = "systemhealth";
/**
* Determine whether the given permission is allowed for a particular
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 8b471a0..d47e780 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -179,6 +179,8 @@
int userId);
void addStatusChangeListener(int mask, ISyncStatusObserver callback);
-
void removeStatusChangeListener(ISyncStatusObserver callback);
+
+ void putCache(in String packageName, in Uri key, in Bundle value, int userId);
+ Bundle getCache(in String packageName, in Uri key, int userId);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 09fa5e11..182475f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4560,7 +4560,8 @@
/**
* This flag is only used in split-screen multi-window mode. The new activity will be displayed
* adjacent to the one launching it. This can only be used in conjunction with
- * {@link #FLAG_ACTIVITY_NEW_TASK}.
+ * {@link #FLAG_ACTIVITY_NEW_TASK}. Also, setting {@link #FLAG_ACTIVITY_MULTIPLE_TASK} is
+ * required if you want a new instance of an existing activity to be created.
*/
public static final int FLAG_ACTIVITY_LAUNCH_ADJACENT = 0x00001000;
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 14ef61c..5da3c86 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.content.res.Configuration;
+import android.content.res.Configuration.NativeConfig;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Printer;
@@ -495,6 +496,28 @@
@ScreenOrientation
public int screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ CONFIG_MCC,
+ CONFIG_MNC,
+ CONFIG_LOCALE,
+ CONFIG_TOUCHSCREEN,
+ CONFIG_KEYBOARD,
+ CONFIG_KEYBOARD_HIDDEN,
+ CONFIG_NAVIGATION,
+ CONFIG_ORIENTATION,
+ CONFIG_SCREEN_LAYOUT,
+ CONFIG_UI_MODE,
+ CONFIG_SCREEN_SIZE,
+ CONFIG_SMALLEST_SCREEN_SIZE,
+ CONFIG_DENSITY,
+ CONFIG_LAYOUT_DIRECTION,
+ CONFIG_FONT_SCALE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Config {}
+
/**
* Bit in {@link #configChanges} that indicates that the activity
* can itself handle changes to the IMSI MCC. Set from the
@@ -629,7 +652,7 @@
*
* @hide
*/
- public static int activityInfoConfigToNative(int input) {
+ public static @NativeConfig int activityInfoConfigJavaToNative(@Config int input) {
int output = 0;
for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
if ((input & (1 << i)) != 0) {
@@ -644,7 +667,7 @@
*
* @hide
*/
- public static int activityInfoConfigNativeToJava(int input) {
+ public static @Config int activityInfoConfigNativeToJava(@NativeConfig int input) {
int output = 0;
for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
if ((input & CONFIG_NATIVE_BITS[i]) != 0) {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index ad174f6..58d75f7 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -707,6 +707,12 @@
public int uid;
/**
+ * The minimum SDK version this application can run on. It will not run
+ * on earlier versions.
+ */
+ public String minSdkVersion;
+
+ /**
* The minimum SDK version this application targets. It may run on earlier
* versions, but it knows how to work with any new behavior added at this
* version. Will be {@link android.os.Build.VERSION_CODES#CUR_DEVELOPMENT}
@@ -790,7 +796,9 @@
pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
}
}
- pw.println(prefix + "enabled=" + enabled + " targetSdkVersion=" + targetSdkVersion
+ pw.println(prefix + "enabled=" + enabled
+ + " minSdkVersion=" + minSdkVersion
+ + " targetSdkVersion=" + targetSdkVersion
+ " versionCode=" + versionCode);
if ((flags&DUMP_FLAG_DETAILS) != 0) {
if (manageSpaceActivityName != null) {
@@ -884,6 +892,7 @@
deviceEncryptedDataDir = orig.deviceEncryptedDataDir;
credentialEncryptedDataDir = orig.credentialEncryptedDataDir;
uid = orig.uid;
+ minSdkVersion = orig.minSdkVersion;
targetSdkVersion = orig.targetSdkVersion;
versionCode = orig.versionCode;
enabled = orig.enabled;
@@ -938,6 +947,7 @@
dest.writeString(deviceEncryptedDataDir);
dest.writeString(credentialEncryptedDataDir);
dest.writeInt(uid);
+ dest.writeString(minSdkVersion);
dest.writeInt(targetSdkVersion);
dest.writeInt(versionCode);
dest.writeInt(enabled ? 1 : 0);
@@ -992,6 +1002,7 @@
deviceEncryptedDataDir = source.readString();
credentialEncryptedDataDir = source.readString();
uid = source.readInt();
+ minSdkVersion = source.readString();
targetSdkVersion = source.readInt();
versionCode = source.readInt();
enabled = source.readInt() != 0;
diff --git a/core/java/android/content/pm/AppsQueryHelper.java b/core/java/android/content/pm/AppsQueryHelper.java
index e542589..4c01b27 100644
--- a/core/java/android/content/pm/AppsQueryHelper.java
+++ b/core/java/android/content/pm/AppsQueryHelper.java
@@ -171,7 +171,7 @@
return mPackageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES
| PackageManager.GET_DISABLED_COMPONENTS, userId).getList();
} catch (RemoteException e) {
- throw new IllegalStateException("Package manager has died", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -181,9 +181,9 @@
return mPackageManager.queryIntentActivities(intent, null,
PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.GET_UNINSTALLED_PACKAGES,
- userId);
+ userId).getList();
} catch (RemoteException e) {
- throw new IllegalStateException("Package manager has died", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -192,9 +192,9 @@
try {
return mPackageManager.queryIntentServices(intent, null,
PackageManager.GET_META_DATA
- | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId).getList();
} catch (RemoteException e) {
- throw new IllegalStateException("Package manager has died", e);
+ throw e.rethrowFromSystemServer();
}
}
@@ -205,8 +205,7 @@
return mPackageManager.getPackagesHoldingPermissions(new String[]{perm}, 0,
userId).getList();
} catch (RemoteException e) {
- throw new IllegalStateException("Package manager has died", e);
+ throw e.rethrowFromSystemServer();
}
}
-
}
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index cc266c5..7b57872 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -22,9 +22,12 @@
import android.content.pm.IOnAppsChangedListener;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.ParcelFileDescriptor;
+
import java.util.List;
/**
@@ -42,4 +45,19 @@
boolean isPackageEnabled(String packageName, in UserHandle user);
boolean isActivityEnabled(in ComponentName component, in UserHandle user);
ApplicationInfo getApplicationInfo(String packageName, int flags, in UserHandle user);
+
+ ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName,
+ in ComponentName componentName, int flags, in UserHandle user);
+ ParceledListSlice getShortcutInfo(String callingPackage, String packageName, in List<String> ids,
+ in UserHandle user);
+ void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
+ in UserHandle user);
+ boolean startShortcut(String callingPackage, String packageName, String id,
+ in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user);
+
+ int getShortcutIconResId(String callingPackage, in ShortcutInfo shortcut, in UserHandle user);
+ ParcelFileDescriptor getShortcutIconFd(String callingPackage, in ShortcutInfo shortcut,
+ in UserHandle user);
+
+ boolean hasShortcutHostPermission(String callingPackage);
}
diff --git a/core/java/android/content/pm/IOnAppsChangedListener.aidl b/core/java/android/content/pm/IOnAppsChangedListener.aidl
index 1303696..e6525af 100644
--- a/core/java/android/content/pm/IOnAppsChangedListener.aidl
+++ b/core/java/android/content/pm/IOnAppsChangedListener.aidl
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.pm.ParceledListSlice;
import android.os.UserHandle;
/**
@@ -29,4 +30,5 @@
void onPackagesUnavailable(in UserHandle user, in String[] packageNames, boolean replacing);
void onPackagesSuspended(in UserHandle user, in String[] packageNames);
void onPackagesUnsuspended(in UserHandle user, in String[] packageNames);
+ void onShortcutChanged(in UserHandle user, String packageName, in ParceledListSlice shortcuts);
}
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index aee3ba7..2a3fac3 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -29,6 +29,8 @@
ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
ParcelFileDescriptor openRead(String name);
+ void removeSplit(String splitName);
+
void close();
void commit(in IntentSender statusReceiver);
void abandon();
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0389085..c684447 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -71,11 +71,11 @@
PermissionInfo getPermissionInfo(String name, int flags);
- List<PermissionInfo> queryPermissionsByGroup(String group, int flags);
+ ParceledListSlice queryPermissionsByGroup(String group, int flags);
PermissionGroupInfo getPermissionGroupInfo(String name, int flags);
- List<PermissionGroupInfo> getAllPermissionGroups(int flags);
+ ParceledListSlice getAllPermissionGroups(int flags);
ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
@@ -120,6 +120,8 @@
int checkUidSignatures(int uid1, int uid2);
+ List<String> getAllPackages();
+
String[] getPackagesForUid(int uid);
String getNameForUid(int uid);
@@ -138,24 +140,24 @@
boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
- List<ResolveInfo> queryIntentActivities(in Intent intent,
+ ParceledListSlice queryIntentActivities(in Intent intent,
String resolvedType, int flags, int userId);
- List<ResolveInfo> queryIntentActivityOptions(
+ ParceledListSlice queryIntentActivityOptions(
in ComponentName caller, in Intent[] specifics,
in String[] specificTypes, in Intent intent,
String resolvedType, int flags, int userId);
- List<ResolveInfo> queryIntentReceivers(in Intent intent,
+ ParceledListSlice queryIntentReceivers(in Intent intent,
String resolvedType, int flags, int userId);
ResolveInfo resolveService(in Intent intent,
String resolvedType, int flags, int userId);
- List<ResolveInfo> queryIntentServices(in Intent intent,
+ ParceledListSlice queryIntentServices(in Intent intent,
String resolvedType, int flags, int userId);
- List<ResolveInfo> queryIntentContentProviders(in Intent intent,
+ ParceledListSlice queryIntentContentProviders(in Intent intent,
String resolvedType, int flags, int userId);
/**
@@ -189,7 +191,7 @@
* @return A List<applicationInfo> containing one entry for each persistent
* application.
*/
- List<ApplicationInfo> getPersistentApplications(int flags);
+ ParceledListSlice getPersistentApplications(int flags);
ProviderInfo resolveContentProvider(String name, int flags, int userId);
@@ -210,7 +212,7 @@
InstrumentationInfo getInstrumentationInfo(
in ComponentName className, int flags);
- List<InstrumentationInfo> queryInstrumentation(
+ ParceledListSlice queryInstrumentation(
String targetPackage, int flags);
/** @deprecated Use PackageInstaller instead */
@@ -240,12 +242,6 @@
String getInstallerPackageName(in String packageName);
- void addPackageToPreferred(String packageName);
-
- void removePackageFromPreferred(String packageName);
-
- List<PackageInfo> getPreferredPackages(int flags);
-
void resetApplicationPreferences(int userId);
ResolveInfo getLastChosenActivity(in Intent intent,
@@ -386,6 +382,13 @@
*/
void clearApplicationUserData(in String packageName, IPackageDataObserver observer, int userId);
+ /**
+ * Clear the profile data of an application.
+ * @param packageName The package name of the application whose profile data
+ * need to be deleted
+ */
+ void clearApplicationProfileData(in String packageName);
+
/**
* Get package statistics including the code, data and cache size for
* an already installed package
@@ -406,7 +409,7 @@
* Get a list of features that are available on the
* system.
*/
- FeatureInfo[] getSystemAvailableFeatures();
+ ParceledListSlice getSystemAvailableFeatures();
boolean hasSystemFeature(String name, int version);
@@ -480,8 +483,8 @@
void verifyIntentFilter(int id, int verificationCode, in List<String> failedDomains);
int getIntentVerificationStatus(String packageName, int userId);
boolean updateIntentVerificationStatus(String packageName, int status, int userId);
- List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName);
- List<IntentFilter> getAllIntentFilters(String packageName);
+ ParceledListSlice getIntentFilterVerifications(String packageName);
+ ParceledListSlice getAllIntentFilters(String packageName);
boolean setDefaultBrowserPackageName(String packageName, int userId);
String getDefaultBrowserPackageName(int userId);
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
new file mode 100644
index 0000000..8f9dcfc
--- /dev/null
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.content.pm;
+
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
+
+/**
+ * {@hide}
+ */
+interface IShortcutService {
+
+ boolean setDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
+ int userId);
+
+ ParceledListSlice getDynamicShortcuts(String packageName, int userId);
+
+ boolean addDynamicShortcut(String packageName, in ShortcutInfo shortcutInfo, int userId);
+
+ void deleteDynamicShortcut(String packageName, in String shortcutId, int userId);
+
+ void deleteAllDynamicShortcuts(String packageName, int userId);
+
+ ParceledListSlice getPinnedShortcuts(String packageName, int userId);
+
+ boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId);
+
+ int getMaxDynamicShortcutCount(String packageName, int userId);
+
+ int getRemainingCallCount(String packageName, int userId);
+
+ long getRateLimitResetTime(String packageName, int userId);
+
+ int getIconMaxDimensions(String packageName, int userId);
+
+ void resetThrottling(); // system only API for developer opsions
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index e443d50..8d43c44 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -16,23 +16,26 @@
package android.content.pm;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ILauncherApps;
-import android.content.pm.IOnAppsChangedListener;
import android.content.pm.PackageManager.ApplicationInfoFlags;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -148,6 +151,95 @@
*/
public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
}
+
+ /**
+ * Indicates that one or more shortcuts (which may be dynamic and/or pinned)
+ * 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).
+ * @param user The UserHandle of the profile that generated the change.
+ */
+ public void onShortcutsChanged(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+ }
+ }
+
+ /**
+ * Represents a query passed to {@link #getShortcuts(ShortcutQuery, UserHandle)}.
+ */
+ public static class ShortcutQuery {
+ /**
+ * Include dynamic shortcuts in the result.
+ */
+ public static final int FLAG_GET_DYNAMIC = 1 << 0;
+
+ /**
+ * Include pinned shortcuts in the result.
+ */
+ public static final int FLAG_GET_PINNED = 1 << 1;
+
+ /**
+ * Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()} for which
+ * fields are available.
+ */
+ public static final int FLAG_GET_KEY_FIELDS_ONLY = 1 << 2;
+
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ FLAG_GET_DYNAMIC,
+ FLAG_GET_PINNED,
+ FLAG_GET_KEY_FIELDS_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface QueryFlags {}
+
+ long mChangedSince;
+
+ @Nullable
+ String mPackage;
+
+ @Nullable
+ ComponentName mActivity;
+
+ @QueryFlags
+ int mQueryFlags;
+
+ public ShortcutQuery() {
+ }
+
+ /**
+ * If non-zero, returns only shortcuts that have been added or updated since the timestamp,
+ * which is a milliseconds since the Epoch.
+ */
+ public void setChangedSince(long changedSince) {
+ mChangedSince = changedSince;
+ }
+
+ /**
+ * If non-null, returns only shortcuts from the package.
+ */
+ public void setPackage(@Nullable String packageName) {
+ mPackage = packageName;
+ }
+
+ /**
+ * If non-null, returns only shortcuts associated with the activity.
+ */
+ public void setActivity(@Nullable ComponentName activity) {
+ mActivity = activity;
+ }
+
+ /**
+ * Set query options.
+ */
+ public void setQueryFlags(@QueryFlags int queryFlags) {
+ mQueryFlags = queryFlags;
+ }
}
/** @hide */
@@ -302,6 +394,154 @@
}
}
+ /**
+ * Returns whether the caller can access the shortcut information.
+ *
+ * <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
+ * 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
+ * this permission, it does <b>not</b> have to purge pinned shortcut information.
+ */
+ public boolean hasShortcutHostPermission() {
+ try {
+ return mService.hasShortcutHostPermission(mContext.getPackageName());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the IDs of {@link ShortcutInfo}s that match {@code query}.
+ *
+ * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+ * #hasShortcutHostPermission()}.
+ *
+ * @param query result includes shortcuts matching this query.
+ * @param user The UserHandle of the profile.
+ *
+ * @return the IDs of {@link ShortcutInfo}s that match the query.
+ */
+ @Nullable
+ public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query,
+ @NonNull UserHandle user) {
+ try {
+ return mService.getShortcuts(mContext.getPackageName(),
+ query.mChangedSince, query.mPackage, query.mActivity, query.mQueryFlags, user)
+ .getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns {@link ShortcutInfo}s with the given IDs from a package.
+ *
+ * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+ * #hasShortcutHostPermission()}.
+ *
+ * @param packageName The target package.
+ * @param ids IDs of the shortcuts to retrieve.
+ * @param user The UserHandle of the profile.
+ *
+ * @return list of {@link ShortcutInfo} associated with the package.
+ */
+ @Nullable
+ public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
+ @NonNull List<String> ids, @NonNull UserHandle user) {
+ try {
+ return mService.getShortcutInfo(mContext.getPackageName(), packageName, ids, user)
+ .getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Pin shortcuts on a package.
+ *
+ * <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()}.
+ *
+ * @param packageName The target package name.
+ * @param shortcutIds The IDs of the shortcut to be pinned.
+ * @param user The UserHandle of the profile.
+ */
+ public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
+ @NonNull UserHandle user) {
+ try {
+ mService.pinShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the icon resource ID, if {@code shortcut} has one
+ * (i.e. when {@link ShortcutInfo#hasIconResource()} returns {@code true}).
+ *
+ * <p>Callers must be allowed to access the shortcut information, as defined in {@link
+ * #hasShortcutHostPermission()}.
+ *
+ * @param shortcut The target shortcut.
+ * @param user The UserHandle of the profile.
+ */
+ public int getShortcutIconResId(@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
+ try {
+ return mService.getShortcutIconResId(mContext.getPackageName(), shortcut, user);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * 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.
+ * @param user The UserHandle of the profile.
+ */
+ public ParcelFileDescriptor getShortcutIconFd(
+ @NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
+ try {
+ return mService.getShortcutIconFd(mContext.getPackageName(), shortcut, user);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Launches a shortcut.
+ *
+ * <p>Callers 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.
+ */
+ public boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
+ @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
+ @NonNull UserHandle user) {
+ try {
+ return mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
+ sourceBounds, startActivityOptions, user);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
/**
* Registers a callback for changes to packages in current and managed profiles.
@@ -474,6 +714,20 @@
}
}
}
+
+ @Override
+ public void onShortcutChanged(UserHandle user, String packageName,
+ ParceledListSlice shortcuts) {
+ if (DEBUG) {
+ Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName);
+ }
+ final List<ShortcutInfo> list = shortcuts.getList();
+ synchronized (LauncherApps.this) {
+ for (CallbackMessageHandler callback : mCallbacks) {
+ callback.postOnShortcutChanged(packageName, user, list);
+ }
+ }
+ }
};
private static class CallbackMessageHandler extends Handler {
@@ -484,6 +738,7 @@
private static final int MSG_UNAVAILABLE = 5;
private static final int MSG_SUSPENDED = 6;
private static final int MSG_UNSUSPENDED = 7;
+ private static final int MSG_SHORTCUT_CHANGED = 8;
private LauncherApps.Callback mCallback;
@@ -492,6 +747,7 @@
String packageName;
boolean replacing;
UserHandle user;
+ List<ShortcutInfo> shortcuts;
}
public CallbackMessageHandler(Looper looper, LauncherApps.Callback callback) {
@@ -527,6 +783,9 @@
case MSG_UNSUSPENDED:
mCallback.onPackagesUnsuspended(info.packageNames, info.user);
break;
+ case MSG_SHORTCUT_CHANGED:
+ mCallback.onShortcutsChanged(info.packageName, info.shortcuts, info.user);
+ break;
}
}
@@ -582,5 +841,14 @@
info.user = user;
obtainMessage(MSG_UNSUSPENDED, info).sendToTarget();
}
+
+ public void postOnShortcutChanged(String packageName, UserHandle user,
+ List<ShortcutInfo> shortcuts) {
+ CallbackInfo info = new CallbackInfo();
+ info.packageName = packageName;
+ info.user = user;
+ info.shortcuts = shortcuts;
+ obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();
+ }
}
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 700a40d..6f2786a1 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -795,6 +795,27 @@
}
/**
+ * Removes a split.
+ * <p>
+ * Split removals occur prior to adding new APKs. If upgrading a feature
+ * split, it is not expected nor desirable to remove the split prior to
+ * upgrading.
+ * <p>
+ * When split removal is bundled with new APKs, the packageName must be
+ * identical.
+ */
+ public void removeSplit(@NonNull String splitName) throws IOException {
+ try {
+ mSession.removeSplit(splitName);
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Attempt to commit everything staged in this session. This may require
* user intervention, and so it may not happen immediately. The final
* result of the commit will be reported through the given callback.
@@ -979,8 +1000,8 @@
}
/**
- * Optionally set the URI where this package was downloaded from. Used for
- * verification purposes.
+ * Optionally set the URI where this package was downloaded from. This is
+ * informational and may be used as a signal for anti-malware purposes.
*
* @see Intent#EXTRA_ORIGINATING_URI
*/
@@ -989,7 +1010,8 @@
}
/**
- * Sets the UID that initiated package installation. Used for verification purposes.
+ * Sets the UID that initiated package installation. This is informational
+ * and may be used as a signal for anti-malware purposes.
*
* @see PackageManager#EXTRA_VERIFICATION_INSTALLER_UID
*/
@@ -998,8 +1020,8 @@
}
/**
- * Optionally set the URI that referred you to install this package. Used
- * for verification purposes.
+ * Optionally set the URI that referred you to install this package. This is
+ * informational and may be used as a signal for anti-malware purposes.
*
* @see Intent#EXTRA_REFERRER
*/
@@ -1031,6 +1053,16 @@
}
/** {@hide} */
+ @SystemApi
+ public void setAllowDowngrade(boolean allowDowngrade) {
+ if (allowDowngrade) {
+ installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ } else {
+ installFlags &= ~PackageManager.INSTALL_ALLOW_DOWNGRADE;
+ }
+ }
+
+ /** {@hide} */
public void setInstallFlagsExternal() {
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c1a559e..e1e8a07 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1312,6 +1312,7 @@
*
* @hide
*/
+ @SystemApi
public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1;
/**
@@ -1321,6 +1322,7 @@
*
* @hide
*/
+ @SystemApi
public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1;
/**
@@ -1594,9 +1596,9 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: If this feature is supported, the Vulkan native API will enumerate
- * at least one {@code VkPhysicalDevice}, and the feature version will indicate what
- * level of optional hardware features limits it supports.
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the Vulkan native API
+ * will enumerate at least one {@code VkPhysicalDevice}, and the feature version will indicate
+ * what level of optional hardware features limits it supports.
* <p>
* Level 0 includes the base Vulkan requirements as well as:
* <ul><li>{@code VkPhysicalDeviceFeatures::textureCompressionETC2}</li></ul>
@@ -1621,7 +1623,7 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The version of this feature indicates the highest
+ * {@link #hasSystemFeature(String, int)}: The version of this feature indicates the highest
* {@code VkPhysicalDeviceProperties::apiVersion} supported by the physical devices that support
* the hardware level indicated by {@link #FEATURE_VULKAN_HARDWARE_LEVEL}. The feature version
* uses the same encoding as Vulkan version numbers:
@@ -4731,8 +4733,8 @@
/**
* Allows a package listening to the
- * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION intent filter verification
- * broadcast} to respond to the package manager. The response must include
+ * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION} intent filter verification
+ * broadcast to respond to the package manager. The response must include
* the {@code verificationCode} which is one of
* {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS} or
* {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
@@ -4741,7 +4743,7 @@
* {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
* @param verificationCode either {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS}
* or {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
- * @param outFailedDomains a list of failed domains if the verificationCode is
+ * @param failedDomains a list of failed domains if the verificationCode is
* {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}, otherwise null;
* @throws SecurityException if the caller does not have the
* INTENT_FILTER_VERIFICATION_AGENT permission.
@@ -4750,7 +4752,7 @@
*/
@SystemApi
public abstract void verifyIntentFilter(int verificationId, int verificationCode,
- List<String> outFailedDomains);
+ List<String> failedDomains);
/**
* Get the status of a Domain Verification Result for an IntentFilter. This is
@@ -5365,6 +5367,9 @@
* will be hidden, the application will not show up in recents, will not be able to show
* toasts or dialogs or ring the device.
*
+ * <p>The package must already be installed. If the package is uninstalled while suspended
+ * the package will no longer be suspended.
+ *
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true} than the packages will be suspended, if set to
* {@code false} the packages will be unsuspended.
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 89f2fc4..13ebb82 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.ComponentName;
import android.content.pm.PackageManager.NameNotFoundException;
import java.util.List;
@@ -140,4 +141,10 @@
* found on the system.
*/
public abstract ApplicationInfo getApplicationInfo(String packageName, int userId);
+
+ /**
+ * Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
+ */
+ public abstract ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+ int userId);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0588a9d..7d7be9a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -121,6 +121,10 @@
private static final int MAX_PACKAGES_PER_APK = 5;
+ public static final int APK_SIGNING_UNKNOWN = 0;
+ public static final int APK_SIGNING_V1 = 1;
+ public static final int APK_SIGNING_V2 = 2;
+
// TODO: switch outError users to PackageParserException
// TODO: refactor "codePath" to "apkPath"
@@ -1058,12 +1062,24 @@
return pkg;
}
+ public static int getApkSigningVersion(Package pkg) {
+ try {
+ if (ApkSignatureSchemeV2Verifier.hasSignature(pkg.baseCodePath)) {
+ return APK_SIGNING_V2;
+ }
+ return APK_SIGNING_V1;
+ } catch (IOException e) {
+ }
+ return APK_SIGNING_UNKNOWN;
+ }
+
/**
* Collect certificates from all the APKs described in the given package,
* populating {@link Package#mSignatures}. Also asserts that all APK
* contents are signed correctly and consistently.
*/
- public static void collectCertificates(Package pkg, int parseFlags) throws PackageParserException {
+ public static void collectCertificates(Package pkg, int parseFlags)
+ throws PackageParserException {
collectCertificatesInternal(pkg, parseFlags);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
@@ -1074,7 +1090,8 @@
}
}
- private static void collectCertificatesInternal(Package pkg, int parseFlags) throws PackageParserException {
+ private static void collectCertificatesInternal(Package pkg, int parseFlags)
+ throws PackageParserException {
pkg.mCertificates = null;
pkg.mSignatures = null;
pkg.mSigningKeys = null;
@@ -1507,6 +1524,7 @@
childPkg.baseRevisionCode = parentPkg.baseRevisionCode;
childPkg.mVersionName = parentPkg.mVersionName;
childPkg.applicationInfo.targetSdkVersion = parentPkg.applicationInfo.targetSdkVersion;
+ childPkg.applicationInfo.minSdkVersion = parentPkg.applicationInfo.minSdkVersion;
childPkg = parseBaseApkCommon(childPkg, CHILD_PACKAGE_TAGS, res, parser, flags, outError);
if (childPkg == null) {
@@ -1837,10 +1855,16 @@
com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
- targetCode = minCode = val.string.toString();
+ targetCode = val.string.toString();
+ if (minCode == null) {
+ minCode = targetCode;
+ }
} else {
// If it's not a string, it's an integer.
targetVers = val.data;
+ if (minVers == 0) {
+ minVers = targetVers;
+ }
}
}
@@ -1866,11 +1890,14 @@
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
+ pkg.applicationInfo.minSdkVersion = minCode;
} else if (minVers > SDK_VERSION) {
outError[0] = "Requires newer sdk version #" + minVers
+ " (current version is #" + SDK_VERSION + ")";
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
+ } else {
+ pkg.applicationInfo.minSdkVersion = Integer.toString(minVers);
}
if (targetCode != null) {
diff --git a/core/java/android/content/pm/ShortcutInfo.aidl b/core/java/android/content/pm/ShortcutInfo.aidl
new file mode 100644
index 0000000..08e1873
--- /dev/null
+++ b/core/java/android/content/pm/ShortcutInfo.aidl
@@ -0,0 +1,18 @@
+/*
+ * 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.content.pm;
+
+parcelable ShortcutInfo;
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
new file mode 100644
index 0000000..e41136c
--- /dev/null
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -0,0 +1,766 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+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 com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * TODO Enhance javadoc
+ *
+ * Represents a shortcut form an application.
+ *
+ * Notes...
+ * - If an {@link Icon} is of a resource, then we'll just persist the package name and resource ID.
+ *
+ * Otherwise, the bitmap will be fetched when it's registered to ShortcutManager, then *shrunk*
+ * if necessary, and persisted.
+ *
+ * We will disallow byte[] icons, because they can easily go over binder size limit.
+ *
+ * TODO Move save/load to this class
+ */
+public class ShortcutInfo implements Parcelable {
+ /* @hide */
+ public static final int FLAG_DYNAMIC = 1 << 0;
+
+ /* @hide */
+ public static final int FLAG_PINNED = 1 << 1;
+
+ /* @hide */
+ public static final int FLAG_HAS_ICON_RES = 1 << 2;
+
+ /* @hide */
+ public static final int FLAG_HAS_ICON_FILE = 1 << 3;
+
+ /* @hide */
+ public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
+
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ FLAG_DYNAMIC,
+ FLAG_PINNED,
+ FLAG_HAS_ICON_RES,
+ FLAG_HAS_ICON_FILE,
+ FLAG_KEY_FIELDS_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ShortcutFlags {}
+
+ // Cloning options.
+
+ /* @hide */
+ private static final int CLONE_REMOVE_ICON = 1 << 0;
+
+ /* @hide */
+ private static final int CLONE_REMOVE_INTENT = 1 << 1;
+
+ /* @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_FOR_LAUNCHER = CLONE_REMOVE_ICON | CLONE_REMOVE_INTENT;
+
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ CLONE_REMOVE_ICON,
+ CLONE_REMOVE_INTENT,
+ CLONE_REMOVE_NON_KEY_INFO,
+ CLONE_REMOVE_FOR_CREATOR,
+ CLONE_REMOVE_FOR_LAUNCHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CloneFlags {}
+
+ private final String mId;
+
+ @NonNull
+ private final String mPackageName;
+
+ @Nullable
+ private ComponentName mActivityComponent;
+
+ @Nullable
+ private Icon mIcon;
+
+ @NonNull
+ private String mTitle;
+
+ /**
+ * Intent *with extras removed*.
+ */
+ @NonNull
+ private Intent mIntent;
+
+ /**
+ * Extras for the intent.
+ */
+ @NonNull
+ private PersistableBundle mIntentPersistableExtras;
+
+ private int mWeight;
+
+ @Nullable
+ private PersistableBundle mExtras;
+
+ private long mLastChangedTimestamp;
+
+ // Internal use only.
+ @ShortcutFlags
+ private int mFlags;
+
+ // Internal use only.
+ private int mIconResourceId;
+
+ // Internal use only.
+ @Nullable
+ private String mBitmapPath;
+
+ private ShortcutInfo(Builder b) {
+ mId = Preconditions.checkStringNotEmpty(b.mId, "Shortcut ID must be provided");
+
+ // Note we can't do other null checks here because SM.updateShortcuts() takes partial
+ // information.
+ mPackageName = b.mContext.getPackageName();
+ mActivityComponent = b.mActivityComponent;
+ mIcon = b.mIcon;
+ mTitle = b.mTitle;
+ 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;
+ mExtras = b.mExtras;
+ updateTimestamp();
+ }
+
+ /**
+ * Throws if any of the mandatory fields is not set.
+ *
+ * @hide
+ */
+ public void enforceMandatoryFields() {
+ Preconditions.checkStringNotEmpty(mTitle, "Shortcut title must be provided");
+ Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided");
+ }
+
+ /**
+ * Copy constructor.
+ */
+ private ShortcutInfo(ShortcutInfo source, @CloneFlags int cloneFlags) {
+ mId = source.mId;
+ mPackageName = source.mPackageName;
+ mFlags = source.mFlags;
+ mLastChangedTimestamp = source.mLastChangedTimestamp;
+
+ if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
+ mActivityComponent = source.mActivityComponent;
+
+ if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
+ mIcon = source.mIcon;
+ mBitmapPath = source.mBitmapPath;
+ }
+
+ mTitle = source.mTitle;
+ if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
+ mIntent = source.mIntent;
+ mIntentPersistableExtras = source.mIntentPersistableExtras;
+ }
+ mWeight = source.mWeight;
+ mExtras = source.mExtras;
+ mIconResourceId = source.mIconResourceId;
+ } else {
+ // Set this bit.
+ mFlags |= FLAG_KEY_FIELDS_ONLY;
+ }
+ }
+
+ /**
+ * Copy a {@link ShortcutInfo}, optionally removing fields.
+ * @hide
+ */
+ public ShortcutInfo clone(@CloneFlags int cloneFlags) {
+ return new ShortcutInfo(this, cloneFlags);
+ }
+
+ /**
+ * Copy non-null/zero fields from another {@link ShortcutInfo}. Only "public" information
+ * will be overwritten. The timestamp will be updated.
+ *
+ * - Flags will not change
+ * - mBitmapPath will not change
+ * - Current time will be set to timestamp
+ *
+ * @hide
+ */
+ public void copyNonNullFieldsFrom(ShortcutInfo source) {
+ Preconditions.checkState(mId.equals(source.mId), "ID must match");
+ Preconditions.checkState(mPackageName.equals(source.mPackageName),
+ "Package name must match");
+
+ if (source.mActivityComponent != null) {
+ mActivityComponent = source.mActivityComponent;
+ }
+
+ if (source.mIcon != null) {
+ mIcon = source.mIcon;
+ }
+ if (source.mTitle != null) {
+ mTitle = source.mTitle;
+ }
+ if (source.mIntent != null) {
+ mIntent = source.mIntent;
+ mIntentPersistableExtras = source.mIntentPersistableExtras;
+ }
+ if (source.mWeight != 0) {
+ mWeight = source.mWeight;
+ }
+ if (source.mExtras != null) {
+ mExtras = source.mExtras;
+ }
+
+ updateTimestamp();
+ }
+
+ /**
+ * @hide
+ */
+ public static Icon validateIcon(Icon icon) {
+ switch (icon.getType()) {
+ case Icon.TYPE_RESOURCE:
+ case Icon.TYPE_BITMAP:
+ break; // OK
+ case Icon.TYPE_URI:
+ if (ContentResolver.SCHEME_CONTENT.equals(icon.getUri().getScheme())) {
+ break;
+ }
+ // Note "file:" is not supported, because depending on the path, system server
+ // cannot access it. // TODO Revisit "file:" icon support
+
+ // fall through
+ default:
+ throw getInvalidIconException();
+ }
+ if (icon.hasTint()) {
+ // TODO support it
+ throw new IllegalArgumentException("Icons with tints are not supported");
+ }
+
+ return icon;
+ }
+
+ /** @hide */
+ public static IllegalArgumentException getInvalidIconException() {
+ return new IllegalArgumentException("Unsupported icon type:"
+ +" only bitmap, resource and content URI are supported");
+ }
+
+ /**
+ * Builder class for {@link ShortcutInfo} objects.
+ */
+ public static class Builder {
+ private final Context mContext;
+
+ private String mId;
+
+ private ComponentName mActivityComponent;
+
+ private Icon mIcon;
+
+ private String mTitle;
+
+ private Intent mIntent;
+
+ private int mWeight;
+
+ private PersistableBundle mExtras;
+
+ /** Constructor. */
+ public Builder(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Sets the ID of the shortcut. This is a mandatory field.
+ */
+ @NonNull
+ public Builder setId(@NonNull String id) {
+ mId = Preconditions.checkStringNotEmpty(id, "id");
+ 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.
+ */
+ @NonNull
+ public Builder setActivityComponent(@NonNull ComponentName activityComponent) {
+ mActivityComponent = Preconditions.checkNotNull(activityComponent, "activityComponent");
+ return this;
+ }
+
+ /**
+ * Optionally sets an icon.
+ *
+ * <ul>
+ * <li>Tints are not supported.
+ * <li>Bitmaps, resources and "content:" URIs are supported.
+ * <li>"content:" URI will be fetched when a shortcut is registered to
+ * {@link ShortcutManager}. Changing the content from the same URI later will
+ * not be reflected to launcher icons.
+ * </ul>
+ *
+ * <p>For performance reasons, icons will <b>NOT</b> be available on instances
+ * returned by {@link ShortcutManager} or {@link LauncherApps}. Launcher applications
+ * need to use {@link LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle)}
+ * and {@link LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)}.
+ */
+ @NonNull
+ public Builder setIcon(Icon icon) {
+ mIcon = validateIcon(icon);
+ return this;
+ }
+
+ /**
+ * Sets the title of a shortcut. This is a mandatory field.
+ */
+ @NonNull
+ public Builder setTitle(@NonNull String title) {
+ mTitle = Preconditions.checkStringNotEmpty(title, "title");
+ return this;
+ }
+
+ /**
+ * Sets the intent of a shortcut. This is a mandatory field. The extras must only contain
+ * persistable information. (See {@link PersistableBundle}).
+ */
+ @NonNull
+ public Builder setIntent(@NonNull Intent intent) {
+ mIntent = Preconditions.checkNotNull(intent, "intent");
+ return this;
+ }
+
+ /**
+ * 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.
+ */
+ @NonNull
+ public Builder setWeight(int weight) {
+ mWeight = weight;
+ 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()}.
+ */
+ @NonNull
+ public Builder setExtras(@NonNull PersistableBundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Creates a {@link ShortcutInfo} instance.
+ */
+ @NonNull
+ public ShortcutInfo build() {
+ return new ShortcutInfo(this);
+ }
+ }
+
+ /**
+ * Return the ID of the shortcut.
+ */
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Return the package name of the creator application.
+ */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Return the target activity, which may be null, in which case the shortcut is not associated
+ * with a specific activity.
+ */
+ @Nullable
+ public ComponentName getActivityComponent() {
+ return mActivityComponent;
+ }
+
+ /**
+ * 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.
+ *
+ * @hide
+ */
+ @Nullable
+ public Icon getIcon() {
+ 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.
+ */
+ @Nullable
+ public String getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Return the intent.
+ *
+ * <p>All shortcuts must have an intent, but this method will return null when
+ * {@link #hasKeyFieldsOnly()} is true.
+ */
+ @Nullable
+ public Intent getIntent() {
+ if (mIntent == null) {
+ return null;
+ }
+ final Intent intent = new Intent(mIntent);
+ intent.replaceExtras(
+ mIntentPersistableExtras != null ? new Bundle(mIntentPersistableExtras) : null);
+ return intent;
+ }
+
+ /**
+ * Return "raw" intent, which is the original intent without the extras.
+ * @hide
+ */
+ @Nullable
+ public Intent getIntentNoExtras() {
+ return mIntent;
+ }
+
+ /**
+ * The extras in the intent. We convert extras into {@link PersistableBundle} so we can
+ * persist them.
+ * @hide
+ */
+ @Nullable
+ public PersistableBundle getIntentPersistableExtras() {
+ return mIntentPersistableExtras;
+ }
+
+ /**
+ * Return the weight of a shortcut, which will be used by Launcher for sorting.
+ * The larger the weight, the more "important" a shortcut is.
+ */
+ public int getWeight() {
+ return mWeight;
+ }
+
+ /**
+ * Optional values that application can set.
+ */
+ @Nullable
+ public PersistableBundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Last time when any of the fields was updated.
+ */
+ public long getLastChangedTimestamp() {
+ return mLastChangedTimestamp;
+ }
+
+ /** @hide */
+ @ShortcutFlags
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /** @hide*/
+ public void setFlags(@ShortcutFlags int flags) {
+ mFlags = flags;
+ }
+
+ /** @hide*/
+ public void addFlags(@ShortcutFlags int flags) {
+ mFlags |= flags;
+ }
+
+ /** @hide*/
+ public void clearFlags(@ShortcutFlags int flags) {
+ mFlags &= ~flags;
+ }
+
+ /** @hide*/
+ public boolean hasFlags(@ShortcutFlags int flags) {
+ return (mFlags & flags) == flags;
+ }
+
+ /** Return whether a shortcut is dynamic. */
+ public boolean isDynamic() {
+ return hasFlags(FLAG_DYNAMIC);
+ }
+
+ /** Return whether a shortcut is pinned. */
+ public boolean isPinned() {
+ return hasFlags(FLAG_PINNED);
+ }
+
+ /**
+ * Return whether a shortcut's icon is a resource in the owning package.
+ *
+ * @see LauncherApps#getShortcutIconResId(ShortcutInfo, UserHandle)
+ */
+ public boolean hasIconResource() {
+ return hasFlags(FLAG_HAS_ICON_RES);
+ }
+
+ /**
+ * Return whether a shortcut's icon is stored as a file.
+ *
+ * @see LauncherApps#getShortcutIconFd(ShortcutInfo, UserHandle)
+ */
+ public boolean hasIconFile() {
+ return hasFlags(FLAG_HAS_ICON_FILE);
+ }
+
+ /**
+ * Return whether a shortcut only contains "key" information only or not. If true, only the
+ * following fields are available.
+ * <ul>
+ * <li>{@link #getId()}
+ * <li>{@link #getPackageName()}
+ * <li>{@link #getLastChangedTimestamp()}
+ * <li>{@link #isDynamic()}
+ * <li>{@link #isPinned()}
+ * <li>{@link #hasIconResource()}
+ * <li>{@link #hasIconFile()}
+ * </ul>
+ */
+ public boolean hasKeyFieldsOnly() {
+ return hasFlags(FLAG_KEY_FIELDS_ONLY);
+ }
+
+ /** @hide */
+ public void updateTimestamp() {
+ mLastChangedTimestamp = System.currentTimeMillis();
+ }
+
+ /** @hide */
+ // VisibleForTesting
+ public void setTimestamp(long value) {
+ mLastChangedTimestamp = value;
+ }
+
+ /** @hide */
+ public void clearIcon() {
+ mIcon = null;
+ }
+
+ /** @hide */
+ public void setIconResourceId(int iconResourceId) {
+ mIconResourceId = iconResourceId;
+ }
+
+ /** @hide */
+ public int getIconResourceId() {
+ return mIconResourceId;
+ }
+
+ /** @hide */
+ public String getBitmapPath() {
+ return mBitmapPath;
+ }
+
+ /** @hide */
+ public void setBitmapPath(String bitmapPath) {
+ mBitmapPath = bitmapPath;
+ }
+
+ private ShortcutInfo(Parcel source) {
+ final ClassLoader cl = getClass().getClassLoader();
+
+ mId = source.readString();
+ mPackageName = source.readString();
+ mActivityComponent = source.readParcelable(cl);
+ mIcon = source.readParcelable(cl);
+ mTitle = source.readString();
+ mIntent = source.readParcelable(cl);
+ mIntentPersistableExtras = source.readParcelable(cl);
+ mWeight = source.readInt();
+ mExtras = source.readParcelable(cl);
+ mLastChangedTimestamp = source.readLong();
+ mFlags = source.readInt();
+ mIconResourceId = source.readInt();
+ mBitmapPath = source.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeString(mPackageName);
+ dest.writeParcelable(mActivityComponent, flags);
+ dest.writeParcelable(mIcon, flags);
+ dest.writeString(mTitle);
+ dest.writeParcelable(mIntent, flags);
+ dest.writeParcelable(mIntentPersistableExtras, flags);
+ dest.writeInt(mWeight);
+ dest.writeParcelable(mExtras, flags);
+ dest.writeLong(mLastChangedTimestamp);
+ dest.writeInt(mFlags);
+ dest.writeInt(mIconResourceId);
+ dest.writeString(mBitmapPath);
+ }
+
+ public static final Creator<ShortcutInfo> CREATOR =
+ new Creator<ShortcutInfo>() {
+ public ShortcutInfo createFromParcel(Parcel source) {
+ return new ShortcutInfo(source);
+ }
+ public ShortcutInfo[] newArray(int size) {
+ return new ShortcutInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Return a string representation, intended for logging. Some fields will be retracted.
+ */
+ @Override
+ public String toString() {
+ return toStringInner(/* secure =*/ true, /* includeInternalData =*/ false);
+ }
+
+ /** @hide */
+ public String toInsecureString() {
+ return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true);
+ }
+
+ private String toStringInner(boolean secure, boolean includeInternalData) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("ShortcutInfo {");
+
+ sb.append("id=");
+ sb.append(secure ? "***" : mId);
+
+ sb.append(", packageName=");
+ sb.append(mPackageName);
+
+ if (isDynamic()) {
+ sb.append(", dynamic");
+ }
+ if (isPinned()) {
+ sb.append(", pinned");
+ }
+
+ sb.append(", activity=");
+ sb.append(mActivityComponent);
+
+ sb.append(", title=");
+ sb.append(secure ? "***" : mTitle);
+
+ sb.append(", icon=");
+ sb.append(mIcon);
+
+ sb.append(", weight=");
+ sb.append(mWeight);
+
+ sb.append(", timestamp=");
+ sb.append(mLastChangedTimestamp);
+
+ sb.append(", intent=");
+ sb.append(mIntent);
+
+ sb.append(", intentExtras=");
+ sb.append(secure ? "***" : mIntentPersistableExtras);
+
+ sb.append(", extras=");
+ sb.append(mExtras);
+
+ sb.append(", flags=");
+ sb.append(mFlags);
+
+ if (includeInternalData) {
+
+ sb.append(", iconRes=");
+ sb.append(mIconResourceId);
+
+ sb.append(", bitmapPath=");
+ sb.append(mBitmapPath);
+ }
+
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /** @hide */
+ public ShortcutInfo(String id, String packageName, ComponentName activityComponent,
+ Icon icon, String title, Intent intent, PersistableBundle intentPersistableExtras,
+ int weight, PersistableBundle extras, long lastChangedTimestamp,
+ int flags, int iconResId, String bitmapPath) {
+ mId = id;
+ mPackageName = packageName;
+ mActivityComponent = activityComponent;
+ mIcon = icon;
+ mTitle = title;
+ mIntent = intent;
+ mIntentPersistableExtras = intentPersistableExtras;
+ mWeight = weight;
+ mExtras = extras;
+ mLastChangedTimestamp = lastChangedTimestamp;
+ mFlags = flags;
+ mIconResourceId = iconResId;
+ mBitmapPath = bitmapPath;
+ }
+}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
new file mode 100644
index 0000000..b247f65
--- /dev/null
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -0,0 +1,258 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * TODO Enhance javadoc
+ *
+ * {@link ShortcutManager} manages shortcuts created by applications.
+ *
+ * <h3>Dynamic shortcuts and pinned shortcuts</h3>
+ *
+ * An application can publish shortcuts with {@link #setDynamicShortcuts(List)} and
+ * {@link #addDynamicShortcut(ShortcutInfo)}. There can be at most
+ * {@link #getMaxDynamicShortcutCount()} number of dynamic shortcuts at a time from the same
+ * application.
+ * A dynamic shortcut can be deleted with {@link #deleteDynamicShortcut(String)}, and apps
+ * can also use {@link #deleteAllDynamicShortcuts()} 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>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>
+ *
+ * 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.
+ *
+ * <h3>Rate limiting</h3>
+ *
+ * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcut(ShortcutInfo)},
+ * and {@link #updateShortcuts(List)} 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.
+ *
+ * <p>An applications can use {@link #getRateLimitResetTime()} to get the next reset time.
+ *
+ * <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.
+ *
+ * TODO: Define a Broadcast to let apps update shortcuts on a restored device.
+ *
+ * <h3>APIs for launcher</h3>
+ *
+ * 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)}.
+ */
+public class ShortcutManager {
+ private static final String TAG = "ShortcutManager";
+
+ private final Context mContext;
+ private final IShortcutService mService;
+
+ /**
+ * @hide
+ */
+ public ShortcutManager(Context context, IShortcutService service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Publish a list of shortcuts. All existing dynamic shortcuts from the caller application
+ * will be replaced.
+ *
+ * <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.
+ */
+ public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
+ try {
+ return mService.setDynamicShortcuts(mContext.getPackageName(),
+ new ParceledListSlice(shortcutInfoList), injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return all dynamic shortcuts from the caller application. The number of result items
+ * will not exceed the value returned by {@link #getMaxDynamicShortcutCount()}.
+ */
+ @NonNull
+ public List<ShortcutInfo> getDynamicShortcuts() {
+ try {
+ return mService.getDynamicShortcuts(mContext.getPackageName(), injectMyUserId())
+ .getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Publish a single dynamic shortcut. If there's already dynamic or pinned shortcuts with
+ * the same ID, they will all be 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.
+ */
+ public boolean addDynamicShortcut(@NonNull ShortcutInfo shortcutInfo) {
+ try {
+ return mService.addDynamicShortcut(
+ mContext.getPackageName(), shortcutInfo, injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Delete a single dynamic shortcut by ID.
+ */
+ public void deleteDynamicShortcut(@NonNull String shortcutId) {
+ try {
+ mService.deleteDynamicShortcut(mContext.getPackageName(), shortcutId, injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Delete all dynamic shortcuts from the caller application.
+ */
+ public void deleteAllDynamicShortcuts() {
+ try {
+ mService.deleteAllDynamicShortcuts(mContext.getPackageName(), injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return all pinned shortcuts from the caller application.
+ */
+ @NonNull
+ public List<ShortcutInfo> getPinnedShortcuts() {
+ try {
+ return mService.getPinnedShortcuts(mContext.getPackageName(), injectMyUserId())
+ .getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Update all existing shortcuts with the same IDs. Shortcuts may be pinned and/or dynamic.
+ *
+ * <p>This API will be rate-limited.
+ *
+ * @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
+ */
+ public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) {
+ try {
+ return mService.updateShortcuts(mContext.getPackageName(),
+ new ParceledListSlice(shortcutInfoList), injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the max number of dynamic shortcuts that each application can have at a time.
+ */
+ public int getMaxDynamicShortcutCount() {
+ try {
+ return mService.getMaxDynamicShortcutCount(mContext.getPackageName(), injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the number of times the caller application can call the rate-limited APIs
+ * before the rate limit counter is reset.
+ *
+ * @see #getRateLimitResetTime()
+ */
+ public int getRemainingCallCount() {
+ try {
+ return mService.getRemainingCallCount(mContext.getPackageName(), injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return when the rate limit count will be reset next time, in milliseconds since the epoch.
+ *
+ * @see #getRemainingCallCount()
+ * @see System#currentTimeMillis()
+ */
+ public long getRateLimitResetTime() {
+ try {
+ return mService.getRateLimitResetTime(mContext.getPackageName(), injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the max width and height for icons, in pixels.
+ */
+ public int getIconMaxDimensions() {
+ try {
+ return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @hide injection point */
+ @VisibleForTesting
+ protected int injectMyUserId() {
+ return UserHandle.myUserId();
+ }
+}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
new file mode 100644
index 0000000..7c764aa
--- /dev/null
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -0,0 +1,68 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+
+import java.util.List;
+
+/**
+ * Entry points used by {@link LauncherApps}.
+ *
+ * <p>No permission / argument checks will be performed inside.
+ * Callers must check the calling app permission and the calling package name.
+ * @hide
+ */
+public abstract class ShortcutServiceInternal {
+ public interface ShortcutChangeListener {
+ void onShortcutChanged(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @UserIdInt int userId);
+ }
+
+ public abstract List<ShortcutInfo>
+ getShortcuts(@NonNull String callingPackage, long changedSince,
+ @Nullable String packageName, @Nullable ComponentName componentName,
+ @ShortcutQuery.QueryFlags int flags,
+ int userId);
+
+ public abstract List<ShortcutInfo>
+ getShortcutInfo(@NonNull String callingPackage,
+ @NonNull String packageName, @Nullable List<String> ids, int userId);
+
+ public abstract void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
+ @NonNull List<String> shortcutIds, int userId);
+
+ public abstract Intent createShortcutIntent(@NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId);
+
+ public abstract void addListener(@NonNull ShortcutChangeListener listener);
+
+ public abstract int getShortcutIconResId(@NonNull String callingPackage,
+ @NonNull ShortcutInfo shortcut, int userId);
+
+ public abstract ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
+ @NonNull ShortcutInfo shortcut, int userId);
+
+ public abstract boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId);
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 7b0b98d..4ad86f7 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
+import android.content.res.Configuration.NativeConfig;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.util.SparseArray;
@@ -796,7 +797,10 @@
/*package*/ static final int STYLE_DATA = 1;
/*package*/ static final int STYLE_ASSET_COOKIE = 2;
/*package*/ static final int STYLE_RESOURCE_ID = 3;
- /*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+
+ /* Offset within typed data array for native changingConfigurations. */
+ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+
/*package*/ static final int STYLE_DENSITY = 5;
/*package*/ native static final boolean applyStyle(long theme,
int defStyleAttr, int defStyleRes, long xmlParser,
@@ -845,7 +849,7 @@
TypedValue outValue,
boolean resolve);
/*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix);
- /*package*/ native static final int getThemeChangingConfigurations(long theme);
+ /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme);
private native final long openXmlAssetNative(int cookie, String fileName);
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 9e1b312..fb5bfd3 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -19,6 +19,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources.Theme;
import android.graphics.Color;
@@ -82,7 +83,7 @@
private ColorStateListFactory mFactory;
private int[][] mThemeAttrs;
- private int mChangingConfigurations;
+ private @Config int mChangingConfigurations;
private int[][] mStateSpecs;
private int[] mColors;
@@ -251,7 +252,7 @@
int depth;
int type;
- int changingConfigurations = 0;
+ @Config int changingConfigurations = 0;
int defaultColor = DEFAULT_COLOR;
boolean hasUnresolvedAttrs = false;
@@ -440,8 +441,8 @@
*
* @see android.content.pm.ActivityInfo
*/
- public int getChangingConfigurations() {
- return mChangingConfigurations;
+ public @Config int getChangingConfigurations() {
+ return super.getChangingConfigurations() | mChangingConfigurations;
}
private int modulateColorAlpha(int baseColor, float alphaMod) {
@@ -620,7 +621,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mSrc.mChangingConfigurations;
}
diff --git a/core/java/android/content/res/ComplexColor.java b/core/java/android/content/res/ComplexColor.java
index d96ec62..b297764 100644
--- a/core/java/android/content/res/ComplexColor.java
+++ b/core/java/android/content/res/ComplexColor.java
@@ -25,6 +25,8 @@
* {@link android.content.res.ColorStateList} or {@link android.content.res.GradientColor}
*/
public abstract class ComplexColor {
+ private int mChangingConfigurations;
+
/**
* @return {@code true} if this ComplexColor changes color based on state, {@code false}
* otherwise.
@@ -52,4 +54,24 @@
* @hide only for resource preloading
*/
public abstract ComplexColor obtainForTheme(Theme t);
+
+ /**
+ * @hide only for resource preloading
+ */
+ final void setBaseChangingConfigurations(int changingConfigurations) {
+ mChangingConfigurations = changingConfigurations;
+ }
+
+ /**
+ * Returns a mask of the configuration parameters for which this color
+ * may change, requiring that it be re-created.
+ *
+ * @return a mask of the changing configuration parameters, as defined by
+ * {@link android.content.pm.ActivityInfo}
+ *
+ * @see android.content.pm.ActivityInfo
+ */
+ public int getChangingConfigurations() {
+ return mChangingConfigurations;
+ }
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index be4f895..9b1d462 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -22,8 +22,11 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,6 +35,8 @@
import android.view.View;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Locale;
@@ -663,6 +668,28 @@
*/
public int seq;
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ NATIVE_CONFIG_MCC,
+ NATIVE_CONFIG_MNC,
+ NATIVE_CONFIG_LOCALE,
+ NATIVE_CONFIG_TOUCHSCREEN,
+ NATIVE_CONFIG_KEYBOARD,
+ NATIVE_CONFIG_KEYBOARD_HIDDEN,
+ NATIVE_CONFIG_NAVIGATION,
+ NATIVE_CONFIG_ORIENTATION,
+ NATIVE_CONFIG_DENSITY,
+ NATIVE_CONFIG_SCREEN_SIZE,
+ NATIVE_CONFIG_VERSION,
+ NATIVE_CONFIG_SCREEN_LAYOUT,
+ NATIVE_CONFIG_UI_MODE,
+ NATIVE_CONFIG_SMALLEST_SCREEN_SIZE,
+ NATIVE_CONFIG_LAYOUTDIR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NativeConfig {}
+
/** @hide Native-specific bit mask for MCC config; DO NOT USE UNLESS YOU ARE SURE. */
public static final int NATIVE_CONFIG_MCC = 0x0001;
/** @hide Native-specific bit mask for MNC config; DO NOT USE UNLESS YOU ARE SURE. */
@@ -917,14 +944,13 @@
}
/**
- * Copy the fields from delta into this Configuration object, keeping
- * track of which ones have changed. Any undefined fields in
- * <var>delta</var> are ignored and not copied in to the current
- * Configuration.
- * @return Returns a bit mask of the changed fields, as per
- * {@link #diff}.
+ * Copies the fields from delta into this Configuration object, keeping
+ * track of which ones have changed. Any undefined fields in {@code delta}
+ * are ignored and not copied in to the current Configuration.
+ *
+ * @return a bit mask of the changed fields, as per {@link #diff}
*/
- public int updateFrom(Configuration delta) {
+ public @Config int updateFrom(@NonNull Configuration delta) {
int changed = 0;
if (delta.fontScale > 0 && fontScale != delta.fontScale) {
changed |= ActivityInfo.CONFIG_FONT_SCALE;
@@ -1171,17 +1197,19 @@
}
/**
- * Determine if a new resource needs to be loaded from the bit set of
+ * Determines if a new resource needs to be loaded from the bit set of
* configuration changes returned by {@link #updateFrom(Configuration)}.
*
- * @param configChanges The mask of changes configurations as returned by
- * {@link #updateFrom(Configuration)}.
- * @param interestingChanges The configuration changes that the resource
- * can handled, as given in {@link android.util.TypedValue#changingConfigurations}.
- *
- * @return Return true if the resource needs to be loaded, else false.
+ * @param configChanges the mask of changes configurations as returned by
+ * {@link #updateFrom(Configuration)}
+ * @param interestingChanges the configuration changes that the resource
+ * can handle as given in
+ * {@link android.util.TypedValue#changingConfigurations}
+ * @return {@code true} if the resource needs to be loaded, {@code false}
+ * otherwise
*/
- public static boolean needNewResources(int configChanges, int interestingChanges) {
+ public static boolean needNewResources(@Config int configChanges,
+ @Config int interestingChanges) {
return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0;
}
diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java
index fecda87..70290c4 100644
--- a/core/java/android/content/res/ConfigurationBoundResourceCache.java
+++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java
@@ -16,6 +16,8 @@
package android.content.res;
+import android.content.pm.ActivityInfo.Config;
+
/**
* A Cache class which can be used to cache resource objects that are easy to clone but more
* expensive to inflate.
@@ -23,36 +25,26 @@
* @hide For internal use only.
*/
public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> {
- private final Resources mResources;
-
- /**
- * Creates a cache for the given Resources instance.
- *
- * @param resources the resources to use when creating new instances
- */
- public ConfigurationBoundResourceCache(Resources resources) {
- mResources = resources;
- }
-
/**
* If the resource is cached, creates and returns a new instance of it.
*
* @param key a key that uniquely identifies the drawable resource
+ * @param resources a Resources object from which to create new instances.
* @param theme the theme where the resource will be used
* @return a new instance of the resource, or {@code null} if not in
* the cache
*/
- public T getInstance(long key, Resources.Theme theme) {
+ public T getInstance(long key, Resources resources, Resources.Theme theme) {
final ConstantState<T> entry = get(key, theme);
if (entry != null) {
- return entry.newInstance(mResources, theme);
+ return entry.newInstance(resources, theme);
}
return null;
}
@Override
- public boolean shouldInvalidateEntry(ConstantState<T> entry, int configChanges) {
+ public boolean shouldInvalidateEntry(ConstantState<T> entry, @Config int configChanges) {
return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
}
}
diff --git a/core/java/android/content/res/ConstantState.java b/core/java/android/content/res/ConstantState.java
index ee609df..09d4a59 100644
--- a/core/java/android/content/res/ConstantState.java
+++ b/core/java/android/content/res/ConstantState.java
@@ -15,6 +15,8 @@
*/
package android.content.res;
+import android.content.pm.ActivityInfo.Config;
+
/**
* A cache class that can provide new instances of a particular resource which may change
* depending on the current {@link Resources.Theme} or {@link Configuration}.
@@ -33,7 +35,7 @@
* Return a bit mask of configuration changes that will impact
* this resource (and thus require completely reloading it).
*/
- abstract public int getChangingConfigurations();
+ abstract public @Config int getChangingConfigurations();
/**
* Create a new instance without supplying resources the caller
diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java
index ba00134..7b27fac 100644
--- a/core/java/android/content/res/DrawableCache.java
+++ b/core/java/android/content/res/DrawableCache.java
@@ -22,29 +22,19 @@
* Class which can be used to cache Drawable resources against a theme.
*/
class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
- private final Resources mResources;
-
- /**
- * Creates a cache for the given Resources instance.
- *
- * @param resources the resources to use when creating new instances
- */
- public DrawableCache(Resources resources) {
- mResources = resources;
- }
-
/**
* If the resource is cached, creates and returns a new instance of it.
*
* @param key a key that uniquely identifies the drawable resource
+ * @param resources a Resources object from which to create new instances.
* @param theme the theme where the resource will be used
* @return a new instance of the resource, or {@code null} if not in
* the cache
*/
- public Drawable getInstance(long key, Resources.Theme theme) {
+ public Drawable getInstance(long key, Resources resources, Resources.Theme theme) {
final Drawable.ConstantState entry = get(key, theme);
if (entry != null) {
- return entry.newDrawable(mResources, theme);
+ return entry.newDrawable(resources, theme);
}
return null;
diff --git a/core/java/android/content/res/GradientColor.java b/core/java/android/content/res/GradientColor.java
index 98ef2ea..f29656a 100644
--- a/core/java/android/content/res/GradientColor.java
+++ b/core/java/android/content/res/GradientColor.java
@@ -17,8 +17,10 @@
package android.content.res;
import android.annotation.ColorInt;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources.Theme;
import com.android.internal.R;
@@ -37,24 +39,60 @@
import android.util.Xml;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
-
+/**
+ * Lets you define a gradient color, which is used inside
+ * {@link android.graphics.drawable.VectorDrawable}.
+ *
+ * {@link android.content.res.GradientColor}s are created from XML resource files defined in the
+ * "color" subdirectory directory of an application's resource directory. The XML file contains
+ * a single "gradient" element with a number of attributes and elements inside. For example:
+ * <pre>
+ * <gradient xmlns:android="http://schemas.android.com/apk/res/android">
+ * <android:startColor="?android:attr/colorPrimary"/>
+ * <android:endColor="?android:attr/colorControlActivated"/>
+ * <.../>
+ * <android:type="linear"/>
+ * </gradient>
+ * </pre>
+ *
+ * This can describe either a {@link android.graphics.LinearGradient},
+ * {@link android.graphics.RadialGradient}, or {@link android.graphics.SweepGradient}.
+ *
+ * Note that different attributes are relevant for different types of gradient.
+ * For example, android:gradientRadius is only applied to RadialGradient.
+ * androd:centerX and android:centerY are only applied to SweepGradient or RadialGradient.
+ * android:startX, android:startY, android:endX and android:endY are only applied to LinearGradient.
+ *
+ * Also note if any color "item" element is defined, then startColor, centerColor and endColor will
+ * be ignored.
+ */
public class GradientColor extends ComplexColor {
private static final String TAG = "GradientColor";
private static final boolean DBG_GRADIENT = false;
+ @IntDef({TILE_MODE_CLAMP, TILE_MODE_REPEAT, TILE_MODE_MIRROR})
+ @Retention(RetentionPolicy.SOURCE)
+ private @interface GradientTileMode {}
+ private static final int TILE_MODE_CLAMP = 0;
+ private static final int TILE_MODE_REPEAT = 1;
+ private static final int TILE_MODE_MIRROR = 2;
+
/** Lazily-created factory for this GradientColor. */
private GradientColorFactory mFactory;
- private int mChangingConfigurations;
+ private @Config int mChangingConfigurations;
private int mDefaultColor;
// After parsing all the attributes from XML, this shader is the ultimate result containing
// all the XML information.
private Shader mShader = null;
- // Below are the attributes at the root element <gradient>
+ // Below are the attributes at the root element <gradient>.
+ // NOTE: they need to be copied in the copy constructor!
private int mGradientType = GradientDrawable.LINEAR_GRADIENT;
private float mCenterX = 0f;
@@ -70,6 +108,8 @@
private int mEndColor = 0;
private boolean mHasCenterColor = false;
+ private int mTileMode = 0; // Clamp mode.
+
private float mGradientRadius = 0f;
// Below are the attributes for the <item> element.
@@ -100,6 +140,7 @@
mEndColor = copy.mEndColor;
mHasCenterColor = copy.mHasCenterColor;
mGradientRadius = copy.mGradientRadius;
+ mTileMode = copy.mTileMode;
if (copy.mItemColors != null) {
mItemColors = copy.mItemColors.clone();
@@ -117,6 +158,20 @@
}
}
+ // Set the default to clamp mode.
+ private static Shader.TileMode parseTileMode(@GradientTileMode int tileMode) {
+ switch (tileMode) {
+ case TILE_MODE_CLAMP:
+ return Shader.TileMode.CLAMP;
+ case TILE_MODE_REPEAT:
+ return Shader.TileMode.REPEAT;
+ case TILE_MODE_MIRROR:
+ return Shader.TileMode.MIRROR;
+ default:
+ return Shader.TileMode.CLAMP;
+ }
+ }
+
/**
* Update the root level's attributes, either for inflate or applyTheme.
*/
@@ -150,6 +205,9 @@
mEndColor = a.getColor(
R.styleable.GradientColor_endColor, mEndColor);
+ mTileMode = a.getInt(
+ R.styleable.GradientColor_tileMode, mTileMode);
+
if (DBG_GRADIENT) {
Log.v(TAG, "hasCenterColor is " + mHasCenterColor);
if (mHasCenterColor) {
@@ -157,6 +215,7 @@
}
Log.v(TAG, "startColor: " + mStartColor);
Log.v(TAG, "endColor: " + mEndColor);
+ Log.v(TAG, "tileMode: " + mTileMode);
}
mGradientRadius = a.getFloat(R.styleable.GradientColor_gradientRadius,
@@ -406,11 +465,11 @@
if (mGradientType == GradientDrawable.LINEAR_GRADIENT) {
mShader = new LinearGradient(mStartX, mStartY, mEndX, mEndY, tempColors, tempOffsets,
- Shader.TileMode.CLAMP);
+ parseTileMode(mTileMode));
} else {
if (mGradientType == GradientDrawable.RADIAL_GRADIENT) {
mShader = new RadialGradient(mCenterX, mCenterY, mGradientRadius, tempColors,
- tempOffsets, Shader.TileMode.CLAMP);
+ tempOffsets, parseTileMode(mTileMode));
} else {
mShader = new SweepGradient(mCenterX, mCenterY, tempColors, tempOffsets);
}
@@ -448,7 +507,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mSrc.mChangingConfigurations;
}
@@ -483,6 +542,19 @@
return clone;
}
+ /**
+ * Returns a mask of the configuration parameters for which this gradient
+ * may change, requiring that it be re-created.
+ *
+ * @return a mask of the changing configuration parameters, as defined by
+ * {@link android.content.pm.ActivityInfo}
+ *
+ * @see android.content.pm.ActivityInfo
+ */
+ public int getChangingConfigurations() {
+ return super.getChangingConfigurations() | mChangingConfigurations;
+ }
+
private void applyTheme(Theme t) {
if (mThemeAttrs != null) {
applyRootAttrsTheme(t);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a54f40f..fb706fc 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -40,26 +40,21 @@
import android.annotation.XmlRes;
import android.content.pm.ActivityInfo;
import android.graphics.Movie;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.DrawableInflater;
-import android.icu.text.PluralRules;
import android.os.Build;
import android.os.Bundle;
-import android.os.Trace;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
-import android.util.LocaleList;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pools.SynchronizedPool;
-import android.util.Slog;
import android.util.TypedValue;
-import android.util.Xml;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.XmlUtils;
@@ -68,8 +63,8 @@
import java.io.IOException;
import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Locale;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
/**
* Class for accessing an application's resources. This sits on top of the
@@ -99,51 +94,15 @@
public class Resources {
static final String TAG = "Resources";
- private static final boolean DEBUG_LOAD = false;
- private static final boolean DEBUG_CONFIG = false;
- private static final boolean TRACE_FOR_PRELOAD = false;
- private static final boolean TRACE_FOR_MISS_PRELOAD = false;
-
- private static final int LAYOUT_DIR_CONFIG = ActivityInfo.activityInfoConfigToNative(
- ActivityInfo.CONFIG_LAYOUT_DIRECTION);
-
- private static final int ID_OTHER = 0x01000004;
-
private static final Object sSync = new Object();
- // Information about preloaded resources. Note that they are not
- // protected by a lock, because while preloading in zygote we are all
- // single-threaded, and after that these are immutable.
- private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
- private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
- = new LongSparseArray<>();
- private static final LongSparseArray<android.content.res.ConstantState<ComplexColor>>
- sPreloadedComplexColors = new LongSparseArray<>();
-
- /** Size of the cyclical cache used to map XML files to blocks. */
- private static final int XML_BLOCK_CACHE_SIZE = 4;
-
- // Pool of TypedArrays targeted to this Resources object.
- final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
-
// Used by BridgeResources in layoutlib
static Resources mSystem = null;
- private static boolean sPreloaded;
+ private ResourcesImpl mResourcesImpl;
- /** Lock object used to protect access to caches and configuration. */
- private final Object mAccessLock = new Object();
-
- // These are protected by mAccessLock.
- private final Configuration mTmpConfig = new Configuration();
- private final DrawableCache mDrawableCache = new DrawableCache(this);
- private final DrawableCache mColorDrawableCache = new DrawableCache(this);
- private final ConfigurationBoundResourceCache<ComplexColor> mComplexColorCache =
- new ConfigurationBoundResourceCache<>(this);
- private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
- new ConfigurationBoundResourceCache<>(this);
- private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
- new ConfigurationBoundResourceCache<>(this);
+ // Pool of TypedArrays targeted to this Resources object.
+ final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
/** Used to inflate drawable objects from XML. */
private DrawableInflater mDrawableInflater;
@@ -154,29 +113,14 @@
/** Single-item pool used to minimize TypedValue allocations. */
private TypedValue mTmpValue = new TypedValue();
- private boolean mPreloading;
-
- // Cyclical cache used for recently-accessed XML files.
- private int mLastCachedXmlBlockIndex = -1;
- private final int[] mCachedXmlBlockCookies = new int[XML_BLOCK_CACHE_SIZE];
- private final String[] mCachedXmlBlockFiles = new String[XML_BLOCK_CACHE_SIZE];
- private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[XML_BLOCK_CACHE_SIZE];
-
- final AssetManager mAssets;
final ClassLoader mClassLoader;
- final DisplayMetrics mMetrics = new DisplayMetrics();
- private final Configuration mConfiguration = new Configuration();
-
- private PluralRules mPluralRule;
-
- private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
-
- static {
- sPreloadedDrawables = new LongSparseArray[2];
- sPreloadedDrawables[0] = new LongSparseArray<>();
- sPreloadedDrawables[1] = new LongSparseArray<>();
- }
+ /**
+ * WeakReferences to Themes that were constructed from this Resources object.
+ * We keep track of these in case our underlying implementation is changed, in which case
+ * the Themes must also get updated ThemeImpls.
+ */
+ private final ArrayList<WeakReference<Theme>> mThemeRefs = new ArrayList<>();
/**
* Returns the most appropriate default theme for the specified target SDK version.
@@ -219,32 +163,20 @@
}
/**
- * @return the inflater used to create drawable objects
- * @hide Pending API finalization.
+ * Return a global shared Resources object that provides access to only
+ * system resources (no application resources), and is not configured for
+ * the current screen (can not use dimension units, does not change based
+ * on orientation, etc).
*/
- public final DrawableInflater getDrawableInflater() {
- if (mDrawableInflater == null) {
- mDrawableInflater = new DrawableInflater(this, mClassLoader);
+ public static Resources getSystem() {
+ synchronized (sSync) {
+ Resources ret = mSystem;
+ if (ret == null) {
+ ret = new Resources();
+ mSystem = ret;
+ }
+ return ret;
}
- return mDrawableInflater;
- }
-
- /**
- * Used by AnimatorInflater.
- *
- * @hide
- */
- public ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
- return mAnimatorCache;
- }
-
- /**
- * Used by AnimatorInflater.
- *
- * @hide
- */
- public ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
- return mStateListAnimatorCache;
}
/**
@@ -275,51 +207,105 @@
* selecting/computing resource values (optional).
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
- this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ this(null);
+ mResourcesImpl = new ResourcesImpl(assets, metrics, config,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
}
/**
* Creates a new Resources object with CompatibilityInfo.
*
- * @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).
- * @param compatInfo this resource's compatibility info. Must not be null.
* @param classLoader class loader for the package used to load custom
* resource classes, may be {@code null} to use system
* class loader
* @hide
*/
- public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
- CompatibilityInfo compatInfo, @Nullable ClassLoader classLoader) {
- mAssets = assets;
+ public Resources(@Nullable ClassLoader classLoader) {
mClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader;
- mMetrics.setToDefaults();
- if (compatInfo != null) {
- mCompatibilityInfo = compatInfo;
- }
- updateConfiguration(config, metrics);
- assets.ensureStringBlocks();
}
/**
- * Return a global shared Resources object that provides access to only
- * system resources (no application resources), and is not configured for
- * the current screen (can not use dimension units, does not change based
- * on orientation, etc).
+ * Only for creating the System resources.
*/
- public static Resources getSystem() {
- synchronized (sSync) {
- Resources ret = mSystem;
- if (ret == null) {
- ret = new Resources();
- mSystem = ret;
- }
+ private Resources() {
+ this(null);
- return ret;
+ final DisplayMetrics metrics = new DisplayMetrics();
+ metrics.setToDefaults();
+
+ final Configuration config = new Configuration();
+ config.setToDefaults();
+
+ mResourcesImpl = new ResourcesImpl(AssetManager.getSystem(), metrics, config,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
+ }
+
+ /**
+ * Set the underlying implementation (containing all the resources and caches)
+ * and updates all Theme references to new implementations as well.
+ * @hide
+ */
+ public void setImpl(ResourcesImpl impl) {
+ if (impl == mResourcesImpl) {
+ return;
}
+
+ mResourcesImpl = impl;
+
+ // Create new ThemeImpls that are identical to the ones we have.
+ synchronized (mThemeRefs) {
+ final int count = mThemeRefs.size();
+ for (int i = 0; i < count; i++) {
+ WeakReference<Theme> weakThemeRef = mThemeRefs.get(i);
+ Theme theme = weakThemeRef != null ? weakThemeRef.get() : null;
+ if (theme != null) {
+ theme.setImpl(mResourcesImpl.newThemeImpl(theme.getKey()));
+ }
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public ResourcesImpl getImpl() {
+ return mResourcesImpl;
+ }
+
+ /**
+ * @hide
+ */
+ public ClassLoader getClassLoader() {
+ return mClassLoader;
+ }
+
+ /**
+ * @return the inflater used to create drawable objects
+ * @hide Pending API finalization.
+ */
+ public final DrawableInflater getDrawableInflater() {
+ if (mDrawableInflater == null) {
+ mDrawableInflater = new DrawableInflater(this, mClassLoader);
+ }
+ return mDrawableInflater;
+ }
+
+ /**
+ * Used by AnimatorInflater.
+ *
+ * @hide
+ */
+ public ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
+ return mResourcesImpl.getAnimatorCache();
+ }
+
+ /**
+ * Used by AnimatorInflater.
+ *
+ * @hide
+ */
+ public ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
+ return mResourcesImpl.getStateListAnimatorCache();
}
/**
@@ -337,8 +323,8 @@
* @return CharSequence The string data associated with the resource, plus
* possibly styled text information.
*/
- public CharSequence getText(@StringRes int id) throws NotFoundException {
- CharSequence res = mAssets.getResourceText(id);
+ @NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
+ CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
if (res != null) {
return res;
}
@@ -366,41 +352,10 @@
* @return CharSequence The string data associated with the resource, plus
* possibly styled text information.
*/
+ @NonNull
public CharSequence getQuantityText(@PluralsRes int id, int quantity)
throws NotFoundException {
- PluralRules rule = getPluralRule();
- CharSequence res = mAssets.getResourceBagText(id,
- attrForQuantityCode(rule.select(quantity)));
- if (res != null) {
- return res;
- }
- res = mAssets.getResourceBagText(id, ID_OTHER);
- if (res != null) {
- return res;
- }
- throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
- + " quantity=" + quantity
- + " item=" + rule.select(quantity));
- }
-
- private PluralRules getPluralRule() {
- synchronized (sSync) {
- if (mPluralRule == null) {
- mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
- }
- return mPluralRule;
- }
- }
-
- private static int attrForQuantityCode(String quantityCode) {
- switch (quantityCode) {
- case PluralRules.KEYWORD_ZERO: return 0x01000005;
- case PluralRules.KEYWORD_ONE: return 0x01000006;
- case PluralRules.KEYWORD_TWO: return 0x01000007;
- case PluralRules.KEYWORD_FEW: return 0x01000008;
- case PluralRules.KEYWORD_MANY: return 0x01000009;
- default: return ID_OTHER;
- }
+ return mResourcesImpl.getQuantityText(id, quantity);
}
/**
@@ -419,12 +374,7 @@
*/
@NonNull
public String getString(@StringRes int id) throws NotFoundException {
- final CharSequence res = getText(id);
- if (res != null) {
- return res.toString();
- }
- throw new NotFoundException("String resource ID #0x"
- + Integer.toHexString(id));
+ return getText(id).toString();
}
@@ -449,7 +399,8 @@
@NonNull
public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException {
final String raw = getString(id);
- return String.format(mConfiguration.getLocales().get(0), raw, formatArgs);
+ return String.format(mResourcesImpl.getConfiguration().getLocales().get(0), raw,
+ formatArgs);
}
/**
@@ -477,10 +428,12 @@
* @return String The string data associated with the resource,
* stripped of styled text information.
*/
+ @NonNull
public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs)
throws NotFoundException {
String raw = getQuantityText(id, quantity).toString();
- return String.format(mConfiguration.getLocales().get(0), raw, formatArgs);
+ return String.format(mResourcesImpl.getConfiguration().getLocales().get(0), raw,
+ formatArgs);
}
/**
@@ -503,8 +456,8 @@
* @return String The string data associated with the resource,
* stripped of styled text information.
*/
- public String getQuantityString(@PluralsRes int id, int quantity)
- throws NotFoundException {
+ @NonNull
+ public String getQuantityString(@PluralsRes int id, int quantity) throws NotFoundException {
return getQuantityText(id, quantity).toString();
}
@@ -523,7 +476,7 @@
* possibly styled text information, or def if id is 0 or not found.
*/
public CharSequence getText(@StringRes int id, CharSequence def) {
- CharSequence res = id != 0 ? mAssets.getResourceText(id) : null;
+ CharSequence res = id != 0 ? mResourcesImpl.getAssets().getResourceText(id) : null;
return res != null ? res : def;
}
@@ -538,13 +491,13 @@
*
* @return The styled text array associated with the resource.
*/
+ @NonNull
public CharSequence[] getTextArray(@ArrayRes int id) throws NotFoundException {
- CharSequence[] res = mAssets.getResourceTextArray(id);
+ CharSequence[] res = mResourcesImpl.getAssets().getResourceTextArray(id);
if (res != null) {
return res;
}
- throw new NotFoundException("Text array resource ID #0x"
- + Integer.toHexString(id));
+ throw new NotFoundException("Text array resource ID #0x" + Integer.toHexString(id));
}
/**
@@ -558,14 +511,14 @@
*
* @return The string array associated with the resource.
*/
+ @NonNull
public String[] getStringArray(@ArrayRes int id)
throws NotFoundException {
- String[] res = mAssets.getResourceStringArray(id);
+ String[] res = mResourcesImpl.getAssets().getResourceStringArray(id);
if (res != null) {
return res;
}
- throw new NotFoundException("String array resource ID #0x"
- + Integer.toHexString(id));
+ throw new NotFoundException("String array resource ID #0x" + Integer.toHexString(id));
}
/**
@@ -579,13 +532,13 @@
*
* @return The int array associated with the resource.
*/
+ @NonNull
public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
- int[] res = mAssets.getArrayIntResource(id);
+ int[] res = mResourcesImpl.getAssets().getArrayIntResource(id);
if (res != null) {
return res;
}
- throw new NotFoundException("Int array resource ID #0x"
- + Integer.toHexString(id));
+ throw new NotFoundException("Int array resource ID #0x" + Integer.toHexString(id));
}
/**
@@ -601,16 +554,16 @@
* Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
* when done with it.
*/
- public TypedArray obtainTypedArray(@ArrayRes int id)
- throws NotFoundException {
- int len = mAssets.getArraySize(id);
+ @NonNull
+ public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
+ final ResourcesImpl impl = mResourcesImpl;
+ int len = impl.getAssets().getArraySize(id);
if (len < 0) {
- throw new NotFoundException("Array resource ID #0x"
- + Integer.toHexString(id));
+ throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
}
TypedArray array = TypedArray.obtain(this, len);
- array.mLength = mAssets.retrieveArray(id, array.mData);
+ array.mLength = impl.getAssets().retrieveArray(id, array.mData);
array.mIndices[0] = 0;
return array;
@@ -634,10 +587,12 @@
* @see #getDimensionPixelSize
*/
public float getDimension(@DimenRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimension(value.data, mMetrics);
+ return TypedValue.complexToDimension(value.data, impl.getDisplayMetrics());
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
@@ -666,10 +621,13 @@
* @see #getDimensionPixelSize
*/
public int getDimensionPixelOffset(@DimenRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelOffset(value.data, mMetrics);
+ return TypedValue.complexToDimensionPixelOffset(value.data,
+ impl.getDisplayMetrics());
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
@@ -699,10 +657,12 @@
* @see #getDimensionPixelOffset
*/
public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(value.data, mMetrics);
+ return TypedValue.complexToDimensionPixelSize(value.data, impl.getDisplayMetrics());
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
@@ -729,8 +689,9 @@
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
public float getFraction(@FractionRes int id, int base, int pbase) {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ mResourcesImpl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_FRACTION) {
return TypedValue.complexToFraction(value.data, base, pbase);
}
@@ -800,9 +761,11 @@
*/
public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
- return loadDrawable(value, id, theme);
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
+ return impl.loadDrawable(this, value, id, theme, true);
} finally {
releaseTempTypedValue(value);
}
@@ -855,14 +818,16 @@
* not exist.
*/
public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
- getValueForDensity(id, density, value, true);
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValueForDensity(id, density, value, true);
// If the drawable's XML lives in our current density qualifier,
// it's okay to use a scaled version from the cache. Otherwise, we
// need to actually load the drawable from XML.
- final boolean useCache = value.density == mMetrics.densityDpi;
+ final DisplayMetrics metrics = impl.getDisplayMetrics();
+ final boolean useCache = value.density == metrics.densityDpi;
/*
* Pretend the requested density is actually the display density. If
@@ -873,18 +838,23 @@
*/
if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
if (value.density == density) {
- value.density = mMetrics.densityDpi;
+ value.density = metrics.densityDpi;
} else {
- value.density = (value.density * mMetrics.densityDpi) / density;
+ value.density = (value.density * metrics.densityDpi) / density;
}
}
-
- return loadDrawable(value, id, theme, useCache);
+ return impl.loadDrawable(this, value, id, theme, useCache);
} finally {
releaseTempTypedValue(value);
}
}
+ @NonNull
+ Drawable loadDrawable(@NonNull TypedValue value, int id, @Nullable Theme theme)
+ throws NotFoundException {
+ return mResourcesImpl.loadDrawable(this, value, id, theme, true);
+ }
+
/**
* Return a movie object associated with the particular resource ID.
* @param id The desired resource identifier, as generated by the aapt
@@ -894,13 +864,12 @@
*
*/
public Movie getMovie(@RawRes int id) throws NotFoundException {
- InputStream is = openRawResource(id);
- Movie movie = Movie.decodeStream(is);
+ final InputStream is = openRawResource(id);
+ final Movie movie = Movie.decodeStream(is);
try {
is.close();
- }
- catch (java.io.IOException e) {
- // don't care, since the return value is valid
+ } catch (IOException e) {
+ // No one cares.
}
return movie;
}
@@ -944,8 +913,10 @@
*/
@ColorInt
public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
return value.data;
@@ -954,7 +925,7 @@
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
}
- final ColorStateList csl = loadColorStateList(value, id, theme);
+ final ColorStateList csl = impl.loadColorStateList(this, value, id, theme);
return csl.getDefaultColor();
} finally {
releaseTempTypedValue(value);
@@ -1012,14 +983,27 @@
@Nullable
public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
- return loadColorStateList(value, id, theme);
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
+ return impl.loadColorStateList(this, value, id, theme);
} finally {
releaseTempTypedValue(value);
}
}
+ @Nullable
+ ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
+ throws NotFoundException {
+ return mResourcesImpl.loadColorStateList(this, value, id, theme);
+ }
+
+ @Nullable
+ public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
+ return mResourcesImpl.loadComplexColor(this, value, id, theme);
+ }
+
/**
* Return a boolean associated with a particular resource ID. This can be
* used with any integral resource value, and will return true if it is
@@ -1034,8 +1018,9 @@
* @return Returns the boolean value contained in the resource.
*/
public boolean getBoolean(@BoolRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ mResourcesImpl.getValue(id, value, true);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
return value.data != 0;
@@ -1059,8 +1044,9 @@
* @return Returns the integer value contained in the resource.
*/
public int getInteger(@IntegerRes int id) throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ mResourcesImpl.getValue(id, value, true);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
return value.data;
@@ -1086,8 +1072,9 @@
* @hide Pending API council approval.
*/
public float getFloat(int id) {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ mResourcesImpl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_FLOAT) {
return value.getFloat();
}
@@ -1195,20 +1182,6 @@
}
/**
- * Returns a TypedValue populated with data for the specified resource ID
- * that's suitable for temporary use. The obtained TypedValue should be
- * released using {@link #releaseTempTypedValue(TypedValue)}.
- *
- * @param id the resource ID for which data should be obtained
- * @return a populated typed value suitable for temporary use
- */
- private TypedValue obtainTempTypedValue(@AnyRes int id) {
- final TypedValue value = obtainTempTypedValue();
- getValue(id, value, true);
- return value;
- }
-
- /**
* Returns a TypedValue suitable for temporary use. The obtained TypedValue
* should be released using {@link #releaseTempTypedValue(TypedValue)}.
*
@@ -1257,17 +1230,7 @@
*/
public InputStream openRawResource(@RawRes int id, TypedValue value)
throws NotFoundException {
- getValue(id, value, true);
-
- try {
- return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
- AssetManager.ACCESS_STREAMING);
- } catch (Exception e) {
- NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
- " from drawable resource ID #0x" + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
+ return mResourcesImpl.openRawResource(id, value);
}
/**
@@ -1293,12 +1256,9 @@
*/
public AssetFileDescriptor openRawResourceFd(@RawRes int id)
throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
- return mAssets.openNonAssetFd(value.assetCookie, value.string.toString());
- } catch (Exception e) {
- throw new NotFoundException("File " + value.string.toString() + " from drawable "
- + "resource ID #0x" + Integer.toHexString(id), e);
+ return mResourcesImpl.openRawResourceFd(id, value);
} finally {
releaseTempTypedValue(value);
}
@@ -1321,12 +1281,7 @@
*/
public void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
- boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
- if (found) {
- return;
- }
- throw new NotFoundException("Resource ID #0x"
- + Integer.toHexString(id));
+ mResourcesImpl.getValue(id, outValue, resolveRefs);
}
/**
@@ -1344,11 +1299,7 @@
*/
public void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
boolean resolveRefs) throws NotFoundException {
- boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
- if (found) {
- return;
- }
- throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
+ mResourcesImpl.getValueForDensity(id, density, outValue, resolveRefs);
}
/**
@@ -1373,12 +1324,7 @@
*/
public void getValue(String name, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
- int id = getIdentifier(name, "string", null);
- if (id != 0) {
- getValue(id, outValue, resolveRefs);
- return;
- }
- throw new NotFoundException("String resource name " + name);
+ mResourcesImpl.getValue(name, outValue, resolveRefs);
}
/**
@@ -1397,6 +1343,15 @@
* retrieve XML attributes with style and theme information applied.
*/
public final class Theme {
+ private ResourcesImpl.ThemeImpl mThemeImpl;
+
+ private Theme() {
+ }
+
+ void setImpl(ResourcesImpl.ThemeImpl impl) {
+ mThemeImpl = impl;
+ }
+
/**
* Place new attribute values into the theme. The style resource
* specified by <var>resid</var> will be retrieved from this Theme's
@@ -1415,12 +1370,7 @@
* if not already defined in the theme.
*/
public void applyStyle(int resId, boolean force) {
- synchronized (mKey) {
- AssetManager.applyThemeStyle(mTheme, resId, force);
-
- mThemeResId = resId;
- mKey.append(resId, force);
- }
+ mThemeImpl.applyStyle(resId, force);
}
/**
@@ -1433,14 +1383,7 @@
* @param other The existing Theme to copy from.
*/
public void setTo(Theme other) {
- synchronized (mKey) {
- synchronized (other.mKey) {
- AssetManager.copyTheme(mTheme, other.mTheme);
-
- mThemeResId = other.mThemeResId;
- mKey.setTo(other.getKey());
- }
- }
+ mThemeImpl.setTo(other.mThemeImpl);
}
/**
@@ -1463,13 +1406,7 @@
* @see #obtainStyledAttributes(AttributeSet, int[], int, int)
*/
public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
- synchronized (mKey) {
- final int len = attrs.length;
- final TypedArray array = TypedArray.obtain(Resources.this, len);
- array.mTheme = this;
- AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices);
- return array;
- }
+ return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, 0);
}
/**
@@ -1494,13 +1431,7 @@
*/
public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs)
throws NotFoundException {
- synchronized (mKey) {
- final int len = attrs.length;
- final TypedArray array = TypedArray.obtain(Resources.this, len);
- array.mTheme = this;
- AssetManager.applyStyle(mTheme, 0, resId, 0, attrs, array.mData, array.mIndices);
- return array;
- }
+ return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, resId);
}
/**
@@ -1553,23 +1484,7 @@
*/
public TypedArray obtainStyledAttributes(AttributeSet set,
@StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
- synchronized (mKey) {
- final int len = attrs.length;
- final TypedArray array = TypedArray.obtain(Resources.this, len);
-
- // XXX note that for now we only work with compiled XML files.
- // To support generic XML files we will need to manually parse
- // out the attributes from the XML file (applying type information
- // contained in the resources and such).
- final XmlBlock.Parser parser = (XmlBlock.Parser) set;
- AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
- parser != null ? parser.mParseState : 0,
- attrs, array.mData, array.mIndices);
- array.mTheme = this;
- array.mXml = parser;
-
- return array;
- }
+ return mThemeImpl.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes);
}
/**
@@ -1588,20 +1503,7 @@
*/
@NonNull
public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) {
- synchronized (mKey) {
- final int len = attrs.length;
- if (values == null || len != values.length) {
- throw new IllegalArgumentException(
- "Base attribute values must the same length as attrs");
- }
-
- final TypedArray array = TypedArray.obtain(Resources.this, len);
- AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
- array.mTheme = this;
- array.mXml = null;
-
- return array;
- }
+ return mThemeImpl.resolveAttributes(this, values, attrs);
}
/**
@@ -1622,9 +1524,7 @@
* <var>outValue</var> is valid, else false.
*/
public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
- synchronized (mKey) {
- return mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
- }
+ return mThemeImpl.resolveAttribute(resid, outValue, resolveRefs);
}
/**
@@ -1634,7 +1534,7 @@
* @hide
*/
public int[] getAllAttributes() {
- return mAssets.getStyleAttributes(getAppliedStyleResId());
+ return mThemeImpl.getAllAttributes();
}
/**
@@ -1670,11 +1570,7 @@
* @see ActivityInfo
*/
public int getChangingConfigurations() {
- synchronized (mKey) {
- final int nativeChangingConfig =
- AssetManager.getThemeChangingConfigurations(mTheme);
- return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
- }
+ return mThemeImpl.getChangingConfigurations();
}
/**
@@ -1685,43 +1581,23 @@
* @param prefix Text to prefix each line printed.
*/
public void dump(int priority, String tag, String prefix) {
- synchronized (mKey) {
- AssetManager.dumpTheme(mTheme, priority, tag, prefix);
- }
+ mThemeImpl.dump(priority, tag, prefix);
}
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- mAssets.releaseTheme(mTheme);
- }
-
- /*package*/ Theme() {
- mAssets = Resources.this.mAssets;
- mTheme = mAssets.createTheme();
- }
-
- /** Unique key for the series of styles applied to this theme. */
- private final ThemeKey mKey = new ThemeKey();
-
- @SuppressWarnings("hiding")
- private final AssetManager mAssets;
- private final long mTheme;
-
- /** Resource identifier for the theme. */
- private int mThemeResId = 0;
-
// Needed by layoutlib.
/*package*/ long getNativeTheme() {
- return mTheme;
+ return mThemeImpl.getNativeTheme();
}
/*package*/ int getAppliedStyleResId() {
- return mThemeResId;
+ return mThemeImpl.getAppliedStyleResId();
}
- /*package*/ ThemeKey getKey() {
- return mKey;
+ /**
+ * @hide
+ */
+ public ThemeKey getKey() {
+ return mThemeImpl.getKey();
}
private String getResourceNameFromHexString(String hexString) {
@@ -1729,7 +1605,7 @@
}
/**
- * Parses {@link #mKey} and returns a String array that holds pairs of
+ * Parses {@link #getKey()} and returns a String array that holds pairs of
* adjacent Theme data: resource name followed by whether or not it was
* forced, as specified by {@link #applyStyle(int, boolean)}.
*
@@ -1737,21 +1613,7 @@
*/
@ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
public String[] getTheme() {
- synchronized (mKey) {
- final int N = mKey.mCount;
- final String[] themes = new String[N * 2];
- for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
- final int resId = mKey.mResId[j];
- final boolean forced = mKey.mForce[j];
- try {
- themes[i] = getResourceName(resId);
- } catch (NotFoundException e) {
- themes[i] = Integer.toHexString(i);
- }
- themes[i + 1] = forced ? "forced" : "not forced";
- }
- return themes;
- }
+ return mThemeImpl.getTheme();
}
/** @hide */
@@ -1772,16 +1634,7 @@
* @hide
*/
public void rebase() {
- synchronized (mKey) {
- AssetManager.clearTheme(mTheme);
-
- // Reapply the same styles in the same order.
- for (int i = 0; i < mKey.mCount; i++) {
- final int resId = mKey.mResId[i];
- final boolean force = mKey.mForce[i];
- AssetManager.applyThemeStyle(mTheme, resId, force);
- }
- }
+ mThemeImpl.rebase();
}
}
@@ -1870,7 +1723,10 @@
* @return Theme The newly created Theme container.
*/
public final Theme newTheme() {
- return new Theme();
+ Theme theme = new Theme();
+ theme.setImpl(mResourcesImpl.newThemeImpl());
+ mThemeRefs.add(new WeakReference<>(theme));
+ return theme;
}
/**
@@ -1894,7 +1750,7 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
XmlBlock.Parser parser = (XmlBlock.Parser)set;
- mAssets.retrieveAttributes(parser.mParseState, attrs,
+ mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs,
array.mData, array.mIndices);
array.mXml = parser;
@@ -1905,151 +1761,16 @@
/**
* Store the newly updated configuration.
*/
- public void updateConfiguration(Configuration config,
- DisplayMetrics metrics) {
+ public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
updateConfiguration(config, metrics, null);
}
/**
* @hide
*/
- public void updateConfiguration(Configuration config,
- DisplayMetrics metrics, CompatibilityInfo compat) {
- synchronized (mAccessLock) {
- if (false) {
- Slog.i(TAG, "**** Updating config of " + this + ": old config is "
- + mConfiguration + " old compat is " + mCompatibilityInfo);
- Slog.i(TAG, "**** Updating config of " + this + ": new config is "
- + config + " new compat is " + compat);
- }
- if (compat != null) {
- mCompatibilityInfo = compat;
- }
- if (metrics != null) {
- mMetrics.setTo(metrics);
- }
- // NOTE: We should re-arrange this code to create a Display
- // with the CompatibilityInfo that is used everywhere we deal
- // with the display in relation to this app, rather than
- // doing the conversion here. This impl should be okay because
- // we make sure to return a compatible display in the places
- // where there are public APIs to retrieve the display... but
- // it would be cleaner and more maintainble to just be
- // consistently dealing with a compatible display everywhere in
- // the framework.
- mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
-
- final int configChanges = calcConfigChanges(config);
-
- LocaleList locales = mConfiguration.getLocales();
- if (locales.isEmpty()) {
- locales = LocaleList.getAdjustedDefault();
- mConfiguration.setLocales(locales);
- }
- if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
- mMetrics.densityDpi = mConfiguration.densityDpi;
- mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
- }
- mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
-
- final int width, height;
- if (mMetrics.widthPixels >= mMetrics.heightPixels) {
- width = mMetrics.widthPixels;
- height = mMetrics.heightPixels;
- } else {
- //noinspection SuspiciousNameCombination
- width = mMetrics.heightPixels;
- //noinspection SuspiciousNameCombination
- height = mMetrics.widthPixels;
- }
-
- final int keyboardHidden;
- if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
- && mConfiguration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
- keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
- } else {
- keyboardHidden = mConfiguration.keyboardHidden;
- }
-
- mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
- adjustLanguageTag(locales.get(0).toLanguageTag()),
- mConfiguration.orientation,
- mConfiguration.touchscreen,
- mConfiguration.densityDpi, mConfiguration.keyboard,
- keyboardHidden, mConfiguration.navigation, width, height,
- mConfiguration.smallestScreenWidthDp,
- mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
- mConfiguration.screenLayout, mConfiguration.uiMode,
- Build.VERSION.RESOURCES_SDK_INT);
-
- if (DEBUG_CONFIG) {
- Slog.i(TAG, "**** Updating config of " + this + ": final config is "
- + mConfiguration + " final compat is " + mCompatibilityInfo);
- }
-
- mDrawableCache.onConfigurationChange(configChanges);
- mColorDrawableCache.onConfigurationChange(configChanges);
- mComplexColorCache.onConfigurationChange(configChanges);
- mAnimatorCache.onConfigurationChange(configChanges);
- mStateListAnimatorCache.onConfigurationChange(configChanges);
-
- flushLayoutCache();
- }
- synchronized (sSync) {
- if (mPluralRule != null) {
- mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
- }
- }
- }
-
- /**
- * Called by ConfigurationBoundResourceCacheTest via reflection.
- */
- private int calcConfigChanges(Configuration config) {
- int configChanges = 0xfffffff;
- if (config != null) {
- mTmpConfig.setTo(config);
- int density = config.densityDpi;
- if (density == Configuration.DENSITY_DPI_UNDEFINED) {
- density = mMetrics.noncompatDensityDpi;
- }
-
- mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
-
- if (mTmpConfig.getLocales().isEmpty()) {
- mTmpConfig.setLocales(LocaleList.getDefault());
- }
- configChanges = mConfiguration.updateFrom(mTmpConfig);
- configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
- }
- return configChanges;
- }
-
- /**
- * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
- * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
- *
- * All released versions of android prior to "L" used the deprecated language
- * tags, so we will need to support them for backwards compatibility.
- *
- * Note that this conversion needs to take place *after* the call to
- * {@code toLanguageTag} because that will convert all the deprecated codes to
- * the new ones, even if they're set manually.
- */
- private static String adjustLanguageTag(String languageTag) {
- final int separator = languageTag.indexOf('-');
- final String language;
- final String remainder;
-
- if (separator == -1) {
- language = languageTag;
- remainder = "";
- } else {
- language = languageTag.substring(0, separator);
- remainder = languageTag.substring(separator);
- }
-
- return Locale.adjustLanguageCode(language) + remainder;
+ public void updateConfiguration(Configuration config, DisplayMetrics metrics,
+ CompatibilityInfo compat) {
+ mResourcesImpl.updateConfiguration(config, metrics, compat);
}
/**
@@ -2074,9 +1795,7 @@
* @return The resource's current display metrics.
*/
public DisplayMetrics getDisplayMetrics() {
- if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
- + "x" + mMetrics.heightPixels + " " + mMetrics.density);
- return mMetrics;
+ return mResourcesImpl.getDisplayMetrics();
}
/**
@@ -2086,13 +1805,13 @@
* @return The resource's current configuration.
*/
public Configuration getConfiguration() {
- return mConfiguration;
+ return mResourcesImpl.getConfiguration();
}
/** @hide */
public Configuration[] getSizeConfigurations() {
- return mAssets.getSizeConfigurations();
- };
+ return mResourcesImpl.getSizeConfigurations();
+ }
/**
* Return the compatibility mode information for the application.
@@ -2102,17 +1821,17 @@
* @hide
*/
public CompatibilityInfo getCompatibilityInfo() {
- return mCompatibilityInfo;
+ return mResourcesImpl.getCompatibilityInfo();
}
/**
* This is just for testing.
* @hide
*/
+ @VisibleForTesting
public void setCompatibilityInfo(CompatibilityInfo ci) {
if (ci != null) {
- mCompatibilityInfo = ci;
- updateConfiguration(mConfiguration, mMetrics);
+ mResourcesImpl.updateConfiguration(null, null, ci);
}
}
@@ -2137,15 +1856,7 @@
* resource was found. (0 is not a valid resource ID.)
*/
public int getIdentifier(String name, String defType, String defPackage) {
- if (name == null) {
- throw new NullPointerException("name is null");
- }
- try {
- return Integer.parseInt(name);
- } catch (Exception e) {
- // Ignore
- }
- return mAssets.getResourceIdentifier(name, defType, defPackage);
+ return mResourcesImpl.getIdentifier(name, defType, defPackage);
}
/**
@@ -2172,10 +1883,7 @@
* @see #getResourceEntryName
*/
public String getResourceName(@AnyRes int resid) throws NotFoundException {
- String str = mAssets.getResourceName(resid);
- if (str != null) return str;
- throw new NotFoundException("Unable to find resource ID #0x"
- + Integer.toHexString(resid));
+ return mResourcesImpl.getResourceName(resid);
}
/**
@@ -2191,10 +1899,7 @@
* @see #getResourceName
*/
public String getResourcePackageName(@AnyRes int resid) throws NotFoundException {
- String str = mAssets.getResourcePackageName(resid);
- if (str != null) return str;
- throw new NotFoundException("Unable to find resource ID #0x"
- + Integer.toHexString(resid));
+ return mResourcesImpl.getResourcePackageName(resid);
}
/**
@@ -2210,10 +1915,7 @@
* @see #getResourceName
*/
public String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
- String str = mAssets.getResourceTypeName(resid);
- if (str != null) return str;
- throw new NotFoundException("Unable to find resource ID #0x"
- + Integer.toHexString(resid));
+ return mResourcesImpl.getResourceTypeName(resid);
}
/**
@@ -2229,10 +1931,7 @@
* @see #getResourceName
*/
public String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
- String str = mAssets.getResourceEntryName(resid);
- if (str != null) return str;
- throw new NotFoundException("Unable to find resource ID #0x"
- + Integer.toHexString(resid));
+ return mResourcesImpl.getResourceEntryName(resid);
}
/**
@@ -2335,7 +2034,7 @@
* Retrieve underlying AssetManager storage for these resources.
*/
public final AssetManager getAssets() {
- return mAssets;
+ return mResourcesImpl.getAssets();
}
/**
@@ -2344,19 +2043,7 @@
* tools.
*/
public final void flushLayoutCache() {
- synchronized (mCachedXmlBlocks) {
- Arrays.fill(mCachedXmlBlockCookies, 0);
- Arrays.fill(mCachedXmlBlockFiles, null);
-
- final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
- for (int i = 0; i < XML_BLOCK_CACHE_SIZE; i++) {
- final XmlBlock oldBlock = cachedXmlBlocks[i];
- if (oldBlock != null) {
- oldBlock.close();
- }
- }
- Arrays.fill(cachedXmlBlocks, null);
- }
+ mResourcesImpl.flushLayoutCache();
}
/**
@@ -2365,15 +2052,7 @@
* {@hide}
*/
public final void startPreloading() {
- synchronized (sSync) {
- if (sPreloaded) {
- throw new IllegalStateException("Resources already preloaded");
- }
- sPreloaded = true;
- mPreloading = true;
- mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;
- updateConfiguration(null, null);
- }
+ mResourcesImpl.startPreloading();
}
/**
@@ -2381,441 +2060,14 @@
* to normal Resources operation.
*/
public final void finishPreloading() {
- if (mPreloading) {
- mPreloading = false;
- flushLayoutCache();
- }
+ mResourcesImpl.finishPreloading();
}
/**
* @hide
*/
public LongSparseArray<ConstantState> getPreloadedDrawables() {
- return sPreloadedDrawables[0];
- }
-
- private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
- int resourceId, String name) {
- // We allow preloading of resources even if they vary by font scale (which
- // doesn't impact resource selection) or density (which we handle specially by
- // simply turning off all preloading), as well as any other configs specified
- // by the caller.
- if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
- ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) {
- String resName;
- try {
- resName = getResourceName(resourceId);
- } catch (NotFoundException e) {
- resName = "?";
- }
- // This should never happen in production, so we should log a
- // warning even if we're not debugging.
- Log.w(TAG, "Preloaded " + name + " resource #0x"
- + Integer.toHexString(resourceId)
- + " (" + resName + ") that varies with configuration!!");
- return false;
- }
- if (TRACE_FOR_PRELOAD) {
- String resName;
- try {
- resName = getResourceName(resourceId);
- } catch (NotFoundException e) {
- resName = "?";
- }
- Log.w(TAG, "Preloading " + name + " resource #0x"
- + Integer.toHexString(resourceId)
- + " (" + resName + ")");
- }
- return true;
- }
-
- @Nullable
- Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
- return loadDrawable(value, id, theme, true);
- }
-
- @Nullable
- Drawable loadDrawable(TypedValue value, int id, Theme theme, boolean useCache)
- throws NotFoundException {
- try {
- if (TRACE_FOR_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) {
- Log.d("PreloadDrawable", name);
- }
- }
- }
-
- final boolean isColorDrawable;
- final DrawableCache caches;
- final long key;
- if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
- && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
- isColorDrawable = true;
- caches = mColorDrawableCache;
- key = value.data;
- } else {
- isColorDrawable = false;
- caches = mDrawableCache;
- key = (((long) value.assetCookie) << 32) | value.data;
- }
-
- // First, check whether we have a cached version of this drawable
- // that was inflated against the specified theme. Skip the cache if
- // we're currently preloading or we're not using the cache.
- if (!mPreloading && useCache) {
- final Drawable cachedDrawable = caches.getInstance(key, theme);
- if (cachedDrawable != null) {
- return cachedDrawable;
- }
- }
-
- // Next, check preloaded drawables. Preloaded drawables may contain
- // unresolved theme attributes.
- final ConstantState cs;
- if (isColorDrawable) {
- cs = sPreloadedColorDrawables.get(key);
- } else {
- cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
- }
-
- Drawable dr;
- if (cs != null) {
- dr = cs.newDrawable(this);
- } else if (isColorDrawable) {
- dr = new ColorDrawable(value.data);
- } else {
- dr = loadDrawableForCookie(value, id, null);
- }
-
- // Determine if the drawable has unresolved theme attributes. If it
- // does, we'll need to apply a theme and store it in a theme-specific
- // cache.
- final boolean canApplyTheme = dr != null && dr.canApplyTheme();
- if (canApplyTheme && theme != null) {
- dr = dr.mutate();
- dr.applyTheme(theme);
- dr.clearMutated();
- }
-
- // If we were able to obtain a drawable, store it in the appropriate
- // cache: preload, not themed, null theme, or theme-specific. Don't
- // pollute the cache with drawables loaded from a foreign density.
- if (dr != null && useCache) {
- dr.setChangingConfigurations(value.changingConfigurations);
- cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
- }
-
- return dr;
- } catch (Exception e) {
- String name;
- try {
- name = getResourceName(id);
- } catch (NotFoundException e2) {
- name = "(missing name)";
- }
-
- // The target drawable might fail to load for any number of
- // reasons, but we always want to include the resource name.
- // Since the client already expects this method to throw a
- // NotFoundException, just throw one of those.
- final NotFoundException nfe = new NotFoundException("Drawable " + name
- + " with resource ID #0x" + Integer.toHexString(id), e);
- nfe.setStackTrace(new StackTraceElement[0]);
- throw nfe;
- }
- }
-
- private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
- Theme theme, boolean usesTheme, long key, Drawable dr) {
- final ConstantState cs = dr.getConstantState();
- if (cs == null) {
- return;
- }
-
- if (mPreloading) {
- final int changingConfigs = cs.getChangingConfigurations();
- if (isColorDrawable) {
- if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
- sPreloadedColorDrawables.put(key, cs);
- }
- } else {
- if (verifyPreloadConfig(
- changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
- if ((changingConfigs & LAYOUT_DIR_CONFIG) == 0) {
- // If this resource does not vary based on layout direction,
- // we can put it in all of the preload maps.
- sPreloadedDrawables[0].put(key, cs);
- sPreloadedDrawables[1].put(key, cs);
- } else {
- // Otherwise, only in the layout dir we loaded it for.
- sPreloadedDrawables[mConfiguration.getLayoutDirection()].put(key, cs);
- }
- }
- }
- } else {
- synchronized (mAccessLock) {
- caches.put(key, theme, cs, usesTheme);
- }
- }
- }
-
- /**
- * Loads a drawable from XML or resources stream.
- */
- private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
- if (value.string == null) {
- throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
- + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
- }
-
- final String file = value.string.toString();
-
- if (TRACE_FOR_MISS_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) {
- Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
- + ": " + name + " at " + file);
- }
- }
- }
-
- if (DEBUG_LOAD) {
- Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
- }
-
- final Drawable dr;
-
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
- try {
- if (file.endsWith(".xml")) {
- final XmlResourceParser rp = loadXmlResourceParser(
- file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXml(this, rp, theme);
- rp.close();
- } else {
- final InputStream is = mAssets.openNonAsset(
- value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- dr = Drawable.createFromResourceStream(this, value, is, file, null);
- is.close();
- }
- } catch (Exception e) {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
- final NotFoundException rnf = new NotFoundException(
- "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
-
- return dr;
- }
-
- /**
- * Given the value and id, we can get the XML filename as in value.data, based on that, we
- * first try to load CSL from the cache. If not found, try to get from the constant state.
- * Last, parse the XML and generate the CSL.
- */
- private ComplexColor loadComplexColorFromName(Theme theme, TypedValue value, int id) {
- final long key = (((long) value.assetCookie) << 32) | value.data;
- final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
- ComplexColor complexColor = cache.getInstance(key, theme);
- if (complexColor != null) {
- return complexColor;
- }
-
- final android.content.res.ConstantState<ComplexColor> factory =
- sPreloadedComplexColors.get(key);
-
- if (factory != null) {
- complexColor = factory.newInstance(this, theme);
- }
- if (complexColor == null) {
- complexColor = loadComplexColorForCookie(value, id, theme);
- }
-
- if (complexColor != null) {
- if (mPreloading) {
- if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
- "color")) {
- sPreloadedComplexColors.put(key, complexColor.getConstantState());
- }
- } else {
- cache.put(key, theme, complexColor.getConstantState());
- }
- }
- return complexColor;
- }
-
- @Nullable
- public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, Theme theme) {
- if (TRACE_FOR_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) android.util.Log.d("loadComplexColor", name);
- }
- }
-
- final long key = (((long) value.assetCookie) << 32) | value.data;
-
- // Handle inline color definitions.
- if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
- && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
- return getColorStateListFromInt(value, key);
- }
-
- final String file = value.string.toString();
-
- ComplexColor complexColor;
- if (file.endsWith(".xml")) {
- try {
- complexColor = loadComplexColorFromName(theme, value, id);
- } catch (Exception e) {
- final NotFoundException rnf = new NotFoundException(
- "File " + file + " from complex color resource ID #0x"
- + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- } else {
- throw new NotFoundException(
- "File " + file + " from drawable resource ID #0x"
- + Integer.toHexString(id) + ": .xml extension required");
- }
-
- return complexColor;
- }
-
- @Nullable
- ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
- throws NotFoundException {
- if (TRACE_FOR_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) android.util.Log.d("PreloadColorStateList", name);
- }
- }
-
- final long key = (((long) value.assetCookie) << 32) | value.data;
-
- // Handle inline color definitions.
- if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
- && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
- return getColorStateListFromInt(value, key);
- }
-
- ComplexColor complexColor = loadComplexColorFromName(theme, value, id);
- if (complexColor != null && complexColor instanceof ColorStateList) {
- return (ColorStateList) complexColor;
- }
-
- throw new NotFoundException(
- "Can't find ColorStateList from drawable resource ID #0x"
- + Integer.toHexString(id));
- }
-
- @NonNull
- private ColorStateList getColorStateListFromInt(@NonNull TypedValue value, long key) {
- ColorStateList csl;
- final android.content.res.ConstantState<ComplexColor> factory =
- sPreloadedComplexColors.get(key);
- if (factory != null) {
- return (ColorStateList) factory.newInstance();
- }
-
- csl = ColorStateList.valueOf(value.data);
-
- if (mPreloading) {
- if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
- "color")) {
- sPreloadedComplexColors.put(key, csl.getConstantState());
- }
- }
-
- return csl;
- }
-
- /**
- * Load a ComplexColor based on the XML file content. The result can be a GradientColor or
- * ColorStateList. Note that pure color will be wrapped into a ColorStateList.
- *
- * We deferred the parser creation to this function b/c we need to differentiate b/t gradient
- * and selector tag.
- *
- * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
- */
- @Nullable
- private ComplexColor loadComplexColorForCookie(TypedValue value, int id, Theme theme) {
- if (value.string == null) {
- throw new UnsupportedOperationException(
- "Can't convert to ComplexColor: type=0x" + value.type);
- }
-
- final String file = value.string.toString();
-
- if (TRACE_FOR_MISS_PRELOAD) {
- // Log only framework resources
- if ((id >>> 24) == 0x1) {
- final String name = getResourceName(id);
- if (name != null) {
- Log.d(TAG, "Loading framework ComplexColor #" + Integer.toHexString(id)
- + ": " + name + " at " + file);
- }
- }
- }
-
- if (DEBUG_LOAD) {
- Log.v(TAG, "Loading ComplexColor for cookie " + value.assetCookie + ": " + file);
- }
-
- ComplexColor complexColor = null;
-
- Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
- if (file.endsWith(".xml")) {
- try {
- final XmlResourceParser parser = loadXmlResourceParser(
- file, id, value.assetCookie, "ComplexColor");
-
- final AttributeSet attrs = Xml.asAttributeSet(parser);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- // Seek parser to start tag.
- }
- if (type != XmlPullParser.START_TAG) {
- throw new XmlPullParserException("No start tag found");
- }
-
- final String name = parser.getName();
- if (name.equals("gradient")) {
- complexColor = GradientColor.createFromXmlInner(this, parser, attrs, theme);
- } else if (name.equals("selector")) {
- complexColor = ColorStateList.createFromXmlInner(this, parser, attrs, theme);
- }
- parser.close();
- } catch (Exception e) {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
- final NotFoundException rnf = new NotFoundException(
- "File " + file + " from ComplexColor resource ID #0x"
- + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- } else {
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
- throw new NotFoundException(
- "File " + file + " from drawable resource ID #0x"
- + Integer.toHexString(id) + ": .xml extension required");
- }
- Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
-
- return complexColor;
+ return mResourcesImpl.getPreloadedDrawables();
}
/**
@@ -2829,10 +2081,12 @@
@NonNull
XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
throws NotFoundException {
- final TypedValue value = obtainTempTypedValue(id);
+ final TypedValue value = obtainTempTypedValue();
try {
+ final ResourcesImpl impl = mResourcesImpl;
+ impl.getValue(id, value, true);
if (value.type == TypedValue.TYPE_STRING) {
- return loadXmlResourceParser(value.string.toString(), id,
+ return impl.loadXmlResourceParser(value.string.toString(), id,
value.assetCookie, type);
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
@@ -2853,49 +2107,18 @@
* @throws NotFoundException if the file could not be loaded
*/
@NonNull
- XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id,
- int assetCookie, @NonNull String type) throws NotFoundException {
- if (id != 0) {
- try {
- synchronized (mCachedXmlBlocks) {
- final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies;
- final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
- final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
- // First see if this block is in our cache.
- final int num = cachedXmlBlockFiles.length;
- for (int i = 0; i < num; i++) {
- if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
- && cachedXmlBlockFiles[i].equals(file)) {
- return cachedXmlBlocks[i].newParser();
- }
- }
+ XmlResourceParser loadXmlResourceParser(String file, int id, int assetCookie,
+ String type) throws NotFoundException {
+ return mResourcesImpl.loadXmlResourceParser(file, id, assetCookie, type);
+ }
- // Not in the cache, create a new block and put it at
- // the next slot in the cache.
- final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
- if (block != null) {
- final int pos = (mLastCachedXmlBlockIndex + 1) % num;
- mLastCachedXmlBlockIndex = pos;
- final XmlBlock oldBlock = cachedXmlBlocks[pos];
- if (oldBlock != null) {
- oldBlock.close();
- }
- cachedXmlBlockCookies[pos] = assetCookie;
- cachedXmlBlockFiles[pos] = file;
- cachedXmlBlocks[pos] = block;
- return block.newParser();
- }
- }
- } catch (Exception e) {
- final NotFoundException rnf = new NotFoundException("File " + file
- + " from xml type " + type + " resource ID #0x" + Integer.toHexString(id));
- rnf.initCause(e);
- throw rnf;
- }
- }
-
- throw new NotFoundException("File " + file + " from xml type " + type + " resource ID #0x"
- + Integer.toHexString(id));
+ /**
+ * Called by ConfigurationBoundResourceCacheTest.
+ * @hide
+ */
+ @VisibleForTesting
+ public int calcConfigChanges(Configuration config) {
+ return mResourcesImpl.calcConfigChanges(config);
}
/**
@@ -2911,16 +2134,4 @@
}
return theme.obtainStyledAttributes(set, attrs, 0, 0);
}
-
- private Resources() {
- mAssets = AssetManager.getSystem();
- mClassLoader = ClassLoader.getSystemClassLoader();
- // NOTE: Intentionally leaving this uninitialized (all values set
- // to zero), so that anyone who tries to do something that requires
- // metrics will get a very wrong value.
- mConfiguration.setToDefaults();
- mMetrics.setToDefaults();
- updateConfiguration(null, null);
- mAssets.ensureStringBlocks();
- }
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
new file mode 100644
index 0000000..a364010
--- /dev/null
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -0,0 +1,1165 @@
+/*
+ * 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.content.res;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.animation.Animator;
+import android.animation.StateListAnimator;
+import android.annotation.AnyRes;
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.PluralsRes;
+import android.annotation.RawRes;
+import android.annotation.StyleRes;
+import android.annotation.StyleableRes;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.icu.text.PluralRules;
+import android.os.Build;
+import android.os.Trace;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.LocaleList;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.util.Xml;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * The implementation of Resource access. This class contains the AssetManager and all caches
+ * associated with it.
+ *
+ * {@link Resources} is just a thing wrapper around this class. When a configuration change
+ * occurs, clients can retain the same {@link Resources} reference because the underlying
+ * {@link ResourcesImpl} object will be updated or re-created.
+ *
+ * @hide
+ */
+public class ResourcesImpl {
+ static final String TAG = "Resources";
+
+ private static final boolean DEBUG_LOAD = false;
+ private static final boolean DEBUG_CONFIG = false;
+ private static final boolean TRACE_FOR_PRELOAD = false;
+ private static final boolean TRACE_FOR_MISS_PRELOAD = false;
+
+ private static final int LAYOUT_DIR_CONFIG = ActivityInfo.activityInfoConfigJavaToNative(
+ ActivityInfo.CONFIG_LAYOUT_DIRECTION);
+
+ private static final int ID_OTHER = 0x01000004;
+
+ private static final Object sSync = new Object();
+
+ private static boolean sPreloaded;
+ private boolean mPreloading;
+
+ // Information about preloaded resources. Note that they are not
+ // protected by a lock, because while preloading in zygote we are all
+ // single-threaded, and after that these are immutable.
+ private static final LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables;
+ private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables
+ = new LongSparseArray<>();
+ private static final LongSparseArray<android.content.res.ConstantState<ComplexColor>>
+ sPreloadedComplexColors = new LongSparseArray<>();
+
+ /** Lock object used to protect access to caches and configuration. */
+ private final Object mAccessLock = new Object();
+
+ // These are protected by mAccessLock.
+ private final Configuration mTmpConfig = new Configuration();
+ private final DrawableCache mDrawableCache = new DrawableCache();
+ private final DrawableCache mColorDrawableCache = new DrawableCache();
+ private final ConfigurationBoundResourceCache<ComplexColor> mComplexColorCache =
+ new ConfigurationBoundResourceCache<>();
+ private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
+ new ConfigurationBoundResourceCache<>();
+ private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
+ new ConfigurationBoundResourceCache<>();
+
+ /** Size of the cyclical cache used to map XML files to blocks. */
+ private static final int XML_BLOCK_CACHE_SIZE = 4;
+
+ // Cyclical cache used for recently-accessed XML files.
+ private int mLastCachedXmlBlockIndex = -1;
+ private final int[] mCachedXmlBlockCookies = new int[XML_BLOCK_CACHE_SIZE];
+ private final String[] mCachedXmlBlockFiles = new String[XML_BLOCK_CACHE_SIZE];
+ private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[XML_BLOCK_CACHE_SIZE];
+
+
+ final AssetManager mAssets;
+ final DisplayMetrics mMetrics = new DisplayMetrics();
+
+ private PluralRules mPluralRule;
+
+ private final Configuration mConfiguration = new Configuration();
+ private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+
+ static {
+ sPreloadedDrawables = new LongSparseArray[2];
+ sPreloadedDrawables[0] = new LongSparseArray<>();
+ sPreloadedDrawables[1] = new LongSparseArray<>();
+ }
+
+ /**
+ * Creates a new ResourcesImpl object with CompatibilityInfo.
+ *
+ * @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).
+ * @param compatInfo this resource's compatibility info. Must not be null.
+ */
+ public ResourcesImpl(AssetManager assets, DisplayMetrics metrics, Configuration config,
+ CompatibilityInfo compatInfo) {
+ mAssets = assets;
+ mMetrics.setToDefaults();
+ updateConfiguration(config, metrics, compatInfo);
+ mAssets.ensureStringBlocks();
+ }
+
+ public AssetManager getAssets() {
+ return mAssets;
+ }
+
+ DisplayMetrics getDisplayMetrics() {
+ if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
+ + "x" + mMetrics.heightPixels + " " + mMetrics.density);
+ return mMetrics;
+ }
+
+ Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ Configuration[] getSizeConfigurations() {
+ return mAssets.getSizeConfigurations();
+ }
+
+ CompatibilityInfo getCompatibilityInfo() {
+ return mCompatibilityInfo;
+ }
+
+ private PluralRules getPluralRule() {
+ synchronized (sSync) {
+ if (mPluralRule == null) {
+ mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
+ }
+ return mPluralRule;
+ }
+ }
+
+ void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
+ throws NotFoundException {
+ boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
+ if (found) {
+ return;
+ }
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
+ }
+
+ void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
+ boolean resolveRefs) throws NotFoundException {
+ boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
+ if (found) {
+ return;
+ }
+ throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
+ }
+
+ void getValue(String name, TypedValue outValue, boolean resolveRefs)
+ throws NotFoundException {
+ int id = getIdentifier(name, "string", null);
+ if (id != 0) {
+ getValue(id, outValue, resolveRefs);
+ return;
+ }
+ throw new NotFoundException("String resource name " + name);
+ }
+
+ int getIdentifier(String name, String defType, String defPackage) {
+ if (name == null) {
+ throw new NullPointerException("name is null");
+ }
+ try {
+ return Integer.parseInt(name);
+ } catch (Exception e) {
+ // Ignore
+ }
+ return mAssets.getResourceIdentifier(name, defType, defPackage);
+ }
+
+ @NonNull
+ String getResourceName(@AnyRes int resid) throws NotFoundException {
+ String str = mAssets.getResourceName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ @NonNull
+ String getResourcePackageName(@AnyRes int resid) throws NotFoundException {
+ String str = mAssets.getResourcePackageName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ @NonNull
+ String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
+ String str = mAssets.getResourceTypeName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ @NonNull
+ String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
+ String str = mAssets.getResourceEntryName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ @NonNull
+ CharSequence getQuantityText(@PluralsRes int id, int quantity) throws NotFoundException {
+ PluralRules rule = getPluralRule();
+ CharSequence res = mAssets.getResourceBagText(id,
+ attrForQuantityCode(rule.select(quantity)));
+ if (res != null) {
+ return res;
+ }
+ res = mAssets.getResourceBagText(id, ID_OTHER);
+ if (res != null) {
+ return res;
+ }
+ throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
+ + " quantity=" + quantity
+ + " item=" + rule.select(quantity));
+ }
+
+ private static int attrForQuantityCode(String quantityCode) {
+ switch (quantityCode) {
+ case PluralRules.KEYWORD_ZERO: return 0x01000005;
+ case PluralRules.KEYWORD_ONE: return 0x01000006;
+ case PluralRules.KEYWORD_TWO: return 0x01000007;
+ case PluralRules.KEYWORD_FEW: return 0x01000008;
+ case PluralRules.KEYWORD_MANY: return 0x01000009;
+ default: return ID_OTHER;
+ }
+ }
+
+ @NonNull
+ AssetFileDescriptor openRawResourceFd(@RawRes int id, TypedValue tempValue)
+ throws NotFoundException {
+ getValue(id, tempValue, true);
+ try {
+ return mAssets.openNonAssetFd(tempValue.assetCookie, tempValue.string.toString());
+ } catch (Exception e) {
+ throw new NotFoundException("File " + tempValue.string.toString() + " from drawable "
+ + "resource ID #0x" + Integer.toHexString(id), e);
+ }
+ }
+
+ @NonNull
+ InputStream openRawResource(@RawRes int id, TypedValue value) throws NotFoundException {
+ getValue(id, value, true);
+ try {
+ return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
+ AssetManager.ACCESS_STREAMING);
+ } catch (Exception e) {
+ NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
+ " from drawable resource ID #0x" + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ }
+
+ ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
+ return mAnimatorCache;
+ }
+
+ ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
+ return mStateListAnimatorCache;
+ }
+
+ public void updateConfiguration(Configuration config, DisplayMetrics metrics,
+ CompatibilityInfo compat) {
+ synchronized (mAccessLock) {
+ if (false) {
+ Slog.i(TAG, "**** Updating config of " + this + ": old config is "
+ + mConfiguration + " old compat is " + mCompatibilityInfo);
+ Slog.i(TAG, "**** Updating config of " + this + ": new config is "
+ + config + " new compat is " + compat);
+ }
+ if (compat != null) {
+ mCompatibilityInfo = compat;
+ }
+ if (metrics != null) {
+ mMetrics.setTo(metrics);
+ }
+ // NOTE: We should re-arrange this code to create a Display
+ // with the CompatibilityInfo that is used everywhere we deal
+ // with the display in relation to this app, rather than
+ // doing the conversion here. This impl should be okay because
+ // we make sure to return a compatible display in the places
+ // where there are public APIs to retrieve the display... but
+ // it would be cleaner and more maintainble to just be
+ // consistently dealing with a compatible display everywhere in
+ // the framework.
+ mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
+
+ final @Config int configChanges = calcConfigChanges(config);
+
+ LocaleList locales = mConfiguration.getLocales();
+ if (locales.isEmpty()) {
+ locales = LocaleList.getAdjustedDefault();
+ mConfiguration.setLocales(locales);
+ }
+ if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
+ mMetrics.densityDpi = mConfiguration.densityDpi;
+ mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ }
+ mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
+
+ final int width, height;
+ if (mMetrics.widthPixels >= mMetrics.heightPixels) {
+ width = mMetrics.widthPixels;
+ height = mMetrics.heightPixels;
+ } else {
+ //noinspection SuspiciousNameCombination
+ width = mMetrics.heightPixels;
+ //noinspection SuspiciousNameCombination
+ height = mMetrics.widthPixels;
+ }
+
+ final int keyboardHidden;
+ if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
+ && mConfiguration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
+ keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
+ } else {
+ keyboardHidden = mConfiguration.keyboardHidden;
+ }
+
+ mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
+ adjustLanguageTag(locales.get(0).toLanguageTag()),
+ mConfiguration.orientation,
+ mConfiguration.touchscreen,
+ mConfiguration.densityDpi, mConfiguration.keyboard,
+ keyboardHidden, mConfiguration.navigation, width, height,
+ mConfiguration.smallestScreenWidthDp,
+ mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
+ mConfiguration.screenLayout, mConfiguration.uiMode,
+ Build.VERSION.RESOURCES_SDK_INT);
+
+ if (DEBUG_CONFIG) {
+ Slog.i(TAG, "**** Updating config of " + this + ": final config is "
+ + mConfiguration + " final compat is " + mCompatibilityInfo);
+ }
+
+ mDrawableCache.onConfigurationChange(configChanges);
+ mColorDrawableCache.onConfigurationChange(configChanges);
+ mComplexColorCache.onConfigurationChange(configChanges);
+ mAnimatorCache.onConfigurationChange(configChanges);
+ mStateListAnimatorCache.onConfigurationChange(configChanges);
+
+ flushLayoutCache();
+ }
+ synchronized (sSync) {
+ if (mPluralRule != null) {
+ mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
+ }
+ }
+ }
+
+ /**
+ * Applies the new configuration, returning a bitmask of the changes
+ * between the old and new configurations.
+ *
+ * @param config the new configuration
+ * @return bitmask of config changes
+ */
+ public @Config int calcConfigChanges(@Nullable Configuration config) {
+ if (config == null) {
+ // If there is no configuration, assume all flags have changed.
+ return 0xFFFFFFFF;
+ }
+
+ mTmpConfig.setTo(config);
+ int density = config.densityDpi;
+ if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+ density = mMetrics.noncompatDensityDpi;
+ }
+
+ mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
+
+ if (mTmpConfig.getLocales().isEmpty()) {
+ mTmpConfig.setLocales(LocaleList.getDefault());
+ }
+ return mConfiguration.updateFrom(mTmpConfig);
+ }
+
+ /**
+ * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
+ * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
+ *
+ * All released versions of android prior to "L" used the deprecated language
+ * tags, so we will need to support them for backwards compatibility.
+ *
+ * Note that this conversion needs to take place *after* the call to
+ * {@code toLanguageTag} because that will convert all the deprecated codes to
+ * the new ones, even if they're set manually.
+ */
+ private static String adjustLanguageTag(String languageTag) {
+ final int separator = languageTag.indexOf('-');
+ final String language;
+ final String remainder;
+
+ if (separator == -1) {
+ language = languageTag;
+ remainder = "";
+ } else {
+ language = languageTag.substring(0, separator);
+ remainder = languageTag.substring(separator);
+ }
+
+ return Locale.adjustLanguageCode(language) + remainder;
+ }
+
+ /**
+ * Call this to remove all cached loaded layout resources from the
+ * Resources object. Only intended for use with performance testing
+ * tools.
+ */
+ public void flushLayoutCache() {
+ synchronized (mCachedXmlBlocks) {
+ Arrays.fill(mCachedXmlBlockCookies, 0);
+ Arrays.fill(mCachedXmlBlockFiles, null);
+
+ final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
+ for (int i = 0; i < XML_BLOCK_CACHE_SIZE; i++) {
+ final XmlBlock oldBlock = cachedXmlBlocks[i];
+ if (oldBlock != null) {
+ oldBlock.close();
+ }
+ }
+ Arrays.fill(cachedXmlBlocks, null);
+ }
+ }
+
+ @Nullable
+ Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,
+ boolean useCache) throws NotFoundException {
+ try {
+ if (TRACE_FOR_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) {
+ Log.d("PreloadDrawable", name);
+ }
+ }
+ }
+
+ final boolean isColorDrawable;
+ final DrawableCache caches;
+ final long key;
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+ && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ isColorDrawable = true;
+ caches = mColorDrawableCache;
+ key = value.data;
+ } else {
+ isColorDrawable = false;
+ caches = mDrawableCache;
+ key = (((long) value.assetCookie) << 32) | value.data;
+ }
+
+ // First, check whether we have a cached version of this drawable
+ // that was inflated against the specified theme. Skip the cache if
+ // we're currently preloading or we're not using the cache.
+ if (!mPreloading && useCache) {
+ final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
+ if (cachedDrawable != null) {
+ return cachedDrawable;
+ }
+ }
+
+ // Next, check preloaded drawables. Preloaded drawables may contain
+ // unresolved theme attributes.
+ final Drawable.ConstantState cs;
+ if (isColorDrawable) {
+ cs = sPreloadedColorDrawables.get(key);
+ } else {
+ cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
+ }
+
+ Drawable dr;
+ if (cs != null) {
+ dr = cs.newDrawable(wrapper);
+ } else if (isColorDrawable) {
+ dr = new ColorDrawable(value.data);
+ } else {
+ dr = loadDrawableForCookie(wrapper, value, id, null);
+ }
+
+ // Determine if the drawable has unresolved theme attributes. If it
+ // does, we'll need to apply a theme and store it in a theme-specific
+ // cache.
+ final boolean canApplyTheme = dr != null && dr.canApplyTheme();
+ if (canApplyTheme && theme != null) {
+ dr = dr.mutate();
+ dr.applyTheme(theme);
+ dr.clearMutated();
+ }
+
+ // If we were able to obtain a drawable, store it in the appropriate
+ // cache: preload, not themed, null theme, or theme-specific. Don't
+ // pollute the cache with drawables loaded from a foreign density.
+ if (dr != null && useCache) {
+ dr.setChangingConfigurations(value.changingConfigurations);
+ cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
+ }
+
+ return dr;
+ } catch (Exception e) {
+ String name;
+ try {
+ name = getResourceName(id);
+ } catch (NotFoundException e2) {
+ name = "(missing name)";
+ }
+
+ // The target drawable might fail to load for any number of
+ // reasons, but we always want to include the resource name.
+ // Since the client already expects this method to throw a
+ // NotFoundException, just throw one of those.
+ final NotFoundException nfe = new NotFoundException("Drawable " + name
+ + " with resource ID #0x" + Integer.toHexString(id), e);
+ nfe.setStackTrace(new StackTraceElement[0]);
+ throw nfe;
+ }
+ }
+
+ private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
+ Resources.Theme theme, boolean usesTheme, long key, Drawable dr) {
+ final Drawable.ConstantState cs = dr.getConstantState();
+ if (cs == null) {
+ return;
+ }
+
+ if (mPreloading) {
+ final int changingConfigs = cs.getChangingConfigurations();
+ if (isColorDrawable) {
+ if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
+ sPreloadedColorDrawables.put(key, cs);
+ }
+ } else {
+ if (verifyPreloadConfig(
+ changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
+ if ((changingConfigs & LAYOUT_DIR_CONFIG) == 0) {
+ // If this resource does not vary based on layout direction,
+ // we can put it in all of the preload maps.
+ sPreloadedDrawables[0].put(key, cs);
+ sPreloadedDrawables[1].put(key, cs);
+ } else {
+ // Otherwise, only in the layout dir we loaded it for.
+ sPreloadedDrawables[mConfiguration.getLayoutDirection()].put(key, cs);
+ }
+ }
+ }
+ } else {
+ synchronized (mAccessLock) {
+ caches.put(key, theme, cs, usesTheme);
+ }
+ }
+ }
+
+ private boolean verifyPreloadConfig(@Config int changingConfigurations,
+ @Config int allowVarying, @AnyRes int resourceId, @Nullable String name) {
+ // We allow preloading of resources even if they vary by font scale (which
+ // doesn't impact resource selection) or density (which we handle specially by
+ // simply turning off all preloading), as well as any other configs specified
+ // by the caller.
+ if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
+ ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) {
+ String resName;
+ try {
+ resName = getResourceName(resourceId);
+ } catch (NotFoundException e) {
+ resName = "?";
+ }
+ // This should never happen in production, so we should log a
+ // warning even if we're not debugging.
+ Log.w(TAG, "Preloaded " + name + " resource #0x"
+ + Integer.toHexString(resourceId)
+ + " (" + resName + ") that varies with configuration!!");
+ return false;
+ }
+ if (TRACE_FOR_PRELOAD) {
+ String resName;
+ try {
+ resName = getResourceName(resourceId);
+ } catch (NotFoundException e) {
+ resName = "?";
+ }
+ Log.w(TAG, "Preloading " + name + " resource #0x"
+ + Integer.toHexString(resourceId)
+ + " (" + resName + ")");
+ }
+ return true;
+ }
+
+ /**
+ * Loads a drawable from XML or resources stream.
+ */
+ private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,
+ Resources.Theme theme) {
+ if (value.string == null) {
+ throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
+ }
+
+ final String file = value.string.toString();
+
+ if (TRACE_FOR_MISS_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) {
+ Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
+ + ": " + name + " at " + file);
+ }
+ }
+ }
+
+ if (DEBUG_LOAD) {
+ Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
+ }
+
+ final Drawable dr;
+
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+ try {
+ if (file.endsWith(".xml")) {
+ final XmlResourceParser rp = loadXmlResourceParser(
+ file, id, value.assetCookie, "drawable");
+ dr = Drawable.createFromXml(wrapper, rp, theme);
+ rp.close();
+ } else {
+ final InputStream is = mAssets.openNonAsset(
+ value.assetCookie, file, AssetManager.ACCESS_STREAMING);
+ dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
+ is.close();
+ }
+ } catch (Exception e) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ final NotFoundException rnf = new NotFoundException(
+ "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+
+ return dr;
+ }
+
+ /**
+ * Given the value and id, we can get the XML filename as in value.data, based on that, we
+ * first try to load CSL from the cache. If not found, try to get from the constant state.
+ * Last, parse the XML and generate the CSL.
+ */
+ private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
+ TypedValue value, int id) {
+ final long key = (((long) value.assetCookie) << 32) | value.data;
+ final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
+ ComplexColor complexColor = cache.getInstance(key, wrapper, theme);
+ if (complexColor != null) {
+ return complexColor;
+ }
+
+ final android.content.res.ConstantState<ComplexColor> factory =
+ sPreloadedComplexColors.get(key);
+
+ if (factory != null) {
+ complexColor = factory.newInstance(wrapper, theme);
+ }
+ if (complexColor == null) {
+ complexColor = loadComplexColorForCookie(wrapper, value, id, theme);
+ }
+
+ if (complexColor != null) {
+ complexColor.setBaseChangingConfigurations(value.changingConfigurations);
+
+ if (mPreloading) {
+ if (verifyPreloadConfig(complexColor.getChangingConfigurations(),
+ 0, value.resourceId, "color")) {
+ sPreloadedComplexColors.put(key, complexColor.getConstantState());
+ }
+ } else {
+ cache.put(key, theme, complexColor.getConstantState());
+ }
+ }
+ return complexColor;
+ }
+
+ @Nullable
+ ComplexColor loadComplexColor(Resources wrapper, @NonNull TypedValue value, int id,
+ Resources.Theme theme) {
+ if (TRACE_FOR_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) android.util.Log.d("loadComplexColor", name);
+ }
+ }
+
+ final long key = (((long) value.assetCookie) << 32) | value.data;
+
+ // Handle inline color definitions.
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+ && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ return getColorStateListFromInt(value, key);
+ }
+
+ final String file = value.string.toString();
+
+ ComplexColor complexColor;
+ if (file.endsWith(".xml")) {
+ try {
+ complexColor = loadComplexColorFromName(wrapper, theme, value, id);
+ } catch (Exception e) {
+ final NotFoundException rnf = new NotFoundException(
+ "File " + file + " from complex color resource ID #0x"
+ + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ } else {
+ throw new NotFoundException(
+ "File " + file + " from drawable resource ID #0x"
+ + Integer.toHexString(id) + ": .xml extension required");
+ }
+
+ return complexColor;
+ }
+
+ @Nullable
+ ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
+ Resources.Theme theme)
+ throws NotFoundException {
+ if (TRACE_FOR_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) android.util.Log.d("PreloadColorStateList", name);
+ }
+ }
+
+ final long key = (((long) value.assetCookie) << 32) | value.data;
+
+ // Handle inline color definitions.
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
+ && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ return getColorStateListFromInt(value, key);
+ }
+
+ ComplexColor complexColor = loadComplexColorFromName(wrapper, theme, value, id);
+ if (complexColor != null && complexColor instanceof ColorStateList) {
+ return (ColorStateList) complexColor;
+ }
+
+ throw new NotFoundException(
+ "Can't find ColorStateList from drawable resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ @NonNull
+ private ColorStateList getColorStateListFromInt(@NonNull TypedValue value, long key) {
+ ColorStateList csl;
+ final android.content.res.ConstantState<ComplexColor> factory =
+ sPreloadedComplexColors.get(key);
+ if (factory != null) {
+ return (ColorStateList) factory.newInstance();
+ }
+
+ csl = ColorStateList.valueOf(value.data);
+
+ if (mPreloading) {
+ if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
+ "color")) {
+ sPreloadedComplexColors.put(key, csl.getConstantState());
+ }
+ }
+
+ return csl;
+ }
+
+ /**
+ * Load a ComplexColor based on the XML file content. The result can be a GradientColor or
+ * ColorStateList. Note that pure color will be wrapped into a ColorStateList.
+ *
+ * We deferred the parser creation to this function b/c we need to differentiate b/t gradient
+ * and selector tag.
+ *
+ * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
+ */
+ @Nullable
+ private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
+ Resources.Theme theme) {
+ if (value.string == null) {
+ throw new UnsupportedOperationException(
+ "Can't convert to ComplexColor: type=0x" + value.type);
+ }
+
+ final String file = value.string.toString();
+
+ if (TRACE_FOR_MISS_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) {
+ Log.d(TAG, "Loading framework ComplexColor #" + Integer.toHexString(id)
+ + ": " + name + " at " + file);
+ }
+ }
+ }
+
+ if (DEBUG_LOAD) {
+ Log.v(TAG, "Loading ComplexColor for cookie " + value.assetCookie + ": " + file);
+ }
+
+ ComplexColor complexColor = null;
+
+ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
+ if (file.endsWith(".xml")) {
+ try {
+ final XmlResourceParser parser = loadXmlResourceParser(
+ file, id, value.assetCookie, "ComplexColor");
+
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ // Seek parser to start tag.
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ final String name = parser.getName();
+ if (name.equals("gradient")) {
+ complexColor = GradientColor.createFromXmlInner(wrapper, parser, attrs, theme);
+ } else if (name.equals("selector")) {
+ complexColor = ColorStateList.createFromXmlInner(wrapper, parser, attrs, theme);
+ }
+ parser.close();
+ } catch (Exception e) {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ final NotFoundException rnf = new NotFoundException(
+ "File " + file + " from ComplexColor resource ID #0x"
+ + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ } else {
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+ throw new NotFoundException(
+ "File " + file + " from drawable resource ID #0x"
+ + Integer.toHexString(id) + ": .xml extension required");
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+
+ return complexColor;
+ }
+
+ /**
+ * Loads an XML parser for the specified file.
+ *
+ * @param file the path for the XML file to parse
+ * @param id the resource identifier for the file
+ * @param assetCookie the asset cookie for the file
+ * @param type the type of resource (used for logging)
+ * @return a parser for the specified XML file
+ * @throws NotFoundException if the file could not be loaded
+ */
+ @NonNull
+ XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id, int assetCookie,
+ @NonNull String type)
+ throws NotFoundException {
+ if (id != 0) {
+ try {
+ synchronized (mCachedXmlBlocks) {
+ final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies;
+ final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
+ final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
+ // First see if this block is in our cache.
+ final int num = cachedXmlBlockFiles.length;
+ for (int i = 0; i < num; i++) {
+ if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
+ && cachedXmlBlockFiles[i].equals(file)) {
+ return cachedXmlBlocks[i].newParser();
+ }
+ }
+
+ // Not in the cache, create a new block and put it at
+ // the next slot in the cache.
+ final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
+ if (block != null) {
+ final int pos = (mLastCachedXmlBlockIndex + 1) % num;
+ mLastCachedXmlBlockIndex = pos;
+ final XmlBlock oldBlock = cachedXmlBlocks[pos];
+ if (oldBlock != null) {
+ oldBlock.close();
+ }
+ cachedXmlBlockCookies[pos] = assetCookie;
+ cachedXmlBlockFiles[pos] = file;
+ cachedXmlBlocks[pos] = block;
+ return block.newParser();
+ }
+ }
+ } catch (Exception e) {
+ final NotFoundException rnf = new NotFoundException("File " + file
+ + " from xml type " + type + " resource ID #0x" + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ }
+
+ throw new NotFoundException("File " + file + " from xml type " + type + " resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ /**
+ * Start preloading of resource data using this Resources object. Only
+ * for use by the zygote process for loading common system resources.
+ * {@hide}
+ */
+ public final void startPreloading() {
+ synchronized (sSync) {
+ if (sPreloaded) {
+ throw new IllegalStateException("Resources already preloaded");
+ }
+ sPreloaded = true;
+ mPreloading = true;
+ mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;
+ updateConfiguration(null, null, null);
+ }
+ }
+
+ /**
+ * Called by zygote when it is done preloading resources, to change back
+ * to normal Resources operation.
+ */
+ void finishPreloading() {
+ if (mPreloading) {
+ mPreloading = false;
+ flushLayoutCache();
+ }
+ }
+
+ LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
+ return sPreloadedDrawables[0];
+ }
+
+ ThemeImpl newThemeImpl() {
+ return new ThemeImpl();
+ }
+
+ /**
+ * Creates a new ThemeImpl which is already set to the given Resources.ThemeKey.
+ */
+ ThemeImpl newThemeImpl(Resources.ThemeKey key) {
+ ThemeImpl impl = new ThemeImpl();
+ impl.mKey.setTo(key);
+ impl.rebase();
+ return impl;
+ }
+
+ public class ThemeImpl {
+ /**
+ * Unique key for the series of styles applied to this theme.
+ */
+ private final Resources.ThemeKey mKey = new Resources.ThemeKey();
+
+ @SuppressWarnings("hiding")
+ private final AssetManager mAssets;
+ private final long mTheme;
+
+ /**
+ * Resource identifier for the theme.
+ */
+ private int mThemeResId = 0;
+
+ /*package*/ ThemeImpl() {
+ mAssets = ResourcesImpl.this.mAssets;
+ mTheme = mAssets.createTheme();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ mAssets.releaseTheme(mTheme);
+ }
+
+ /*package*/ Resources.ThemeKey getKey() {
+ return mKey;
+ }
+
+ /*package*/ long getNativeTheme() {
+ return mTheme;
+ }
+
+ /*package*/ int getAppliedStyleResId() {
+ return mThemeResId;
+ }
+
+ void applyStyle(int resId, boolean force) {
+ synchronized (mKey) {
+ AssetManager.applyThemeStyle(mTheme, resId, force);
+
+ mThemeResId = resId;
+ mKey.append(resId, force);
+ }
+ }
+
+ void setTo(ThemeImpl other) {
+ synchronized (mKey) {
+ synchronized (other.mKey) {
+ AssetManager.copyTheme(mTheme, other.mTheme);
+
+ mThemeResId = other.mThemeResId;
+ mKey.setTo(other.getKey());
+ }
+ }
+ }
+
+ @NonNull
+ TypedArray obtainStyledAttributes(@NonNull Resources.Theme wrapper,
+ AttributeSet set,
+ @StyleableRes int[] attrs,
+ @AttrRes int defStyleAttr,
+ @StyleRes int defStyleRes) {
+ synchronized (mKey) {
+ final int len = attrs.length;
+ final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
+
+ // XXX note that for now we only work with compiled XML files.
+ // To support generic XML files we will need to manually parse
+ // out the attributes from the XML file (applying type information
+ // contained in the resources and such).
+ final XmlBlock.Parser parser = (XmlBlock.Parser) set;
+ AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
+ parser != null ? parser.mParseState : 0,
+ attrs, array.mData, array.mIndices);
+ array.mTheme = wrapper;
+ array.mXml = parser;
+
+ return array;
+ }
+ }
+
+ @NonNull
+ TypedArray resolveAttributes(@NonNull Resources.Theme wrapper,
+ @NonNull int[] values,
+ @NonNull int[] attrs) {
+ synchronized (mKey) {
+ final int len = attrs.length;
+ if (values == null || len != values.length) {
+ throw new IllegalArgumentException(
+ "Base attribute values must the same length as attrs");
+ }
+
+ final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
+ AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
+ array.mTheme = wrapper;
+ array.mXml = null;
+ return array;
+ }
+ }
+
+ boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
+ synchronized (mKey) {
+ return mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
+ }
+ }
+
+ int[] getAllAttributes() {
+ return mAssets.getStyleAttributes(getAppliedStyleResId());
+ }
+
+ @Config int getChangingConfigurations() {
+ synchronized (mKey) {
+ final int nativeChangingConfig =
+ AssetManager.getThemeChangingConfigurations(mTheme);
+ return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
+ }
+ }
+
+ public void dump(int priority, String tag, String prefix) {
+ synchronized (mKey) {
+ AssetManager.dumpTheme(mTheme, priority, tag, prefix);
+ }
+ }
+
+ String[] getTheme() {
+ synchronized (mKey) {
+ final int N = mKey.mCount;
+ final String[] themes = new String[N * 2];
+ for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
+ final int resId = mKey.mResId[j];
+ final boolean forced = mKey.mForce[j];
+ try {
+ themes[i] = getResourceName(resId);
+ } catch (NotFoundException e) {
+ themes[i] = Integer.toHexString(i);
+ }
+ themes[i + 1] = forced ? "forced" : "not forced";
+ }
+ return themes;
+ }
+ }
+
+ /**
+ * Rebases the theme against the parent Resource object's current
+ * configuration by re-applying the styles passed to
+ * {@link #applyStyle(int, boolean)}.
+ */
+ void rebase() {
+ synchronized (mKey) {
+ AssetManager.clearTheme(mTheme);
+
+ // Reapply the same styles in the same order.
+ for (int i = 0; i < mKey.mCount; i++) {
+ final int resId = mKey.mResId[i];
+ final boolean force = mKey.mForce[i];
+ AssetManager.applyThemeStyle(mTheme, resId, force);
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 2620571..e894492 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -17,32 +17,59 @@
package android.content.res;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import java.util.Arrays;
import java.util.Objects;
/** @hide */
public final class ResourcesKey {
- private final String mResDir;
- private final float mScale;
- private final int mHash;
+ @Nullable
+ public final String mResDir;
+
+ @Nullable
+ public final String[] mSplitResDirs;
+
+ @Nullable
+ public final String[] mOverlayDirs;
+
+ @Nullable
+ public final String[] mLibDirs;
public final int mDisplayId;
+
@NonNull
public final Configuration mOverrideConfiguration;
- public ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration,
- float scale) {
+ @NonNull
+ public final CompatibilityInfo mCompatInfo;
+
+ private final int mHash;
+
+ public ResourcesKey(@Nullable String resDir,
+ @Nullable String[] splitResDirs,
+ @Nullable String[] overlayDirs,
+ @Nullable String[] libDirs,
+ int displayId,
+ @Nullable Configuration overrideConfig,
+ @Nullable CompatibilityInfo compatInfo) {
mResDir = resDir;
+ mSplitResDirs = splitResDirs;
+ mOverlayDirs = overlayDirs;
+ mLibDirs = libDirs;
mDisplayId = displayId;
- mOverrideConfiguration = overrideConfiguration != null
- ? overrideConfiguration : Configuration.EMPTY;
- mScale = scale;
+ mOverrideConfiguration = overrideConfig != null ? overrideConfig : Configuration.EMPTY;
+ mCompatInfo = compatInfo != null ? compatInfo : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
int hash = 17;
- hash = 31 * hash + (mResDir == null ? 0 : mResDir.hashCode());
+ hash = 31 * hash + Objects.hashCode(mResDir);
+ hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
+ hash = 31 * hash + Arrays.hashCode(mOverlayDirs);
+ hash = 31 * hash + Arrays.hashCode(mLibDirs);
hash = 31 * hash + mDisplayId;
- hash = 31 * hash + mOverrideConfiguration.hashCode();
- hash = 31 * hash + Float.floatToIntBits(mScale);
+ hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
+ hash = 31 * hash + Objects.hashCode(mCompatInfo);
mHash = hash;
}
@@ -60,18 +87,32 @@
if (!(obj instanceof ResourcesKey)) {
return false;
}
+
ResourcesKey peer = (ResourcesKey) obj;
+ if (mHash != peer.mHash) {
+ // If the hashes don't match, the objects can't match.
+ return false;
+ }
if (!Objects.equals(mResDir, peer.mResDir)) {
return false;
}
+ if (!Arrays.equals(mSplitResDirs, peer.mSplitResDirs)) {
+ return false;
+ }
+ if (!Arrays.equals(mOverlayDirs, peer.mOverlayDirs)) {
+ return false;
+ }
+ if (!Arrays.equals(mLibDirs, peer.mLibDirs)) {
+ return false;
+ }
if (mDisplayId != peer.mDisplayId) {
return false;
}
- if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) {
+ if (!Objects.equals(mOverrideConfiguration, peer.mOverrideConfiguration)) {
return false;
}
- if (mScale != peer.mScale) {
+ if (!Objects.equals(mCompatInfo, peer.mCompatInfo)) {
return false;
}
return true;
@@ -79,6 +120,29 @@
@Override
public String toString() {
- return Integer.toHexString(mHash);
+ StringBuilder builder = new StringBuilder().append("ResourcesKey{");
+ builder.append(" mHash=").append(Integer.toHexString(mHash));
+ builder.append(" mResDir=").append(mResDir);
+ builder.append(" mSplitDirs=[");
+ if (mSplitResDirs != null) {
+ builder.append(TextUtils.join(",", mSplitResDirs));
+ }
+ builder.append("]");
+ builder.append(" mOverlayDirs=[");
+ if (mOverlayDirs != null) {
+ builder.append(TextUtils.join(",", mOverlayDirs));
+ }
+ builder.append("]");
+ builder.append(" mLibDirs=[");
+ if (mLibDirs != null) {
+ builder.append(TextUtils.join(",", mLibDirs));
+ }
+ builder.append("]");
+ builder.append(" mDisplayId=").append(mDisplayId);
+ builder.append(" mOverrideConfig=").append(Configuration.resourceQualifierString(
+ mOverrideConfiguration));
+ builder.append(" mCompatInfo=").append(mCompatInfo);
+ builder.append("}");
+ return builder.toString();
}
}
diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java
index 9a2d06147..f1b1e74 100644
--- a/core/java/android/content/res/ThemedResourceCache.java
+++ b/core/java/android/content/res/ThemedResourceCache.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources.Theme;
import android.content.res.Resources.ThemeKey;
import android.util.LongSparseArray;
@@ -115,7 +116,7 @@
*
* @param configChanges a bitmask of configuration changes
*/
- public void onConfigurationChange(int configChanges) {
+ public void onConfigurationChange(@Config int configChanges) {
prune(configChanges);
}
@@ -192,7 +193,7 @@
* simply prune missing weak references
* @return {@code true} if the cache is completely empty after pruning
*/
- private boolean prune(int configChanges) {
+ private boolean prune(@Config int configChanges) {
synchronized (this) {
if (mThemedEntries != null) {
for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
@@ -211,7 +212,7 @@
}
private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries,
- int configChanges) {
+ @Config int configChanges) {
if (entries == null) {
return true;
}
@@ -226,7 +227,7 @@
return entries.size() == 0;
}
- private boolean pruneEntryLocked(@Nullable T entry, int configChanges) {
+ private boolean pruneEntryLocked(@Nullable T entry, @Config int configChanges) {
return entry == null || (configChanges != 0
&& shouldInvalidateEntry(entry, configChanges));
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index da49b64..f6ac0ba 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -20,6 +20,8 @@
import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.annotation.StyleableRes;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
import android.graphics.drawable.Drawable;
import android.os.StrictMode;
import android.util.AttributeSet;
@@ -252,7 +254,8 @@
* @throws RuntimeException if the TypedArray has already been recycled.
* @hide
*/
- public String getNonConfigurationString(@StyleableRes int index, int allowedChangingConfigs) {
+ public String getNonConfigurationString(@StyleableRes int index,
+ @Config int allowedChangingConfigs) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
@@ -260,7 +263,9 @@
index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
- if ((data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]&~allowedChangingConfigs) != 0) {
+ final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava(
+ data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+ if ((changingConfigs & ~allowedChangingConfigs) != 0) {
return null;
}
if (type == TypedValue.TYPE_NULL) {
@@ -1155,12 +1160,12 @@
* @throws RuntimeException if the TypedArray has already been recycled.
* @see android.content.pm.ActivityInfo
*/
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- int changingConfig = 0;
+ @Config int changingConfig = 0;
final int[] data = mData;
final int N = length();
@@ -1170,7 +1175,8 @@
if (type == TypedValue.TYPE_NULL) {
continue;
}
- changingConfig |= data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS];
+ changingConfig |= ActivityInfo.activityInfoConfigNativeToJava(
+ data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
}
return changingConfig;
}
@@ -1185,7 +1191,8 @@
outValue.data = data[index+AssetManager.STYLE_DATA];
outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
- outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
outValue.density = data[index+AssetManager.STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
return true;
@@ -1206,8 +1213,8 @@
/*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {
mResources = resources;
- mMetrics = mResources.mMetrics;
- mAssets = mResources.mAssets;
+ mMetrics = mResources.getDisplayMetrics();
+ mAssets = mResources.getAssets();
mData = data;
mIndices = indices;
mLength = len;
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 8724a96..38279a4 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -990,6 +990,30 @@
int sequenceId) {
// default empty implementation
}
+
+ /**
+ * <p>This method is called if a single buffer for a capture could not be sent to its
+ * destination surface.</p>
+ *
+ * <p>If the whole capture failed, then {@link #onCaptureFailed} will be called instead. If
+ * some but not all buffers were captured but the result metadata will not be available,
+ * then onCaptureFailed will be invoked with {@link CaptureFailure#wasImageCaptured}
+ * returning true, along with one or more calls to {@link #onCaptureBufferLost} for the
+ * failed outputs.</p>
+ *
+ * @param session
+ * The session returned by {@link CameraDevice#createCaptureSession}
+ * @param request
+ * The request that was given to the CameraDevice
+ * @param target
+ * The target Surface that the buffer will not be produced for
+ * @param frameNumber
+ * The frame number for the request
+ */
+ public void onCaptureBufferLost(@NonNull CameraCaptureSession session,
+ @NonNull CaptureRequest request, @NonNull Surface target, long frameNumber) {
+ // default empty implementation
+ }
}
/**
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b3c8e3b..04e64af 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -358,6 +358,8 @@
// TODO: factor out callback to be non-nested, then move setter to constructor
// For now, calling setRemoteDevice will fire initial
// onOpened/onUnconfigured callbacks.
+ // This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
+ // cameraUser dies during setup.
deviceImpl.setRemoteDevice(cameraUser);
device = deviceImpl;
}
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index d58ad22..4add962 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2070,6 +2070,24 @@
*/
public static final int CONTROL_SCENE_MODE_FACE_PRIORITY_LOW_LIGHT = 19;
+ /**
+ * <p>Scene mode values within the range of
+ * <code>[DEVICE_CUSTOM_START, DEVICE_CUSTOM_END]</code> are reserved for device specific
+ * customized scene modes.</p>
+ * @see CaptureRequest#CONTROL_SCENE_MODE
+ * @hide
+ */
+ public static final int CONTROL_SCENE_MODE_DEVICE_CUSTOM_START = 100;
+
+ /**
+ * <p>Scene mode values within the range of
+ * <code>[DEVICE_CUSTOM_START, DEVICE_CUSTOM_END]</code> are reserved for device specific
+ * customized scene modes.</p>
+ * @see CaptureRequest#CONTROL_SCENE_MODE
+ * @hide
+ */
+ public static final int CONTROL_SCENE_MODE_DEVICE_CUSTOM_END = 127;
+
//
// Enumeration values for CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
//
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index 57a080b..9478dc0 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -137,6 +137,11 @@
throw new IllegalArgumentException("Orientation " + orientation +
" is not a valid EXIF orientation value");
}
+ // ExifInterface and TIFF/EP spec differ on definition of
+ // "Unknown" orientation; other values map directly
+ if (orientation == ExifInterface.ORIENTATION_UNDEFINED) {
+ orientation = TAG_ORIENTATION_UNKNOWN;
+ }
nativeSetOrientation(orientation);
return this;
}
@@ -443,7 +448,7 @@
private static final String GPS_LONG_REF_WEST = "W";
private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd";
- private static final String TIFF_DATETIME_FORMAT = "yyyy:MM:dd kk:mm:ss";
+ private static final String TIFF_DATETIME_FORMAT = "yyyy:MM:dd HH:mm:ss";
private static final DateFormat sExifGPSDateStamp = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
private static final DateFormat sDateTimeStampFormat =
new SimpleDateFormat(TIFF_DATETIME_FORMAT);
@@ -458,6 +463,9 @@
private static final int DEFAULT_PIXEL_STRIDE = 2; // bytes per sample
private static final int BYTES_PER_RGB_PIX = 3; // byts per pixel
+ // TIFF tag values needed to map between public API and TIFF spec
+ private static final int TAG_ORIENTATION_UNKNOWN = 9;
+
/**
* Offset, rowStride, and pixelStride are given in bytes. Height and width are given in pixels.
*/
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 00dd780..d84a6fc 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -63,7 +63,8 @@
/**
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
*/
-public class CameraDeviceImpl extends CameraDevice {
+public class CameraDeviceImpl extends CameraDevice
+ implements IBinder.DeathRecipient {
private final String TAG;
private final boolean DEBUG = false;
@@ -261,7 +262,14 @@
return mCallbacks;
}
- public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
+ /**
+ * Set remote device, which triggers initial onOpened/onUnconfigured callbacks
+ *
+ * <p>This function may post onDisconnected and throw CAMERA_DISCONNECTED if remoteDevice dies
+ * during setup.</p>
+ *
+ */
+ public void setRemoteDevice(ICameraDeviceUser remoteDevice) throws CameraAccessException {
synchronized(mInterfaceLock) {
// TODO: Move from decorator to direct binder-mediated exceptions
// If setRemoteFailure already called, do nothing
@@ -269,6 +277,20 @@
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
+ IBinder remoteDeviceBinder = remoteDevice.asBinder();
+ // For legacy camera device, remoteDevice is in the same process, and
+ // asBinder returns NULL.
+ if (remoteDeviceBinder != null) {
+ try {
+ remoteDeviceBinder.linkToDeath(this, /*flag*/ 0);
+ } catch (RemoteException e) {
+ CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
+
+ throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+ "The camera device has encountered a serious error");
+ }
+ }
+
mDeviceHandler.post(mCallOnOpened);
mDeviceHandler.post(mCallOnUnconfigured);
}
@@ -1094,6 +1116,11 @@
int sequenceId) {
// default empty implementation
}
+
+ public void onCaptureBufferLost(CameraDevice camera,
+ CaptureRequest request, Surface target, long frameNumber) {
+ // default empty implementation
+ }
}
/**
@@ -1865,48 +1892,66 @@
final CaptureRequest request = holder.getRequest(subsequenceId);
- // No way to report buffer errors right now
+ Runnable failureDispatch = null;
if (errorCode == ERROR_CAMERA_BUFFER) {
- Log.e(TAG, String.format("Lost output buffer reported for frame %d", frameNumber));
- return;
- }
-
- boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
-
- // This is only approximate - exact handling needs the camera service and HAL to
- // disambiguate between request failures to due abort and due to real errors.
- // For now, assume that if the session believes we're mid-abort, then the error
- // is due to abort.
- int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
- CaptureFailure.REASON_FLUSHED :
- CaptureFailure.REASON_ERROR;
-
- final CaptureFailure failure = new CaptureFailure(
- request,
- reason,
- /*dropped*/ mayHaveBuffers,
- requestId,
- frameNumber);
-
- Runnable failureDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()){
- holder.getCallback().onCaptureFailed(
- CameraDeviceImpl.this,
- request,
- failure);
- }
+ final Surface outputSurface =
+ mConfiguredOutputs.get(resultExtras.getErrorStreamId()).getSurface();
+ if (DEBUG) {
+ Log.v(TAG, String.format("Lost output buffer reported for frame %d, target %s",
+ frameNumber, outputSurface));
}
- };
- holder.getHandler().post(failureDispatch);
+ failureDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getCallback().onCaptureBufferLost(
+ CameraDeviceImpl.this,
+ request,
+ outputSurface,
+ frameNumber);
+ }
+ }
+ };
+ } else {
+ boolean mayHaveBuffers = (errorCode == ERROR_CAMERA_RESULT);
- // Fire onCaptureSequenceCompleted if appropriate
- if (DEBUG) {
- Log.v(TAG, String.format("got error frame %d", frameNumber));
+ // This is only approximate - exact handling needs the camera service and HAL to
+ // disambiguate between request failures to due abort and due to real errors. For
+ // now, assume that if the session believes we're mid-abort, then the error is due
+ // to abort.
+ int reason = (mCurrentSession != null && mCurrentSession.isAborting()) ?
+ CaptureFailure.REASON_FLUSHED :
+ CaptureFailure.REASON_ERROR;
+
+ final CaptureFailure failure = new CaptureFailure(
+ request,
+ reason,
+ /*dropped*/ mayHaveBuffers,
+ requestId,
+ frameNumber);
+
+ failureDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getCallback().onCaptureFailed(
+ CameraDeviceImpl.this,
+ request,
+ failure);
+ }
+ }
+ };
+
+ // Fire onCaptureSequenceCompleted if appropriate
+ if (DEBUG) {
+ Log.v(TAG, String.format("got error frame %d", frameNumber));
+ }
+ mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
+ checkAndFireSequenceComplete();
}
- mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
- checkAndFireSequenceComplete();
+
+ // Dispatch the failure callback
+ holder.getHandler().post(failureDispatch);
}
} // public class CameraDeviceCallbacks
@@ -1962,4 +2007,28 @@
return mCharacteristics;
}
+ /**
+ * Listener for binder death.
+ *
+ * <p> Handle binder death for ICameraDeviceUser. Trigger onError.</p>
+ */
+ public void binderDied() {
+ Log.w(TAG, "CameraDevice " + mCameraId + " died unexpectedly");
+
+ if (mRemoteDevice == null) {
+ return; // Camera already closed
+ }
+
+ mInError = true;
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ if (!isClosed()) {
+ mDeviceCallback.onError(CameraDeviceImpl.this,
+ CameraDeviceCallbacks.ERROR_CAMERA_SERVICE);
+ }
+ }
+ };
+ CameraDeviceImpl.this.mDeviceHandler.post(r);
+ }
}
diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
index d859da7..40535e2 100644
--- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
+++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java
@@ -28,6 +28,7 @@
private int precaptureTriggerId;
private long frameNumber;
private int partialResultCount;
+ private int errorStreamId;
public static final Parcelable.Creator<CaptureResultExtras> CREATOR =
new Parcelable.Creator<CaptureResultExtras>() {
@@ -48,13 +49,14 @@
public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId,
int precaptureTriggerId, long frameNumber,
- int partialResultCount) {
+ int partialResultCount, int errorStreamId) {
this.requestId = requestId;
this.subsequenceId = subsequenceId;
this.afTriggerId = afTriggerId;
this.precaptureTriggerId = precaptureTriggerId;
this.frameNumber = frameNumber;
this.partialResultCount = partialResultCount;
+ this.errorStreamId = errorStreamId;
}
@Override
@@ -70,6 +72,7 @@
dest.writeInt(precaptureTriggerId);
dest.writeLong(frameNumber);
dest.writeInt(partialResultCount);
+ dest.writeInt(errorStreamId);
}
public void readFromParcel(Parcel in) {
@@ -79,6 +82,7 @@
precaptureTriggerId = in.readInt();
frameNumber = in.readLong();
partialResultCount = in.readInt();
+ errorStreamId = in.readInt();
}
public int getRequestId() {
@@ -104,4 +108,8 @@
public int getPartialResultCount() {
return partialResultCount;
}
+
+ public int getErrorStreamId() {
+ return errorStreamId;
+ }
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index e62df3c..661edd7 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -91,11 +91,11 @@
private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
if (holder == null) {
return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE,
- ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
+ ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE);
}
return new CaptureResultExtras(holder.getRequestId(), holder.getSubsequeceId(),
/*afTriggerId*/0, /*precaptureTriggerId*/0, holder.getFrameNumber(),
- /*partialResultCount*/1);
+ /*partialResultCount*/1, /*errorStreamId*/-1);
}
/**
@@ -614,14 +614,16 @@
return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDataspace(surface));
}
- static void configureSurface(Surface surface, int width, int height,
- int pixelFormat) throws BufferQueueAbandonedException {
+ static void connectSurface(Surface surface) throws BufferQueueAbandonedException {
checkNotNull(surface);
- checkArgumentPositive(width, "width must be positive.");
- checkArgumentPositive(height, "height must be positive.");
- LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height,
- pixelFormat));
+ LegacyExceptionUtils.throwOnError(nativeConnectSurface(surface));
+ }
+
+ static void disconnectSurface(Surface surface) throws BufferQueueAbandonedException {
+ if (surface == null) return;
+
+ LegacyExceptionUtils.throwOnError(nativeDisconnectSurface(surface));
}
static void produceFrame(Surface surface, byte[] pixelBuffer, int width,
@@ -716,8 +718,7 @@
private static native int nativeDetectSurfaceDimens(Surface surface,
/*out*/int[/*2*/] dimens);
- private static native int nativeConfigureSurface(Surface surface, int width, int height,
- int pixelFormat);
+ private static native int nativeConnectSurface(Surface surface);
private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width,
int height, int pixelFormat);
@@ -740,5 +741,7 @@
private static native int nativeSetScalingMode(Surface surface, int scalingMode);
+ private static native int nativeDisconnectSurface(Surface surface);
+
static native int nativeGetJpegFooterSize();
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 1ca7ddf..e8ce3ec 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -365,6 +365,14 @@
mGLThreadManager.waitUntilIdle();
}
resetJpegSurfaceFormats(mCallbackOutputs);
+
+ for (Surface s : mCallbackOutputs) {
+ try {
+ LegacyCameraDevice.disconnectSurface(s);
+ } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+ Log.w(TAG, "Surface abandoned, skipping...", e);
+ }
+ }
mPreviewOutputs.clear();
mCallbackOutputs.clear();
mJpegSurfaceIds.clear();
@@ -392,6 +400,10 @@
mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
mCallbackOutputs.add(s);
callbackOutputSizes.add(outSize);
+
+ // LegacyCameraDevice is the producer of JPEG output surfaces
+ // so LegacyCameraDevice needs to connect to the surfaces.
+ LegacyCameraDevice.connectSurface(s);
break;
default:
LegacyCameraDevice.setScalingMode(s, LegacyCameraDevice.
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index bc80fc1..70bc2fd 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -401,6 +401,13 @@
private void clearState() {
mSurfaces.clear();
+ for (EGLSurfaceHolder holder : mConversionSurfaces) {
+ try {
+ LegacyCameraDevice.disconnectSurface(holder.surface);
+ } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+ Log.w(TAG, "Surface abandoned, skipping...", e);
+ }
+ }
mConversionSurfaces.clear();
mPBufferPixels = null;
if (mSurfaceTexture != null) {
@@ -631,6 +638,9 @@
holder.height = surfaceSize.getHeight();
if (LegacyCameraDevice.needsConversion(s)) {
mConversionSurfaces.add(holder);
+ // LegacyCameraDevice is the producer of surfaces if it's not handled by EGL,
+ // so LegacyCameraDevice needs to connect to the surfaces.
+ LegacyCameraDevice.connectSurface(s);
} else {
mSurfaces.add(holder);
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 2826882..6b79a8a 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2269,8 +2269,9 @@
public void onExtractedDeleteText(int start, int end) {
InputConnection conn = getCurrentInputConnection();
if (conn != null) {
+ conn.finishComposingText();
conn.setSelection(start, start);
- conn.deleteSurroundingText(0, end-start);
+ conn.deleteSurroundingText(0, end - start);
}
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 94de933..8738424 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -68,10 +68,12 @@
public static final int FIREWALL_CHAIN_NONE = 0;
public static final int FIREWALL_CHAIN_DOZABLE = 1;
public static final int FIREWALL_CHAIN_STANDBY = 2;
+ public static final int FIREWALL_CHAIN_POWERSAVE = 3;
public static final String FIREWALL_CHAIN_NAME_NONE = "none";
public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable";
public static final String FIREWALL_CHAIN_NAME_STANDBY = "standby";
+ public static final String FIREWALL_CHAIN_NAME_POWERSAVE = "powersave";
private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 3d8b091..25806fa 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -71,9 +71,9 @@
/** {@link #set} value for all roaming values. */
public static final int ROAMING_ALL = -1;
/** {@link #set} value where native, non-roaming data is accounted. */
- public static final int ROAMING_DEFAULT = 0;
+ public static final int ROAMING_NO = 0;
/** {@link #set} value where roaming data is accounted. */
- public static final int ROAMING_ROAMING = 1;
+ public static final int ROAMING_YES = 1;
// TODO: move fields to "mVariable" notation
@@ -123,7 +123,7 @@
public Entry(String iface, int uid, int set, int tag, long rxBytes, long rxPackets,
long txBytes, long txPackets, long operations) {
- this(iface, uid, set, tag, ROAMING_DEFAULT, rxBytes, rxPackets, txBytes, txPackets,
+ this(iface, uid, set, tag, ROAMING_NO, rxBytes, rxPackets, txBytes, txPackets,
operations);
}
@@ -836,10 +836,10 @@
switch (roaming) {
case ROAMING_ALL:
return "ALL";
- case ROAMING_DEFAULT:
- return "DEFAULT";
- case ROAMING_ROAMING:
- return "ROAMING";
+ case ROAMING_NO:
+ return "NO";
+ case ROAMING_YES:
+ return "YES";
default:
return "UNKNOWN";
}
@@ -1019,18 +1019,18 @@
// Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
// the TAG_NONE traffic.
//
- // Relies on the fact that the underlying traffic only has state ROAMING_DEFAULT, which
+ // Relies on the fact that the underlying traffic only has state ROAMING_NO, which
// should be the case as it comes directly from the /proc file. We only blend in the
// roaming data after applying these adjustments, by checking the NetworkIdentity of the
// underlying iface.
int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
- ROAMING_DEFAULT);
+ ROAMING_NO);
if (idxVpnBackground != -1) {
tunSubtract(idxVpnBackground, this, moved);
}
int idxVpnForeground = findIndex(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
- ROAMING_DEFAULT);
+ ROAMING_NO);
if (idxVpnForeground != -1) {
tunSubtract(idxVpnForeground, this, moved);
}
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index ba8bd34..95ffb44 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -19,6 +19,7 @@
import android.annotation.SystemApi;
import android.app.DownloadManager;
import android.app.backup.BackupManager;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.RemoteException;
@@ -33,12 +34,16 @@
import java.net.SocketException;
/**
- * Class that provides network traffic statistics. These statistics include
+ * Class that provides network traffic statistics. These statistics include
* bytes transmitted and received and network packets transmitted and received,
* over all interfaces, over the mobile interface, and on a per-UID basis.
* <p>
- * These statistics may not be available on all platforms. If the statistics
- * are not supported by this device, {@link #UNSUPPORTED} will be returned.
+ * These statistics may not be available on all platforms. If the statistics are
+ * not supported by this device, {@link #UNSUPPORTED} will be returned.
+ * <p>
+ * Note that the statistics returned by this class reset and start from zero
+ * after every reboot. To access more robust historical network statistics data,
+ * use {@link NetworkStatsManager} instead.
*/
public class TrafficStats {
/**
@@ -497,14 +502,27 @@
* monotonically since device boot. Statistics are measured at the network
* layer, so they include both TCP and UDP usage.
* <p>
- * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may return
- * {@link #UNSUPPORTED} on devices where statistics aren't available.
+ * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+ * return {@link #UNSUPPORTED} on devices where statistics aren't available.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#N} this will only
+ * report traffic statistics for the calling UID. It will return
+ * {@link #UNSUPPORTED} for all other UIDs for privacy reasons. To access
+ * historical network statistics belonging to other UIDs, use
+ * {@link NetworkStatsManager}.
*
* @see android.os.Process#myUid()
* @see android.content.pm.ApplicationInfo#uid
*/
public static long getUidTxBytes(int uid) {
- return nativeGetUidStat(uid, TYPE_TX_BYTES);
+ // This isn't actually enforcing any security; it just returns the
+ // unsupported value. The real filtering is done at the kernel level.
+ final int callingUid = android.os.Process.myUid();
+ if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
+ return nativeGetUidStat(uid, TYPE_TX_BYTES);
+ } else {
+ return UNSUPPORTED;
+ }
}
/**
@@ -515,12 +533,25 @@
* <p>
* Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may return
* {@link #UNSUPPORTED} on devices where statistics aren't available.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#N} this will only
+ * report traffic statistics for the calling UID. It will return
+ * {@link #UNSUPPORTED} for all other UIDs for privacy reasons. To access
+ * historical network statistics belonging to other UIDs, use
+ * {@link NetworkStatsManager}.
*
* @see android.os.Process#myUid()
* @see android.content.pm.ApplicationInfo#uid
*/
public static long getUidRxBytes(int uid) {
- return nativeGetUidStat(uid, TYPE_RX_BYTES);
+ // This isn't actually enforcing any security; it just returns the
+ // unsupported value. The real filtering is done at the kernel level.
+ final int callingUid = android.os.Process.myUid();
+ if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
+ return nativeGetUidStat(uid, TYPE_RX_BYTES);
+ } else {
+ return UNSUPPORTED;
+ }
}
/**
@@ -531,12 +562,25 @@
* <p>
* Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may return
* {@link #UNSUPPORTED} on devices where statistics aren't available.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#N} this will only
+ * report traffic statistics for the calling UID. It will return
+ * {@link #UNSUPPORTED} for all other UIDs for privacy reasons. To access
+ * historical network statistics belonging to other UIDs, use
+ * {@link NetworkStatsManager}.
*
* @see android.os.Process#myUid()
* @see android.content.pm.ApplicationInfo#uid
*/
public static long getUidTxPackets(int uid) {
- return nativeGetUidStat(uid, TYPE_TX_PACKETS);
+ // This isn't actually enforcing any security; it just returns the
+ // unsupported value. The real filtering is done at the kernel level.
+ final int callingUid = android.os.Process.myUid();
+ if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
+ return nativeGetUidStat(uid, TYPE_TX_PACKETS);
+ } else {
+ return UNSUPPORTED;
+ }
}
/**
@@ -547,12 +591,25 @@
* <p>
* Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may return
* {@link #UNSUPPORTED} on devices where statistics aren't available.
+ * <p>
+ * Starting in {@link android.os.Build.VERSION_CODES#N} this will only
+ * report traffic statistics for the calling UID. It will return
+ * {@link #UNSUPPORTED} for all other UIDs for privacy reasons. To access
+ * historical network statistics belonging to other UIDs, use
+ * {@link NetworkStatsManager}.
*
* @see android.os.Process#myUid()
* @see android.content.pm.ApplicationInfo#uid
*/
public static long getUidRxPackets(int uid) {
- return nativeGetUidStat(uid, TYPE_RX_PACKETS);
+ // This isn't actually enforcing any security; it just returns the
+ // unsupported value. The real filtering is done at the kernel level.
+ final int callingUid = android.os.Process.myUid();
+ if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
+ return nativeGetUidStat(uid, TYPE_RX_PACKETS);
+ } else {
+ return UNSUPPORTED;
+ }
}
/**
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index ee7bd9a..5c71373 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -235,6 +235,12 @@
return mParcelledData != null;
}
+ /** @hide */
+ ArrayMap<String, Object> getMap() {
+ unparcel();
+ return mMap;
+ }
+
/**
* Returns the number of mappings contained in this Bundle.
*
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 8281279..e1c7ad77 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -465,6 +465,7 @@
};
public abstract long getProcessStateTime(int state, long elapsedRealtimeUs, int which);
+ public abstract Timer getProcessStateTimer(int state);
public abstract Timer getVibratorOnTimer();
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index d73deb6..ea8ba2f 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -70,6 +70,9 @@
private static final boolean CHECK_PARCEL_SIZE = false;
static final String TAG = "Binder";
+ /** @hide */
+ public static boolean LOG_RUNTIME_EXCEPTION = false; // DO NOT SUBMIT WITH TRUE
+
/**
* Control whether dump() calls are allowed.
*/
@@ -560,17 +563,16 @@
// If the call was FLAG_ONEWAY then these exceptions disappear into the ether.
try {
res = onTransact(code, data, reply, flags);
- } catch (RemoteException e) {
- if ((flags & FLAG_ONEWAY) != 0) {
- Log.w(TAG, "Binder call failed.", e);
- } else {
- reply.setDataPosition(0);
- reply.writeException(e);
- }
- res = true;
- } catch (RuntimeException e) {
- if ((flags & FLAG_ONEWAY) != 0) {
+ } catch (RemoteException|RuntimeException e) {
+ if (LOG_RUNTIME_EXCEPTION) {
Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
+ }
+ if ((flags & FLAG_ONEWAY) != 0) {
+ if (e instanceof RemoteException) {
+ Log.w(TAG, "Binder call failed.", e);
+ } else {
+ Log.w(TAG, "Caught a RuntimeException from the binder stub implementation.", e);
+ }
} else {
reply.setDataPosition(0);
reply.writeException(e);
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index 0d94072..cb85eef 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -16,6 +16,9 @@
package android.os;
+import android.content.Context;
+import android.util.Log;
+
import com.android.internal.os.IDropBoxManagerService;
import java.io.ByteArrayInputStream;
@@ -40,6 +43,8 @@
*/
public class DropBoxManager {
private static final String TAG = "DropBoxManager";
+
+ private final Context mContext;
private final IDropBoxManagerService mService;
/** Flag value: Entry's content was deleted to save space. */
@@ -249,14 +254,20 @@
}
/** {@hide} */
- public DropBoxManager(IDropBoxManagerService service) { mService = service; }
+ public DropBoxManager(Context context, IDropBoxManagerService service) {
+ mContext = context;
+ mService = service;
+ }
/**
* Create a dummy instance for testing. All methods will fail unless
* overridden with an appropriate mock implementation. To obtain a
* functional instance, use {@link android.content.Context#getSystemService}.
*/
- protected DropBoxManager() { mService = null; }
+ protected DropBoxManager() {
+ mContext = null;
+ mService = null;
+ }
/**
* Stores human-readable text. The data may be discarded eventually (or even
@@ -270,6 +281,11 @@
try {
mService.add(new Entry(tag, 0, data));
} catch (RemoteException e) {
+ if (e instanceof TransactionTooLargeException
+ && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+ Log.e(TAG, "App sent too much data, so it was ignored", e);
+ return;
+ }
throw e.rethrowFromSystemServer();
}
}
@@ -286,6 +302,11 @@
try {
mService.add(new Entry(tag, 0, data, flags));
} catch (RemoteException e) {
+ if (e instanceof TransactionTooLargeException
+ && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+ Log.e(TAG, "App sent too much data, so it was ignored", e);
+ return;
+ }
throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index 838279b..082194b 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -25,10 +25,12 @@
void removePowerSaveWhitelistApp(String name);
String[] getSystemPowerWhitelistExceptIdle();
String[] getSystemPowerWhitelist();
+ String[] getUserPowerWhitelist();
String[] getFullPowerWhitelistExceptIdle();
String[] getFullPowerWhitelist();
int[] getAppIdWhitelistExceptIdle();
int[] getAppIdWhitelist();
+ int[] getAppIdUserWhitelist();
int[] getAppIdTempWhitelist();
boolean isPowerSaveWhitelistExceptIdleApp(String name);
boolean isPowerSaveWhitelistApp(String name);
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index ea180b2..5872f74 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -24,9 +24,6 @@
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
/**
* A mapping from String values to various types that can be saved to persistent and later
@@ -82,6 +79,20 @@
super(b);
}
+
+ /**
+ * Constructs a PersistableBundle from a Bundle.
+ *
+ * @param b a Bundle to be copied.
+ *
+ * @throws IllegalArgumentException if any element of {@code b} cannot be persisted.
+ *
+ * @hide
+ */
+ public PersistableBundle(Bundle b) {
+ this(b.getMap());
+ }
+
/**
* Constructs a PersistableBundle containing the mappings passed in.
*
@@ -101,6 +112,8 @@
if (value instanceof ArrayMap) {
// Fix up any Maps by replacing them with PersistableBundles.
mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
+ } else if (value instanceof Bundle) {
+ mMap.setValueAt(i, new PersistableBundle(((Bundle) value)));
} else if (!isValidType(value)) {
throw new IllegalArgumentException("Bad value in PersistableBundle key="
+ mMap.keyAt(i) + " value=" + value);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8fd3b0c..4506f51 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -998,6 +998,31 @@
throws IllegalArgumentException, SecurityException;
/**
+ * On some devices, the foreground process may have one or more CPU
+ * cores exclusively reserved for it. This method can be used to
+ * retrieve which cores that are (if any), so the calling process
+ * can then use sched_setaffinity() to lock a thread to these cores.
+ * Note that the calling process must currently be running in the
+ * foreground for this method to return any cores.
+ *
+ * The CPU core(s) exclusively reserved for the foreground process will
+ * stay reserved for as long as the process stays in the foreground.
+ *
+ * As soon as a process leaves the foreground, those CPU cores will
+ * no longer be reserved for it, and will most likely be reserved for
+ * the new foreground process. It's not necessary to change the affinity
+ * of your process when it leaves the foreground (if you had previously
+ * set it to use a reserved core); the OS will automatically take care
+ * of resetting the affinity at that point.
+ *
+ * @return an array of integers, indicating the CPU cores exclusively
+ * reserved for this process. The array will have length zero if no
+ * CPU cores are exclusively reserved for this process at this point
+ * in time.
+ */
+ public static final native int[] getExclusiveCores();
+
+ /**
* Set the priority of the calling thread, based on Linux priorities. See
* {@link #setThreadPriority(int, int)} for more information.
*
diff --git a/core/java/android/os/health/HealthKeys.java b/core/java/android/os/health/HealthKeys.java
new file mode 100644
index 0000000..842def3
--- /dev/null
+++ b/core/java/android/os/health/HealthKeys.java
@@ -0,0 +1,219 @@
+/*
+ * 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.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+/**
+ * Constants and stuff for the android.os.health package.
+ *
+ * @hide
+ */
+public class HealthKeys {
+
+ /**
+ * No valid key will ever be 0.
+ */
+ public static final int UNKNOWN_KEY = 0;
+
+ /*
+ * Base key for each of the different classes. There is
+ * nothing intrinsic to the operation of the value of the
+ * keys. It's just segmented for better debugging. The
+ * classes don't mix them anway.
+ */
+ public static final int BASE_UID = 10000;
+ public static final int BASE_PID = 20000;
+ public static final int BASE_PROCESS = 30000;
+ public static final int BASE_PACKAGE = 40000;
+ public static final int BASE_SERVICE = 50000;
+
+ /*
+ * The types of values supported by HealthStats.
+ */
+ public static final int TYPE_TIMER = 0;
+ public static final int TYPE_MEASUREMENT = 1;
+ public static final int TYPE_STATS = 2;
+ public static final int TYPE_TIMERS = 3;
+ public static final int TYPE_MEASUREMENTS = 4;
+
+ public static final int TYPE_COUNT = 5;
+
+ /**
+ * Annotation to mark public static final int fields that are to be used
+ * as field keys in HealthStats.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.FIELD})
+ public @interface Constant {
+ /**
+ * One of the TYPE_* constants above.
+ */
+ int type();
+ }
+
+ /**
+ * Class to gather the constants defined in a class full of constants and
+ * build the key indices used by HealthStatsWriter and HealthStats.
+ *
+ * @hide
+ */
+ public static class Constants {
+ private final String mDataType;
+ private final int[][] mKeys = new int[TYPE_COUNT][];
+
+ /**
+ * Pass in a class to gather the public static final int fields that are
+ * tagged with the @Constant annotation.
+ */
+ public Constants(Class clazz) {
+ // Save the class name for debugging
+ mDataType = clazz.getSimpleName();
+
+ // Iterate through the list of fields on this class, and build the
+ // constant arrays for these fields.
+ final Field[] fields = clazz.getDeclaredFields();
+ final Class<Constant> annotationClass = Constant.class;
+
+ final int N = fields.length;
+
+ final SortedIntArray[] keys = new SortedIntArray[mKeys.length];
+ for (int i=0; i<keys.length; i++) {
+ keys[i] = new SortedIntArray(N);
+ }
+
+ for (int i=0; i<N; i++) {
+ final Field field = fields[i];
+ final Constant constant = field.getAnnotation(annotationClass);
+ if (constant != null) {
+ final int type = constant.type();
+ if (type >= keys.length) {
+ throw new RuntimeException("Unknown Constant type " + type
+ + " on " + field);
+ }
+ try {
+ keys[type].addValue(field.getInt(null));
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException("Can't read constant value type=" + type
+ + " field=" + field, ex);
+ }
+ }
+ }
+
+ for (int i=0; i<keys.length; i++) {
+ mKeys[i] = keys[i].getArray();
+ }
+ }
+
+ /**
+ * Get a string representation of this class. Useful for debugging. It will be the
+ * simple name of the class passed in the constructor.
+ */
+ public String getDataType() {
+ return mDataType;
+ }
+
+ /**
+ * Return how many keys there are for the given field type.
+ *
+ * @see TYPE_TIMER
+ * @see TYPE_MEASUREMENT
+ * @see TYPE_TIMERS
+ * @see TYPE_MEASUREMENTS
+ * @see TYPE_STATS
+ */
+ public int getSize(int type) {
+ return mKeys[type].length;
+ }
+
+ /**
+ * Return the index for the given type and key combination in the array of field
+ * keys or values.
+ *
+ * @see TYPE_TIMER
+ * @see TYPE_MEASUREMENT
+ * @see TYPE_TIMERS
+ * @see TYPE_MEASUREMENTS
+ * @see TYPE_STATS
+ */
+ public int getIndex(int type, int key) {
+ final int index = Arrays.binarySearch(mKeys[type], key);
+ if (index >= 0) {
+ return index;
+ } else {
+ throw new RuntimeException("Unknown Constant " + key + " (of type "
+ + type + " )");
+ }
+ }
+
+ /**
+ * Get the array of keys for the given field type.
+ */
+ public int[] getKeys(int type) {
+ return mKeys[type];
+ }
+ }
+
+ /**
+ * An array of fixed size that will be sorted.
+ */
+ private static class SortedIntArray {
+ int mCount;
+ int[] mArray;
+
+ /**
+ * Construct with the maximum number of values.
+ */
+ SortedIntArray(int maxCount) {
+ mArray = new int[maxCount];
+ }
+
+ /**
+ * Add a value.
+ */
+ void addValue(int value) {
+ mArray[mCount++] = value;
+ }
+
+ /**
+ * Get the array of values that have been added, with the values in
+ * numerically increasing order.
+ */
+ int[] getArray() {
+ if (mCount == mArray.length) {
+ Arrays.sort(mArray);
+ return mArray;
+ } else {
+ final int[] result = new int[mCount];
+ System.arraycopy(mArray, 0, result, 0, mCount);
+ Arrays.sort(result);
+ return result;
+ }
+ }
+ }
+}
+
+
diff --git a/core/java/android/os/health/HealthStats.java b/core/java/android/os/health/HealthStats.java
new file mode 100644
index 0000000..f0489e2
--- /dev/null
+++ b/core/java/android/os/health/HealthStats.java
@@ -0,0 +1,481 @@
+/*
+ * 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.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * A HealthStats object contains system health data about an application.
+ *
+ * <p>
+ * <b>Data Types</b><br>
+ * Each of the keys references data in one of five data types:
+ *
+ * <p>
+ * A <b>measurement</b> metric contains a sinlge {@code long} value. That value may
+ * be a count, a time, or some other type of value. The unit for a measurement
+ * (COUNT, MS, etc) will always be in the name of the constant for the key to
+ * retrieve it. For example, the
+ * {@link android.os.health.UidHealthStats#MEASUREMENT_WIFI_TX_MS UidHealthStats.MEASUREMENT_WIFI_TX_MS}
+ * value is the number of milliseconds (ms) that were spent transmitting on wifi by an
+ * application. The
+ * {@link android.os.health.UidHealthStats#MEASUREMENT_MOBILE_RX_PACKETS UidHealthStats.MEASUREMENT_MOBILE_RX_PACKETS}
+ * measurement is the number of packets received on behalf of an application.
+ * The {@link android.os.health.UidHealthStats#MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT
+ * UidHealthStats.MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT}
+ * measurement is the number of times the user touched the screen, causing the
+ * screen to stay awake.
+ *
+ *
+ * <p>
+ * A <b>timer</b> metric contains an {@code int} count and a {@code long} time,
+ * measured in milliseconds. Timers track how many times a resource was used, and
+ * the total duration for that usage. For example, the
+ * {@link android.os.health.UidHealthStats#TIMER_FLASHLIGHT}
+ * timer tracks how many times the application turned on the flashlight, and for
+ * how many milliseconds total it kept it on.
+ *
+ * <p>
+ * A <b>measurement map</b> metric is a mapping of {@link java.lang.String} names to
+ * {@link java.lang.Long} values. The names typically are application provided names. For
+ * example, the
+ * {@link android.os.health.PackageHealthStats#MEASUREMENTS_WAKEUP_ALARMS_COUNT
+ * PackageHealthStats.MEASUREMENTS_WAKEUP_ALARMS_COUNT}
+ * measurement map is a mapping of the tag provided to the
+ * {@link android.app.AlarmManager} when the alarm is scheduled.
+ *
+ * <p>
+ * A <b>timer map</b> metric is a mapping of {@link java.lang.String} names to
+ * {@link android.os.health.TimerStat} objects. The names are typically application
+ * provided names. For example, the
+ * {@link android.os.health.UidHealthStats#TIMERS_WAKELOCKS_PARTIAL UidHealthStats.TIMERS_WAKELOCKS_PARTIAL}
+ * is a mapping of tag provided to the {@link android.os.PowerManager} when the
+ * wakelock is created to the number of times and for how long each wakelock was
+ * active.
+ *
+ * <p>
+ * Lastly, a <b>health stats</b> metric is a mapping of {@link java.lang.String}
+ * names to a recursive {@link android.os.health.HealthStats} object containing
+ * more detailed information. For example, the
+ * {@link android.os.health.UidHealthStats#STATS_PACKAGES UidHealthStats.STATS_PACKAGES}
+ * metric is a mapping of the package names for each of the APKs sharing a uid to
+ * the information recorded for that apk. The returned HealthStats objects will
+ * each be associated with a different set of constants. For the HealthStats
+ * returned for UidHealthStats.STATS_PACKAGES, the keys come from the
+ * {@link android.os.health.PackageHealthStats} class.
+ *
+ */
+public class HealthStats {
+ // Header fields
+ private String mDataType;
+
+ // TimerStat fields
+ private int[] mTimerKeys;
+ private int[] mTimerCounts;
+ private long[] mTimerTimes;
+
+ // Measurement fields
+ private int[] mMeasurementKeys;
+ private long[] mMeasurementValues;
+
+ // Stats fields
+ private int[] mStatsKeys;
+ private ArrayMap<String,HealthStats>[] mStatsValues;
+
+ // Timers fields
+ private int[] mTimersKeys;
+ private ArrayMap<String,TimerStat>[] mTimersValues;
+
+ // Measurements fields
+ private int[] mMeasurementsKeys;
+ private ArrayMap<String,Long>[] mMeasurementsValues;
+
+ /**
+ * HealthStats empty constructor not implemented because this
+ * class is read-only.
+ */
+ private HealthStats() {
+ throw new RuntimeException("unsupported");
+ }
+
+ /**
+ * Construct a health stats object from a parcel.
+ *
+ * @hide
+ */
+ public HealthStats(Parcel in) {
+ int count;
+
+ // Header fields
+ mDataType = in.readString();
+
+ // TimerStat fields
+ count = in.readInt();
+ mTimerKeys = new int[count];
+ mTimerCounts = new int[count];
+ mTimerTimes = new long[count];
+ for (int i=0; i<count; i++) {
+ mTimerKeys[i] = in.readInt();
+ mTimerCounts[i] = in.readInt();
+ mTimerTimes[i] = in.readLong();
+ }
+
+ // Measurement fields
+ count = in.readInt();
+ mMeasurementKeys = new int[count];
+ mMeasurementValues = new long[count];
+ for (int i=0; i<count; i++) {
+ mMeasurementKeys[i] = in.readInt();
+ mMeasurementValues[i] = in.readLong();
+ }
+
+ // Stats fields
+ count = in.readInt();
+ mStatsKeys = new int[count];
+ mStatsValues = new ArrayMap[count];
+ for (int i=0; i<count; i++) {
+ mStatsKeys[i] = in.readInt();
+ mStatsValues[i] = createHealthStatsMap(in);
+ }
+
+ // Timers fields
+ count = in.readInt();
+ mTimersKeys = new int[count];
+ mTimersValues = new ArrayMap[count];
+ for (int i=0; i<count; i++) {
+ mTimersKeys[i] = in.readInt();
+ mTimersValues[i] = createParcelableMap(in, TimerStat.CREATOR);
+ }
+
+ // Measurements fields
+ count = in.readInt();
+ mMeasurementsKeys = new int[count];
+ mMeasurementsValues = new ArrayMap[count];
+ for (int i=0; i<count; i++) {
+ mMeasurementsKeys[i] = in.readInt();
+ mMeasurementsValues[i] = createLongsMap(in);
+ }
+ }
+
+ /**
+ * Get a name representing the contents of this object.
+ *
+ * @see UidHealthStats
+ * @see PackageHealthStats
+ * @see PidHealthStats
+ * @see ProcessHealthStats
+ * @see ServiceHealthStats
+ */
+ public String getDataType() {
+ return mDataType;
+ }
+
+ /**
+ * Return whether this object contains a TimerStat for the supplied key.
+ */
+ public boolean hasTimer(int key) {
+ return getIndex(mTimerKeys, key) >= 0;
+ }
+
+ /**
+ * Return a TimerStat object for the given key.
+ *
+ * This will allocate a new {@link TimerStat} object, which may be wasteful. Instead, use
+ * {@link #getTimerCount} and {@link #getTimerTime}.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasTimer hasTimer(int) To check if a value for the given key is present.
+ */
+ public TimerStat getTimer(int key) {
+ final int index = getIndex(mTimerKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return new TimerStat(mTimerCounts[index], mTimerTimes[index]);
+ }
+
+ /**
+ * Get the count for the timer for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasTimer hasTimer(int) To check if a value for the given key is present.
+ */
+ public int getTimerCount(int key) {
+ final int index = getIndex(mTimerKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mTimerCounts[index];
+ }
+
+ /**
+ * Get the time for the timer for the given key, in milliseconds.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasTimer hasTimer(int) To check if a value for the given key is present.
+ */
+ public long getTimerTime(int key) {
+ final int index = getIndex(mTimerKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mTimerTimes[index];
+ }
+
+ /**
+ * Get the number of timer values in this object. Can be used to iterate through
+ * the available timers.
+ *
+ * @see #getTimerKeyAt
+ */
+ public int getTimerKeyCount() {
+ return mTimerKeys.length;
+ }
+
+ /**
+ * Get the key for the timer at the given index. Index must be between 0 and the result
+ * of {@link #getTimerKeyCount getTimerKeyCount()}.
+ *
+ * @see #getTimerKeyCount
+ */
+ public int getTimerKeyAt(int index) {
+ return mTimerKeys[index];
+ }
+
+ /**
+ * Return whether this object contains a measurement for the supplied key.
+ */
+ public boolean hasMeasurement(int key) {
+ return getIndex(mMeasurementKeys, key) >= 0;
+ }
+
+ /**
+ * Get the measurement for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasMeasurement hasMeasurement(int) To check if a value for the given key is present.
+ */
+ public long getMeasurement(int key) {
+ final int index = getIndex(mMeasurementKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad measurement key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mMeasurementValues[index];
+ }
+
+ /**
+ * Get the number of measurement values in this object. Can be used to iterate through
+ * the available measurements.
+ *
+ * @see #getMeasurementKeyAt
+ */
+ public int getMeasurementKeyCount() {
+ return mMeasurementKeys.length;
+ }
+
+ /**
+ * Get the key for the measurement at the given index. Index must be between 0 and the result
+ * of {@link #getMeasurementKeyCount getMeasurementKeyCount()}.
+ *
+ * @see #getMeasurementKeyCount
+ */
+ public int getMeasurementKeyAt(int index) {
+ return mMeasurementKeys[index];
+ }
+
+ /**
+ * Return whether this object contains a HealthStats map for the supplied key.
+ */
+ public boolean hasStats(int key) {
+ return getIndex(mStatsKeys, key) >= 0;
+ }
+
+ /**
+ * Get the HealthStats map for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasStats hasStats(int) To check if a value for the given key is present.
+ */
+ public Map<String,HealthStats> getStats(int key) {
+ final int index = getIndex(mStatsKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad stats key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mStatsValues[index];
+ }
+
+ /**
+ * Get the number of HealthStat map values in this object. Can be used to iterate through
+ * the available measurements.
+ *
+ * @see #getMeasurementKeyAt
+ */
+ public int getStatsKeyCount() {
+ return mStatsKeys.length;
+ }
+
+ /**
+ * Get the key for the timer at the given index. Index must be between 0 and the result
+ * of {@link #getStatsKeyCount getStatsKeyCount()}.
+ *
+ * @see #getStatsKeyCount
+ */
+ public int getStatsKeyAt(int index) {
+ return mStatsKeys[index];
+ }
+
+ /**
+ * Return whether this object contains a timers map for the supplied key.
+ */
+ public boolean hasTimers(int key) {
+ return getIndex(mTimersKeys, key) >= 0;
+ }
+
+ /**
+ * Get the TimerStat map for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasTimers hasTimers(int) To check if a value for the given key is present.
+ */
+ public Map<String,TimerStat> getTimers(int key) {
+ final int index = getIndex(mTimersKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad timers key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mTimersValues[index];
+ }
+
+ /**
+ * Get the number of timer map values in this object. Can be used to iterate through
+ * the available timer maps.
+ *
+ * @see #getTimersKeyAt
+ */
+ public int getTimersKeyCount() {
+ return mTimersKeys.length;
+ }
+
+ /**
+ * Get the key for the timer map at the given index. Index must be between 0 and the result
+ * of {@link #getTimersKeyCount getTimersKeyCount()}.
+ *
+ * @see #getTimersKeyCount
+ */
+ public int getTimersKeyAt(int index) {
+ return mTimersKeys[index];
+ }
+
+ /**
+ * Return whether this object contains a measurements map for the supplied key.
+ */
+ public boolean hasMeasurements(int key) {
+ return getIndex(mMeasurementsKeys, key) >= 0;
+ }
+
+ /**
+ * Get the measurements map for the given key.
+ *
+ * @throws IndexOutOfBoundsException When the key is not present in this object.
+ * @see #hasMeasurements To check if a value for the given key is present.
+ */
+ public Map<String,Long> getMeasurements(int key) {
+ final int index = getIndex(mMeasurementsKeys, key);
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Bad measurements key dataType=" + mDataType
+ + " key=" + key);
+ }
+ return mMeasurementsValues[index];
+ }
+
+ /**
+ * Get the number of measurement map values in this object. Can be used to iterate through
+ * the available measurement maps.
+ *
+ * @see #getMeasurementsKeyAt
+ */
+ public int getMeasurementsKeyCount() {
+ return mMeasurementsKeys.length;
+ }
+
+ /**
+ * Get the key for the measurement map at the given index.
+ * Index must be between 0 and the result
+ * of {@link #getMeasurementsKeyCount getMeasurementsKeyCount()}.
+ *
+ * @see #getMeasurementsKeyCount
+ */
+ public int getMeasurementsKeyAt(int index) {
+ return mMeasurementsKeys[index];
+ }
+
+ /**
+ * Get the index in keys of key.
+ */
+ private static int getIndex(int[] keys, int key) {
+ return Arrays.binarySearch(keys, key);
+ }
+
+ /**
+ * Create an ArrayMap<String,HealthStats> from the given Parcel.
+ */
+ private static ArrayMap<String,HealthStats> createHealthStatsMap(Parcel in) {
+ final int count = in.readInt();
+ final ArrayMap<String,HealthStats> result = new ArrayMap<String,HealthStats>(count);
+ for (int i=0; i<count; i++) {
+ result.put(in.readString(), new HealthStats(in));
+ }
+ return result;
+ }
+
+ /**
+ * Create an ArrayMap<String,T extends Parcelable> from the given Parcel using
+ * the given Parcelable.Creator.
+ */
+ private static <T extends Parcelable> ArrayMap<String,T> createParcelableMap(Parcel in,
+ Parcelable.Creator<T> creator) {
+ final int count = in.readInt();
+ final ArrayMap<String,T> result = new ArrayMap<String,T>(count);
+ for (int i=0; i<count; i++) {
+ result.put(in.readString(), creator.createFromParcel(in));
+ }
+ return result;
+ }
+
+ /**
+ * Create an ArrayMap<String,Long> from the given Parcel.
+ */
+ private static ArrayMap<String,Long> createLongsMap(Parcel in) {
+ final int count = in.readInt();
+ final ArrayMap<String,Long> result = new ArrayMap<String,Long>(count);
+ for (int i=0; i<count; i++) {
+ result.put(in.readString(), in.readLong());
+ }
+ return result;
+ }
+}
+
diff --git a/core/java/android/os/health/HealthStatsParceler.aidl b/core/java/android/os/health/HealthStatsParceler.aidl
new file mode 100644
index 0000000..68c348b
--- /dev/null
+++ b/core/java/android/os/health/HealthStatsParceler.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.os.health;
+
+parcelable HealthStatsParceler;
diff --git a/core/java/android/os/health/HealthStatsParceler.java b/core/java/android/os/health/HealthStatsParceler.java
new file mode 100644
index 0000000..28b3694
--- /dev/null
+++ b/core/java/android/os/health/HealthStatsParceler.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.os.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Class to allow sending the HealthStats through aidl generated glue.
+ *
+ * The alternative would be to send a HealthStats object, which would
+ * require constructing one, and then immediately flattening it. This
+ * saves that step at the cost of doing the extra flattening when
+ * accessed in the same process as the writer.
+ *
+ * The HealthStatsWriter passed in the constructor is retained, so don't
+ * reuse them.
+ * @hide
+ */
+public class HealthStatsParceler implements Parcelable {
+ private HealthStatsWriter mWriter;
+ private HealthStats mHealthStats;
+
+ public static final Parcelable.Creator<HealthStatsParceler> CREATOR
+ = new Parcelable.Creator<HealthStatsParceler>() {
+ public HealthStatsParceler createFromParcel(Parcel in) {
+ return new HealthStatsParceler(in);
+ }
+
+ public HealthStatsParceler[] newArray(int size) {
+ return new HealthStatsParceler[size];
+ }
+ };
+
+ public HealthStatsParceler(HealthStatsWriter writer) {
+ mWriter = writer;
+ }
+
+ public HealthStatsParceler(Parcel in) {
+ mHealthStats = new HealthStats(in);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ // See comment on mWriter declaration above.
+ if (mWriter != null) {
+ mWriter.flattenToParcel(out);
+ } else {
+ throw new RuntimeException("Can not re-parcel HealthStatsParceler that was"
+ + " constructed from a Parcel");
+ }
+ }
+
+ public HealthStats getHealthStats() {
+ if (mWriter != null) {
+ final Parcel parcel = Parcel.obtain();
+ mWriter.flattenToParcel(parcel);
+ parcel.setDataPosition(0);
+ mHealthStats = new HealthStats(parcel);
+ parcel.recycle();
+ }
+
+ return mHealthStats;
+ }
+}
+
diff --git a/core/java/android/os/health/HealthStatsWriter.java b/core/java/android/os/health/HealthStatsWriter.java
new file mode 100644
index 0000000..351836b
--- /dev/null
+++ b/core/java/android/os/health/HealthStatsWriter.java
@@ -0,0 +1,288 @@
+/*
+ * 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.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/**
+ * Class to write the health stats data into a parcel, so it can then be
+ * retrieved via a {@link HealthStats} object.
+ *
+ * There is an attempt to keep this class as low overhead as possible, for
+ * example storing an int[] and a long[] instead of a TimerStat[].
+ *
+ * @hide
+ */
+public class HealthStatsWriter {
+ private final HealthKeys.Constants mConstants;
+
+ // TimerStat fields
+ private final boolean[] mTimerFields;
+ private final int[] mTimerCounts;
+ private final long[] mTimerTimes;
+
+ // Measurement fields
+ private final boolean[] mMeasurementFields;
+ private final long[] mMeasurementValues;
+
+ // Stats fields
+ private final ArrayMap<String,HealthStatsWriter>[] mStatsValues;
+
+ // Timers fields
+ private final ArrayMap<String,TimerStat>[] mTimersValues;
+
+ // Measurements fields
+ private final ArrayMap<String,Long>[] mMeasurementsValues;
+
+ /**
+ * Construct a HealthStatsWriter object with the given constants.
+ *
+ * The "getDataType()" of the resulting HealthStats object will be the
+ * short name of the java class that the Constants object was initalized
+ * with.
+ */
+ public HealthStatsWriter(HealthKeys.Constants constants) {
+ mConstants = constants;
+
+ // TimerStat
+ final int timerCount = constants.getSize(HealthKeys.TYPE_TIMER);
+ mTimerFields = new boolean[timerCount];
+ mTimerCounts = new int[timerCount];
+ mTimerTimes = new long[timerCount];
+
+ // Measurement
+ final int measurementCount = constants.getSize(HealthKeys.TYPE_MEASUREMENT);
+ mMeasurementFields = new boolean[measurementCount];
+ mMeasurementValues = new long[measurementCount];
+
+ // Stats
+ final int statsCount = constants.getSize(HealthKeys.TYPE_STATS);
+ mStatsValues = new ArrayMap[statsCount];
+
+ // Timers
+ final int timersCount = constants.getSize(HealthKeys.TYPE_TIMERS);
+ mTimersValues = new ArrayMap[timersCount];
+
+ // Measurements
+ final int measurementsCount = constants.getSize(HealthKeys.TYPE_MEASUREMENTS);
+ mMeasurementsValues = new ArrayMap[measurementsCount];
+ }
+
+ /**
+ * Add a timer for the given key.
+ */
+ public void addTimer(int timerId, int count, long time) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_TIMER, timerId);
+
+ mTimerFields[index] = true;
+ mTimerCounts[index] = count;
+ mTimerTimes[index] = time;
+ }
+
+ /**
+ * Add a measurement for the given key.
+ */
+ public void addMeasurement(int measurementId, long value) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENT, measurementId);
+
+ mMeasurementFields[index] = true;
+ mMeasurementValues[index] = value;
+ }
+
+ /**
+ * Add a recursive HealthStats object for the given key and string name. The value
+ * is stored as a HealthStatsWriter until this object is written to a parcel, so
+ * don't attempt to reuse the HealthStatsWriter.
+ *
+ * The value field should not be null.
+ */
+ public void addStats(int key, String name, HealthStatsWriter value) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_STATS, key);
+
+ ArrayMap<String,HealthStatsWriter> map = mStatsValues[index];
+ if (map == null) {
+ map = mStatsValues[index] = new ArrayMap<String,HealthStatsWriter>(1);
+ }
+ map.put(name, value);
+ }
+
+ /**
+ * Add a TimerStat for the given key and string name.
+ *
+ * The value field should not be null.
+ */
+ public void addTimers(int key, String name, TimerStat value) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_TIMERS, key);
+
+ ArrayMap<String,TimerStat> map = mTimersValues[index];
+ if (map == null) {
+ map = mTimersValues[index] = new ArrayMap<String,TimerStat>(1);
+ }
+ map.put(name, value);
+ }
+
+ /**
+ * Add a measurement for the given key and string name.
+ */
+ public void addMeasurements(int key, String name, long value) {
+ final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENTS, key);
+
+ ArrayMap<String,Long> map = mMeasurementsValues[index];
+ if (map == null) {
+ map = mMeasurementsValues[index] = new ArrayMap<String,Long>(1);
+ }
+ map.put(name, value);
+ }
+
+ /**
+ * Flattens the data in this HealthStatsWriter to the Parcel format
+ * that can be unparceled into a HealthStat.
+ * @more
+ * (Called flattenToParcel because this HealthStatsWriter itself is
+ * not parcelable and we don't flatten all the business about the
+ * HealthKeys.Constants, only the values that were actually supplied)
+ */
+ public void flattenToParcel(Parcel out) {
+ int[] keys;
+
+ // Header fields
+ out.writeString(mConstants.getDataType());
+
+ // TimerStat fields
+ out.writeInt(countBooleanArray(mTimerFields));
+ keys = mConstants.getKeys(HealthKeys.TYPE_TIMER);
+ for (int i=0; i<keys.length; i++) {
+ if (mTimerFields[i]) {
+ out.writeInt(keys[i]);
+ out.writeInt(mTimerCounts[i]);
+ out.writeLong(mTimerTimes[i]);
+ }
+ }
+
+ // Measurement fields
+ out.writeInt(countBooleanArray(mMeasurementFields));
+ keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENT);
+ for (int i=0; i<keys.length; i++) {
+ if (mMeasurementFields[i]) {
+ out.writeInt(keys[i]);
+ out.writeLong(mMeasurementValues[i]);
+ }
+ }
+
+ // Stats
+ out.writeInt(countObjectArray(mStatsValues));
+ keys = mConstants.getKeys(HealthKeys.TYPE_STATS);
+ for (int i=0; i<keys.length; i++) {
+ if (mStatsValues[i] != null) {
+ out.writeInt(keys[i]);
+ writeHealthStatsWriterMap(out, mStatsValues[i]);
+ }
+ }
+
+ // Timers
+ out.writeInt(countObjectArray(mTimersValues));
+ keys = mConstants.getKeys(HealthKeys.TYPE_TIMERS);
+ for (int i=0; i<keys.length; i++) {
+ if (mTimersValues[i] != null) {
+ out.writeInt(keys[i]);
+ writeParcelableMap(out, mTimersValues[i]);
+ }
+ }
+
+ // Measurements
+ out.writeInt(countObjectArray(mMeasurementsValues));
+ keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENTS);
+ for (int i=0; i<keys.length; i++) {
+ if (mMeasurementsValues[i] != null) {
+ out.writeInt(keys[i]);
+ writeLongsMap(out, mMeasurementsValues[i]);
+ }
+ }
+ }
+
+ /**
+ * Count how many of the fields have been set.
+ */
+ private static int countBooleanArray(boolean[] fields) {
+ int count = 0;
+ final int N = fields.length;
+ for (int i=0; i<N; i++) {
+ if (fields[i]) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Count how many of the fields have been set.
+ */
+ private static <T extends Object> int countObjectArray(T[] fields) {
+ int count = 0;
+ final int N = fields.length;
+ for (int i=0; i<N; i++) {
+ if (fields[i] != null) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Write a map of String to HealthStatsWriter to the Parcel.
+ */
+ private static void writeHealthStatsWriterMap(Parcel out,
+ ArrayMap<String,HealthStatsWriter> map) {
+ final int N = map.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ out.writeString(map.keyAt(i));
+ map.valueAt(i).flattenToParcel(out);
+ }
+ }
+
+ /**
+ * Write a map of String to Parcelables to the Parcel.
+ */
+ private static <T extends Parcelable> void writeParcelableMap(Parcel out,
+ ArrayMap<String,T> map) {
+ final int N = map.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ out.writeString(map.keyAt(i));
+ map.valueAt(i).writeToParcel(out, 0);
+ }
+ }
+
+ /**
+ * Write a map of String to Longs to the Parcel.
+ */
+ private static void writeLongsMap(Parcel out, ArrayMap<String,Long> map) {
+ final int N = map.size();
+ out.writeInt(N);
+ for (int i=0; i<N; i++) {
+ out.writeString(map.keyAt(i));
+ out.writeLong(map.valueAt(i));
+ }
+ }
+}
+
+
diff --git a/core/java/android/os/health/PackageHealthStats.java b/core/java/android/os/health/PackageHealthStats.java
new file mode 100644
index 0000000..2c30d5f
--- /dev/null
+++ b/core/java/android/os/health/PackageHealthStats.java
@@ -0,0 +1,50 @@
+/*
+ * 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.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link UidHealthStats#STATS_PACKAGES UidHealthStats.STATS_PACKAGES} key.
+ */
+public final class PackageHealthStats {
+
+ private PackageHealthStats() {
+ }
+
+ /**
+ * Key for a HealthStats with {@link ServiceHealthStats} keys for each of the
+ * services defined in this apk.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+ public static final int STATS_SERVICES = HealthKeys.BASE_PACKAGE + 1;
+
+ /**
+ * Key for a map of the number of times that a package's wakeup alarms have fired
+ * while the device was on battery.
+ *
+ * @see android.app.AlarmManager.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENTS)
+ public static final int MEASUREMENTS_WAKEUP_ALARMS_COUNT = HealthKeys.BASE_PACKAGE + 2;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS
+ = new HealthKeys.Constants(PackageHealthStats.class);
+}
diff --git a/core/java/android/os/health/PidHealthStats.java b/core/java/android/os/health/PidHealthStats.java
new file mode 100644
index 0000000..fe3c02c
--- /dev/null
+++ b/core/java/android/os/health/PidHealthStats.java
@@ -0,0 +1,42 @@
+/*
+ * 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.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link UidHealthStats#STATS_PIDS UidHealthStats.STATS_PIDS} key.
+ */
+public final class PidHealthStats {
+
+ private PidHealthStats() {
+ }
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WAKE_NESTING_COUNT = HealthKeys.BASE_PID + 1;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WAKE_SUM_MS = HealthKeys.BASE_PID + 2;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WAKE_START_MS = HealthKeys.BASE_PID + 3;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS = new HealthKeys.Constants(PidHealthStats.class);
+}
diff --git a/core/java/android/os/health/ProcessHealthStats.java b/core/java/android/os/health/ProcessHealthStats.java
new file mode 100644
index 0000000..e004ecb
--- /dev/null
+++ b/core/java/android/os/health/ProcessHealthStats.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.os.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link UidHealthStats#STATS_PROCESSES UidHealthStats.STATS_PROCESSES} key.
+ */
+public final class ProcessHealthStats {
+
+ private ProcessHealthStats() {
+ }
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_USER_TIME_MS = HealthKeys.BASE_PROCESS + 1;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_SYSTEM_TIME_MS = HealthKeys.BASE_PROCESS + 2;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_STARTS_COUNT = HealthKeys.BASE_PROCESS + 3;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_CRASHES_COUNT = HealthKeys.BASE_PROCESS + 4;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_ANR_COUNT = HealthKeys.BASE_PROCESS + 5;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_FOREGROUND_MS = HealthKeys.BASE_PROCESS + 6;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS = new HealthKeys.Constants(ProcessHealthStats.class);
+}
diff --git a/core/java/android/os/health/ServiceHealthStats.java b/core/java/android/os/health/ServiceHealthStats.java
new file mode 100644
index 0000000..802ad31
--- /dev/null
+++ b/core/java/android/os/health/ServiceHealthStats.java
@@ -0,0 +1,40 @@
+/*
+ * 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.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link HealthStats#getStats(int) HealthStats.getStats(int)} with the
+ * {@link PackageHealthStats#STATS_SERVICES PackageHealthStats.STATS_SERVICES} key.
+ */
+public final class ServiceHealthStats {
+
+ private ServiceHealthStats() {
+ }
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_START_SERVICE_COUNT = HealthKeys.BASE_SERVICE + 1;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_LAUNCH_COUNT = HealthKeys.BASE_SERVICE + 2;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS
+ = new HealthKeys.Constants(ServiceHealthStats.class);
+}
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
new file mode 100644
index 0000000..520e84e
--- /dev/null
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -0,0 +1,121 @@
+/*
+ * 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.health;
+
+import android.content.Context;
+import android.os.BatteryStats;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.app.IBatteryStats;
+
+/**
+ * Provides access to data about how various system resources are used by applications.
+ * @more
+ * <b>Battery Usage</b><br>
+ * The statistics related to power (battery) usage are recorded since the device
+ * was last unplugged. It is expected that applications schedule more work to do
+ * while the device is plugged in (e.g. using {@link android.app.job.JobScheduler
+ * JobScheduler}), and while that can affect charging rates, it is still preferable
+ * to actually draining the battery.
+ */
+public class SystemHealthManager {
+ private final IBatteryStats mBatteryStats;
+
+ /**
+ * Construct a new SystemHealthManager object.
+ * @hide
+ */
+ public SystemHealthManager() {
+ mBatteryStats = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+ }
+
+ /**
+ * Obtain a SystemHealthManager object for the supplied context.
+ */
+ public static SystemHealthManager from(Context context) {
+ return (SystemHealthManager)context.getSystemService(Context.SYSTEM_HEALTH_SERVICE);
+ }
+
+ /**
+ * Return a {@link HealthStats} object containing a snapshot of system health
+ * metrics for the given uid (user-id, which in usually corresponds to application).
+ * @more
+ *
+ * An application must hold the {@link android.Manifest.permission#BATTERY_STATS
+ * android.permission.BATTERY_STATS} permission in order to retrieve any HealthStats
+ * other than its own.
+ *
+ * @param uid User ID for a given application.
+ * @return A {@link HealthStats} object containing the metrics for the requested
+ * application. The keys for this HealthStats object will be from the {@link UidHealthStats}
+ * class.
+ * @see Process#myUid()
+ */
+ public HealthStats takeUidSnapshot(int uid) {
+ try {
+ final HealthStatsParceler parceler = mBatteryStats.takeUidSnapshot(uid);
+ return parceler.getHealthStats();
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Return a {@link HealthStats} object containing a snapshot of system health
+ * metrics for the application calling this API. This method is the same as calling
+ * {@code takeUidSnapshot(Process.myUid())}.
+ *
+ * @return A {@link HealthStats} object containing the metrics for this application. The keys
+ * for this HealthStats object will be from the {@link UidHealthStats} class.
+ */
+ public HealthStats takeMyUidSnapshot() {
+ return takeUidSnapshot(Process.myUid());
+ }
+
+ /**
+ * Return a {@link HealthStats} object containing a snapshot of system health
+ * metrics for the given uids (user-id, which in usually corresponds to application).
+ * @more
+ *
+ * An application must hold the {@link android.Manifest.permission#BATTERY_STATS
+ * android.permission.BATTERY_STATS} permission in order to retrieve any HealthStats
+ * other than its own.
+ *
+ * @param uids An array of User IDs to retrieve.
+ * @return An array of {@link HealthStats} objects containing the metrics for each of
+ * the requested uids. The keys for this HealthStats object will be from the
+ * {@link UidHealthStats} class.
+ */
+ public HealthStats[] takeUidSnapshots(int[] uids) {
+ try {
+ final HealthStatsParceler[] parcelers = mBatteryStats.takeUidSnapshots(uids);
+ final HealthStats[] results = new HealthStats[uids.length];
+ final int N = uids.length;
+ for (int i=0; i<N; i++) {
+ results[i] = parcelers[i].getHealthStats();
+ }
+ return results;
+ } catch (RemoteException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+}
+
diff --git a/core/java/android/os/health/TimerStat.java b/core/java/android/os/health/TimerStat.java
new file mode 100644
index 0000000..fc51b60
--- /dev/null
+++ b/core/java/android/os/health/TimerStat.java
@@ -0,0 +1,116 @@
+/*
+ * 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.health;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A TimerStat object stores a count and a time.
+ *
+ * @more
+ * When possible, the other APIs in this package avoid requiring a TimerStat
+ * object to be constructed, even internally, but the getTimers method on
+ * {@link android.os.health.HealthStats} does require TimerStat objects.
+ */
+public class TimerStat implements Parcelable {
+ private int mCount;
+ private long mTime;
+
+ /**
+ * The CREATOR instance for use by aidl Binder interfaces.
+ */
+ public static final Parcelable.Creator<TimerStat> CREATOR
+ = new Parcelable.Creator<TimerStat>() {
+ public TimerStat createFromParcel(Parcel in) {
+ return new TimerStat(in);
+ }
+
+ public TimerStat[] newArray(int size) {
+ return new TimerStat[size];
+ }
+ };
+
+ /**
+ * Construct an empty TimerStat object with the count and time set to 0.
+ */
+ public TimerStat() {
+ }
+
+ /**
+ * Construct a TimerStat object with the supplied count and time fields.
+ *
+ * @param count The count
+ * @param time The time
+ */
+ public TimerStat(int count, long time) {
+ mCount = count;
+ mTime = time;
+ }
+
+ /**
+ * Construct a TimerStat object reading the values from a {@link android.os.Parcel Parcel}
+ * object.
+ */
+ public TimerStat(Parcel in) {
+ mCount = in.readInt();
+ mTime = in.readLong();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Write this TimerStat object to a parcel.
+ */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mCount);
+ out.writeLong(mTime);
+ }
+
+ /**
+ * Set the count for this timer.
+ */
+ public void setCount(int count) {
+ mCount = count;
+ }
+
+ /**
+ * Get the count for this timer.
+ */
+ public int getCount() {
+ return mCount;
+ }
+
+ /**
+ * Set the time for this timer.
+ */
+ public void setTime(long time) {
+ mTime = time;
+ }
+
+ /**
+ * Get the time for this timer.
+ */
+ public long getTime() {
+ return mTime;
+ }
+}
diff --git a/core/java/android/os/health/UidHealthStats.java b/core/java/android/os/health/UidHealthStats.java
new file mode 100644
index 0000000..c7d257f
--- /dev/null
+++ b/core/java/android/os/health/UidHealthStats.java
@@ -0,0 +1,280 @@
+/*
+ * 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.health;
+
+/**
+ * Keys for {@link HealthStats} returned from
+ * {@link SystemHealthManager#takeUidSnapshot(int) SystemHealthManager.takeUidSnapshot(int)},
+ * {@link SystemHealthManager#takeMyUidSnapshot() SystemHealthManager.takeMyUidSnapshot()}, and
+ * {@link SystemHealthManager#takeUidSnapshots(int[]) SystemHealthManager.takeUidSnapshots(int[])}.
+ */
+public final class UidHealthStats {
+
+ private UidHealthStats() {
+ }
+
+ /**
+ * How many milliseconds this statistics report covers in wall-clock time while the
+ * device was on battery including both screen-on and screen-off time.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_REALTIME_BATTERY_MS = HealthKeys.BASE_UID + 1;
+
+ /**
+ * How many milliseconds this statistics report covers that the CPU was running while the
+ * device was on battery including both screen-on and screen-off time.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_UPTIME_BATTERY_MS = HealthKeys.BASE_UID + 2;
+
+ /**
+ * How many milliseconds this statistics report covers in wall-clock time while the
+ * device was on battery including both screen-on and screen-off time.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 3;
+
+ /**
+ * How many milliseconds this statistics report covers that the CPU was running while the
+ * device was on battery including both screen-on and screen-off time.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS = HealthKeys.BASE_UID + 4;
+
+ /**
+ * Key for a TimerStat for the times a
+ * {@link android.os.PowerManager#FULL_WAKE_LOCK full wake lock}
+ * was acquired for this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_WAKELOCKS_FULL = HealthKeys.BASE_UID + 5;
+
+ /**
+ * Key for a TimerStat for the times a
+ * {@link android.os.PowerManager#PARTIAL_WAKE_LOCK full wake lock}
+ * was acquired for this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_WAKELOCKS_PARTIAL = HealthKeys.BASE_UID + 6;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_WAKELOCKS_WINDOW = HealthKeys.BASE_UID + 7;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_WAKELOCKS_DRAW = HealthKeys.BASE_UID + 8;
+
+ /**
+ * Key for a map of Timers for the sync adapter syncs that were done for
+ * this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_SYNCS = HealthKeys.BASE_UID + 9;
+
+ /**
+ * Key for a map of Timers for the {@link android.app.job.JobScheduler} jobs for
+ * this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_JOBS = HealthKeys.BASE_UID + 10;
+
+ /**
+ * Key for a timer for the applications use of the GPS sensor.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_GPS_SENSOR = HealthKeys.BASE_UID + 11;
+
+ /**
+ * Key for a map of the sensor usage for this uid. The keys are a
+ * string representation of the handle for the sensor.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMERS)
+ public static final int TIMERS_SENSORS = HealthKeys.BASE_UID + 12;
+
+ /**
+ * Key for a HealthStats with {@link PidHealthStats} keys for each of the
+ * currently running processes for this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+ public static final int STATS_PIDS = HealthKeys.BASE_UID + 13;
+
+ /**
+ * Key for a HealthStats with {@link ProcessHealthStats} keys for each of the
+ * named processes for this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+ public static final int STATS_PROCESSES = HealthKeys.BASE_UID + 14;
+
+ /**
+ * Key for a HealthStats with {@link PackageHealthStats} keys for each of the
+ * APKs that share this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_STATS)
+ public static final int STATS_PACKAGES = HealthKeys.BASE_UID + 15;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_IDLE_MS = HealthKeys.BASE_UID + 16;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_RX_MS = HealthKeys.BASE_UID + 17;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_TX_MS = HealthKeys.BASE_UID + 18;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_POWER_MAMS = HealthKeys.BASE_UID + 19;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_IDLE_MS = HealthKeys.BASE_UID + 20;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_RX_MS = HealthKeys.BASE_UID + 21;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_TX_MS = HealthKeys.BASE_UID + 22;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_POWER_MAMS = HealthKeys.BASE_UID + 23;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_IDLE_MS = HealthKeys.BASE_UID + 24;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_RX_MS = HealthKeys.BASE_UID + 25;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_TX_MS = HealthKeys.BASE_UID + 26;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_POWER_MAMS = HealthKeys.BASE_UID + 27;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_RUNNING_MS = HealthKeys.BASE_UID + 28;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_FULL_LOCK_MS = HealthKeys.BASE_UID + 29;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_WIFI_SCAN = HealthKeys.BASE_UID + 30;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_MULTICAST_MS = HealthKeys.BASE_UID + 31;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_AUDIO = HealthKeys.BASE_UID + 32;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_VIDEO = HealthKeys.BASE_UID + 33;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_FLASHLIGHT = HealthKeys.BASE_UID + 34;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_CAMERA = HealthKeys.BASE_UID + 35;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_FOREGROUND_ACTIVITY = HealthKeys.BASE_UID + 36;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_BLUETOOTH_SCAN = HealthKeys.BASE_UID + 37;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_TOP_MS = HealthKeys.BASE_UID + 38;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS = HealthKeys.BASE_UID + 39;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_TOP_SLEEPING_MS = HealthKeys.BASE_UID + 40;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_FOREGROUND_MS = HealthKeys.BASE_UID + 41;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_BACKGROUND_MS = HealthKeys.BASE_UID + 42;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_PROCESS_STATE_CACHED_MS = HealthKeys.BASE_UID + 43;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_VIBRATOR = HealthKeys.BASE_UID + 44;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_OTHER_USER_ACTIVITY_COUNT = HealthKeys.BASE_UID + 45;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT = HealthKeys.BASE_UID + 46;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT = HealthKeys.BASE_UID + 47;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_RX_BYTES = HealthKeys.BASE_UID + 48;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_TX_BYTES = HealthKeys.BASE_UID + 49;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_RX_BYTES = HealthKeys.BASE_UID + 50;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_TX_BYTES = HealthKeys.BASE_UID + 51;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_RX_BYTES = HealthKeys.BASE_UID + 52;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_TX_BYTES = HealthKeys.BASE_UID + 53;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_RX_PACKETS = HealthKeys.BASE_UID + 54;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_MOBILE_TX_PACKETS = HealthKeys.BASE_UID + 55;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_RX_PACKETS = HealthKeys.BASE_UID + 56;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_WIFI_TX_PACKETS = HealthKeys.BASE_UID + 57;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_RX_PACKETS = HealthKeys.BASE_UID + 58;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_BLUETOOTH_TX_PACKETS = HealthKeys.BASE_UID + 59;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_TIMER)
+ public static final int TIMER_MOBILE_RADIO_ACTIVE = HealthKeys.BASE_UID + 61;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_USER_CPU_TIME_US = HealthKeys.BASE_UID + 62;
+
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_SYSTEM_CPU_TIME_US = HealthKeys.BASE_UID + 63;
+
+ /**
+ * An estimate of the number of milliamp-microsends used by this uid.
+ */
+ @HealthKeys.Constant(type=HealthKeys.TYPE_MEASUREMENT)
+ public static final int MEASUREMENT_CPU_POWER_MAUS = HealthKeys.BASE_UID + 64;
+
+ /**
+ * @hide
+ */
+ public static final HealthKeys.Constants CONSTANTS = new HealthKeys.Constants(UidHealthStats.class);
+}
+
diff --git a/core/java/android/os/health/package.html b/core/java/android/os/health/package.html
new file mode 100644
index 0000000..3a46a5b
--- /dev/null
+++ b/core/java/android/os/health/package.html
@@ -0,0 +1,39 @@
+<html>
+<body>
+
+The android.os.health package contains a set of classes to provide data
+to track the system resources of applications.
+<p>
+Applications running in the background are responsible for a significant amount
+of battery usage on a typical android device. There are several things that
+applications can do in order to reduce their impact. For example, by using
+{@link android.app.job.JobScheduler JobScheduler}, an application does not need
+to independently monitor whether the network is available, whether the device is
+plugged in, etc. In addition to being simpler to use, the application's
+services are only started when the required conditions have been met. But even
+when using the appropriate helper APIs, applications still can reduce their
+footprint. This package provides more insight into what is going on behind the
+scenes when an application is running.
+<p>
+Application data is tracked by which user id (uid) is using particular
+resources. A snapshot of an application's measurements can be taken with the
+{@link android.os.health.SystemHealthManager#takeMyUidSnapshot() SystemHealth.takeMyUidSnapshot()}
+method. The {@link android.os.health.HealthStats} object returned contains the
+statistics.
+<p>
+<b>HealthStats</b><br>
+In order to be returned efficiently, the {@link android.os.health.HealthStats}
+class uses a set of int keys to identify the data returned. The
+{@link android.os.health.UidHealthStats}, {@link android.os.health.PidHealthStats},
+{@link android.os.health.PackageHealthStats} , {@link android.os.health.ProcessHealthStats},
+and {@link android.os.health.ServiceHealthStats} classes provide those constants.
+Each {@link android.os.health.HealthStats} object will be associated with
+exactly one of those classes. The object returned from
+{@link android.os.health.SystemHealthManager#takeMyUidSnapshot() SystemHealth.takeMyUidSnapshot()}
+will be using the {@link android.os.health.UidHealthStats} keys, as it contains all
+of the data available for that uid.
+
+
+</body>
+</html>
+
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index fc440d2..c21c65a 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -2286,7 +2286,12 @@
/**
* Determines the encryption state of the volume.
- * @return a numerical value. See {@code ENCRYPTION_STATE_*} for possible values.
+ * @return a numerical value. See {@code ENCRYPTION_STATE_*} for possible
+ * values.
+ * Note that this has been replaced in most cases by the APIs in
+ * StorageManager (see isEncryptable and below)
+ * This is still useful to get the error state when encryption has failed
+ * and CryptKeeper needs to throw up a screen advising the user what to do
*/
public int getEncryptionState() throws RemoteException;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 17df708..61e6b95 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1025,21 +1025,119 @@
}
}
- /** {@hide} */
- public static boolean isFileBasedEncryptionEnabled() {
- return isNativeFileBasedEncryptionEnabled() || isEmulatedFileBasedEncryptionEnabled();
+ /** {@hide}
+ * Is this device encryptable or already encrypted?
+ * @return true for encryptable or encrypted
+ * false not encrypted and not encryptable
+ */
+ public static boolean isEncryptable() {
+ final String state = SystemProperties.get("ro.crypto.state", "unsupported");
+ return !"unsupported".equalsIgnoreCase(state);
+ }
+
+ /** {@hide}
+ * Is this device already encrypted?
+ * @return true for encrypted. (Implies isEncryptable() == true)
+ * false not encrypted
+ */
+ public static boolean isEncrypted() {
+ final String state = SystemProperties.get("ro.crypto.state", "");
+ return "encrypted".equalsIgnoreCase(state);
+ }
+
+ /** {@hide}
+ * Is this device file encrypted?
+ * @return true for file encrypted. (Implies isEncrypted() == true)
+ * false not encrypted or block encrypted
+ */
+ public static boolean isFileEncryptedNativeOnly() {
+ if (!isEncrypted()) {
+ return false;
+ }
+
+ final String status = SystemProperties.get("ro.crypto.type", "");
+ return "file".equalsIgnoreCase(status);
+ }
+
+ /** {@hide}
+ * Is this device block encrypted?
+ * @return true for block encrypted. (Implies isEncrypted() == true)
+ * false not encrypted or file encrypted
+ */
+ public static boolean isBlockEncrypted() {
+ if (!isEncrypted()) {
+ return false;
+ }
+ final String status = SystemProperties.get("ro.crypto.type", "");
+ return "block".equalsIgnoreCase(status);
+ }
+
+ /** {@hide}
+ * Is this device block encrypted with credentials?
+ * @return true for crediential block encrypted.
+ * (Implies isBlockEncrypted() == true)
+ * false not encrypted, file encrypted or default block encrypted
+ */
+ public static boolean isNonDefaultBlockEncrypted() {
+ if (!isBlockEncrypted()) {
+ return false;
+ }
+
+ try {
+ IMountService mountService = IMountService.Stub.asInterface(
+ ServiceManager.getService("mount"));
+ return mountService.getPasswordType() != CRYPT_TYPE_DEFAULT;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting encryption type");
+ return false;
+ }
+ }
+
+ /** {@hide}
+ * Is this device in the process of being block encrypted?
+ * @return true for encrypting.
+ * false otherwise
+ * Whether device isEncrypted at this point is undefined
+ * Note that only system services and CryptKeeper will ever see this return
+ * true - no app will ever be launched in this state.
+ * Also note that this state will not change without a teardown of the
+ * framework, so no service needs to check for changes during their lifespan
+ */
+ public static boolean isBlockEncrypting() {
+ final String state = SystemProperties.get("vold.encrypt_progress", "");
+ return !"".equalsIgnoreCase(state);
+ }
+
+ /** {@hide}
+ * Is this device non default block encrypted and in the process of
+ * prompting for credentials?
+ * @return true for prompting for credentials.
+ * (Implies isNonDefaultBlockEncrypted() == true)
+ * false otherwise
+ * Note that only system services and CryptKeeper will ever see this return
+ * true - no app will ever be launched in this state.
+ * Also note that this state will not change without a teardown of the
+ * framework, so no service needs to check for changes during their lifespan
+ */
+ public static boolean inCryptKeeperBounce() {
+ final String status = SystemProperties.get("vold.decrypt");
+ return "trigger_restart_min_framework".equals(status);
}
/** {@hide} */
- public static boolean isNativeFileBasedEncryptionEnabled() {
- return "file".equals(SystemProperties.get("ro.crypto.type", "none"));
- }
-
- /** {@hide} */
- public static boolean isEmulatedFileBasedEncryptionEnabled() {
+ public static boolean isFileEncryptedEmulatedOnly() {
return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
}
+ /** {@hide}
+ * Is this device running in a file encrypted mode, either native or emulated?
+ * @return true for file encrypted, false otherwise
+ */
+ public static boolean isFileEncryptedNativeOrEmulated() {
+ return isFileEncryptedNativeOnly()
+ || isFileEncryptedEmulatedOnly();
+ }
+
/** {@hide} */
public static File maybeTranslateEmulatedPathToInternal(File path) {
final IMountService mountService = IMountService.Stub.asInterface(
diff --git a/core/java/android/preference/EditTextPreference.java b/core/java/android/preference/EditTextPreference.java
index ff37042..9467c22 100644
--- a/core/java/android/preference/EditTextPreference.java
+++ b/core/java/android/preference/EditTextPreference.java
@@ -49,6 +49,7 @@
private EditText mEditText;
private String mText;
+ private boolean mTextSet;
public EditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
@@ -85,15 +86,16 @@
* @param text The text to save
*/
public void setText(String text) {
- final boolean wasBlocking = shouldDisableDependents();
-
- mText = text;
-
- persistString(text);
-
- final boolean isBlocking = shouldDisableDependents();
- if (isBlocking != wasBlocking) {
- notifyDependencyChange(isBlocking);
+ // Always persist/notify the first time.
+ final boolean changed = !TextUtils.equals(mText, text);
+ if (changed || !mTextSet) {
+ mText = text;
+ mTextSet = true;
+ persistString(text);
+ if(changed) {
+ notifyDependencyChange(shouldDisableDependents());
+ notifyChanged();
+ }
}
}
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index 9a80e37..5eb8cc2 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -16,12 +16,14 @@
package android.print;
+import android.content.ComponentName;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.print.IPrinterDiscoveryObserver;
import android.print.IPrintDocumentAdapter;
import android.print.PrintJobId;
import android.print.IPrintJobStateChangeListener;
+import android.print.IPrintServicesChangeListener;
import android.print.PrinterId;
import android.print.PrintJobInfo;
import android.print.PrintAttributes;
@@ -45,8 +47,47 @@
void removePrintJobStateChangeListener(in IPrintJobStateChangeListener listener,
int userId);
- List<PrintServiceInfo> getInstalledPrintServices(int userId);
- List<PrintServiceInfo> getEnabledPrintServices(int userId);
+ /**
+ * Listen for changes to the installed and enabled print services.
+ *
+ * @param listener the listener to add
+ * @param userId the id of the user listening
+ *
+ * @see android.print.PrintManager#getPrintServices(int, String)
+ */
+ void addPrintServicesChangeListener(in IPrintServicesChangeListener listener,
+ int userId);
+
+ /**
+ * Stop listening for changes to the installed and enabled print services.
+ *
+ * @param listener the listener to remove
+ * @param userId the id of the user requesting the removal
+ *
+ * @see android.print.PrintManager#getPrintServices(int, String)
+ */
+ void removePrintServicesChangeListener(in IPrintServicesChangeListener listener,
+ int userId);
+
+ /**
+ * Get the print services.
+ *
+ * @param selectionFlags flags selecting which services to get
+ * @param selectedService if not null, the id of the print service to get
+ * @param userId the id of the user requesting the services
+ *
+ * @return the list of selected print services.
+ */
+ List<PrintServiceInfo> getPrintServices(int selectionFlags, int userId);
+
+ /**
+ * Enable or disable a print service.
+ *
+ * @param service The service to enabled or disable
+ * @param isEnabled whether the service should be enabled or disabled
+ * @param userId the id of the user requesting the services
+ */
+ void setPrintServiceEnabled(in ComponentName service, boolean isEnabled, int userId);
void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId);
void startPrinterDiscovery(in IPrinterDiscoveryObserver observer,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java b/core/java/android/print/IPrintServicesChangeListener.aidl
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
copy to core/java/android/print/IPrintServicesChangeListener.aidl
index 264c2c4..2a7ce89 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
+++ b/core/java/android/print/IPrintServicesChangeListener.aidl
@@ -11,21 +11,16 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
+package android.print;
/**
- * Fires when the user invoked the gesture to dock the top/left task.
+ * Interface for observing print services changes.
+ *
+ * @hide
*/
-public class DockingTopTaskEvent extends EventBus.Event {
-
- public int dragMode;
-
- public DockingTopTaskEvent(int dragMode) {
- this.dragMode = dragMode;
- }
+oneway interface IPrintServicesChangeListener {
+ void onPrintServicesChanged();
}
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 0540036..25fc968 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
@@ -111,6 +112,38 @@
private static final boolean DEBUG = false;
private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 1;
+ private static final int MSG_NOTIFY_PRINT_SERVICES_CHANGED = 2;
+
+ /**
+ * Package name of print spooler.
+ *
+ * @hide
+ */
+ public static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
+
+ /**
+ * Select enabled services.
+ * </p>
+ * @see #getPrintServices
+ * @hide
+ */
+ public static final int ENABLED_SERVICES = 1 << 0;
+
+ /**
+ * Select disabled services.
+ * </p>
+ * @see #getPrintServices
+ * @hide
+ */
+ public static final int DISABLED_SERVICES = 1 << 1;
+
+ /**
+ * Select all services.
+ * </p>
+ * @see #getPrintServices
+ * @hide
+ */
+ public static final int ALL_SERVICES = ENABLED_SERVICES | DISABLED_SERVICES;
/**
* The action for launching the print dialog activity.
@@ -165,7 +198,10 @@
private final Handler mHandler;
- private Map<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper> mPrintJobStateChangeListeners;
+ private Map<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper>
+ mPrintJobStateChangeListeners;
+ private Map<PrintServicesChangeListener, PrintServicesChangeListenerWrapper>
+ mPrintServicesChangeListeners;
/** @hide */
public interface PrintJobStateChangeListener {
@@ -178,6 +214,15 @@
public void onPrintJobStateChanged(PrintJobId printJobId);
}
+ /** @hide */
+ public interface PrintServicesChangeListener {
+
+ /**
+ * Callback notifying that the print services changed.
+ */
+ public void onPrintServicesChanged();
+ }
+
/**
* Creates a new instance.
*
@@ -207,6 +252,15 @@
}
args.recycle();
} break;
+ case MSG_NOTIFY_PRINT_SERVICES_CHANGED: {
+ PrintServicesChangeListenerWrapper wrapper =
+ (PrintServicesChangeListenerWrapper) message.obj;
+ PrintServicesChangeListener listener = wrapper.getListener();
+ if (listener != null) {
+ listener.onPrintServicesChanged();
+ }
+ } break;
+
}
}
};
@@ -478,42 +532,82 @@
}
/**
- * Gets the list of enabled print services.
+ * Listen for changes to the installed and enabled print services.
*
- * @return The enabled service list or an empty list.
- * @hide
+ * @param listener the listener to add
+ *
+ * @see android.print.PrintManager#getPrintServices
*/
- public List<PrintServiceInfo> getEnabledPrintServices() {
+ void addPrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
if (mService == null) {
Log.w(LOG_TAG, "Feature android.software.print not available");
- return Collections.emptyList();
+ return;
}
+ if (mPrintServicesChangeListeners == null) {
+ mPrintServicesChangeListeners = new ArrayMap<PrintServicesChangeListener,
+ PrintServicesChangeListenerWrapper>();
+ }
+ PrintServicesChangeListenerWrapper wrappedListener =
+ new PrintServicesChangeListenerWrapper(listener, mHandler);
try {
- List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId);
- if (enabledServices != null) {
- return enabledServices;
- }
+ mService.addPrintServicesChangeListener(wrappedListener, mUserId);
+ mPrintServicesChangeListeners.put(listener, wrappedListener);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
- return Collections.emptyList();
}
/**
- * Gets the list of installed print services.
+ * Stop listening for changes to the installed and enabled print services.
*
- * @return The installed service list or an empty list.
- * @hide
+ * @param listener the listener to remove
+ *
+ * @see android.print.PrintManager#getPrintServices
*/
- public List<PrintServiceInfo> getInstalledPrintServices() {
+ void removePrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
if (mService == null) {
Log.w(LOG_TAG, "Feature android.software.print not available");
- return Collections.emptyList();
+ return;
}
+ if (mPrintServicesChangeListeners == null) {
+ return;
+ }
+ PrintServicesChangeListenerWrapper wrappedListener =
+ mPrintServicesChangeListeners.remove(listener);
+ if (wrappedListener == null) {
+ return;
+ }
+ if (mPrintServicesChangeListeners.isEmpty()) {
+ mPrintServicesChangeListeners = null;
+ }
+ wrappedListener.destroy();
try {
- List<PrintServiceInfo> installedServices = mService.getInstalledPrintServices(mUserId);
- if (installedServices != null) {
- return installedServices;
+ mService.removePrintServicesChangeListener(wrappedListener, mUserId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error removing print services change listener", re);
+ }
+ }
+
+
+ /**
+ * Gets the list of print services, but does not register for updates. The user has to register
+ * for updates by itself, or use {@link PrintServicesLoader}.
+ *
+ * @param selectionFlags flags selecting which services to get. Either
+ * {@link #ENABLED_SERVICES},{@link #DISABLED_SERVICES}, or both.
+ *
+ * @return The enabled service list or an empty list.
+ *
+ * @see #addPrintServicesChangeListener(PrintServicesChangeListener)
+ * @see #removePrintServicesChangeListener(PrintServicesChangeListener)
+ *
+ * @hide
+ */
+ public @NonNull List<PrintServiceInfo> getPrintServices(int selectionFlags) {
+ try {
+ List<PrintServiceInfo> services = mService.getPrintServices(selectionFlags, mUserId);
+ if (services != null) {
+ return services;
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -533,6 +627,26 @@
}
/**
+ * Enable or disable a print service.
+ *
+ * @param service The service to enabled or disable
+ * @param isEnabled whether the service should be enabled or disabled
+ *
+ * @hide
+ */
+ public void setPrintServiceEnabled(@NonNull ComponentName service, boolean isEnabled) {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return;
+ }
+ try {
+ mService.setPrintServiceEnabled(service, isEnabled, mUserId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error enabling or disabling " + service, re);
+ }
+ }
+
+ /**
* @hide
*/
public static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
@@ -1096,4 +1210,36 @@
return mWeakListener.get();
}
}
+
+ /**
+ * @hide
+ */
+ public static final class PrintServicesChangeListenerWrapper extends
+ IPrintServicesChangeListener.Stub {
+ private final WeakReference<PrintServicesChangeListener> mWeakListener;
+ private final WeakReference<Handler> mWeakHandler;
+
+ public PrintServicesChangeListenerWrapper(PrintServicesChangeListener listener,
+ Handler handler) {
+ mWeakListener = new WeakReference<>(listener);
+ mWeakHandler = new WeakReference<>(handler);
+ }
+
+ @Override
+ public void onPrintServicesChanged() {
+ Handler handler = mWeakHandler.get();
+ PrintServicesChangeListener listener = mWeakListener.get();
+ if (handler != null && listener != null) {
+ handler.obtainMessage(MSG_NOTIFY_PRINT_SERVICES_CHANGED, this).sendToTarget();
+ }
+ }
+
+ public void destroy() {
+ mWeakListener.clear();
+ }
+
+ public PrintServicesChangeListener getListener() {
+ return mWeakListener.get();
+ }
+ }
}
diff --git a/core/java/android/print/PrintServicesLoader.java b/core/java/android/print/PrintServicesLoader.java
new file mode 100644
index 0000000..ed41114
--- /dev/null
+++ b/core/java/android/print/PrintServicesLoader.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.print;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Loader;
+import android.os.Handler;
+import android.os.Message;
+import android.printservice.PrintServiceInfo;
+
+import java.util.List;
+
+/**
+ * Loader for the list of print services. Can be parametrized to select a subset.
+ *
+ * @hide
+ */
+public class PrintServicesLoader extends Loader<List<PrintServiceInfo>> {
+ /** What type of services to load. */
+ private final int mSelectionFlags;
+
+ /** The print manager to be used by this object */
+ private final @NonNull PrintManager mPrintManager;
+
+ /** Handler to sequentialize the delivery of the results to the main thread */
+ private Handler mHandler;
+
+ /** Listens for updates to the data from the platform */
+ private PrintManager.PrintServicesChangeListener mListener;
+
+ /**
+ * Create a new PrintServicesLoader.
+ *
+ * @param selectionFlags What type of services to load.
+ */
+ public PrintServicesLoader(@NonNull PrintManager printManager, @NonNull Context context,
+ int selectionFlags) {
+ super(context);
+ mPrintManager = printManager;
+ mSelectionFlags = selectionFlags;
+ }
+
+ @Override
+ protected void onForceLoad() {
+ queueNewResult();
+ }
+
+ /**
+ * Read the print services and queue it to be delivered on the main thread.
+ */
+ private void queueNewResult() {
+ Message m = mHandler.obtainMessage(0);
+ m.obj = mPrintManager.getPrintServices(mSelectionFlags);
+ mHandler.sendMessage(m);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ mHandler = new MyHandler();
+ mListener = new PrintManager.PrintServicesChangeListener() {
+ @Override public void onPrintServicesChanged() {
+ queueNewResult();
+ }
+ };
+
+ mPrintManager.addPrintServicesChangeListener(mListener);
+
+ // Immediately deliver a result
+ deliverResult(mPrintManager.getPrintServices(mSelectionFlags));
+ }
+
+ @Override
+ protected void onStopLoading() {
+ if (mListener != null) {
+ mPrintManager.removePrintServicesChangeListener(mListener);
+ mListener = null;
+ }
+
+ if (mHandler != null) {
+ mHandler.removeMessages(0);
+ mHandler = null;
+ }
+ }
+
+ @Override
+ protected void onReset() {
+ onStopLoading();
+ }
+
+ /**
+ * Handler to sequentialize all the updates to the main thread.
+ */
+ private class MyHandler extends Handler {
+ /**
+ * Create a new handler on the main thread.
+ */
+ public MyHandler() {
+ super(getContext().getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+
+ if (isStarted()) {
+ deliverResult((List<PrintServiceInfo>) msg.obj);
+ }
+ }
+ }
+}
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 91e01f2..45e3d47 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -55,6 +55,8 @@
private final String mId;
+ private boolean mIsEnabled;
+
private final ResolveInfo mResolveInfo;
private final String mSettingsActivityName;
@@ -70,6 +72,7 @@
*/
public PrintServiceInfo(Parcel parcel) {
mId = parcel.readString();
+ mIsEnabled = parcel.readByte() != 0;
mResolveInfo = parcel.readParcelable(null);
mSettingsActivityName = parcel.readString();
mAddPrintersActivityName = parcel.readString();
@@ -180,6 +183,24 @@
}
/**
+ * If the service was enabled when it was read from the system.
+ *
+ * @return The id.
+ */
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /**
+ * Mark a service as enabled or not
+ *
+ * @param isEnabled If the service should be marked as enabled.
+ */
+ public void setIsEnabled(boolean isEnabled) {
+ mIsEnabled = isEnabled;
+ }
+
+ /**
* The service {@link ResolveInfo}.
*
* @return The info.
@@ -238,6 +259,7 @@
@Override
public void writeToParcel(Parcel parcel, int flagz) {
parcel.writeString(mId);
+ parcel.writeByte((byte)(mIsEnabled ? 1 : 0));
parcel.writeParcelable(mResolveInfo, 0);
parcel.writeString(mSettingsActivityName);
parcel.writeString(mAddPrintersActivityName);
@@ -276,6 +298,7 @@
StringBuilder builder = new StringBuilder();
builder.append("PrintServiceInfo{");
builder.append("id=").append(mId);
+ builder.append("isEnabled=").append(mIsEnabled);
builder.append(", resolveInfo=").append(mResolveInfo);
builder.append(", settingsActivityName=").append(mSettingsActivityName);
builder.append(", addPrintersActivityName=").append(mAddPrintersActivityName);
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 0439fe2..def303a 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -29,11 +29,14 @@
* <p>
* The content provider exposes a table containing blocked numbers. The columns and URIs for
* accessing this table are defined by the {@link BlockedNumbers} class. Messages, and calls from
- * blocked numbers are discarded by the platform. If the user contacts emergency
- * services, number blocking is disabled by the platform for a duration defined by
+ * blocked numbers are discarded by the platform. Notifications upon provider changes can be
+ * received using a {@link android.database.ContentObserver}.
+ * </p>
+ * <p>
+ * The platform will not block messages, and calls from emergency numbers as defined by
+ * {@link android.telephony.PhoneNumberUtils#isEmergencyNumber(String)}. If the user contacts
+ * emergency services, number blocking is disabled by the platform for a duration defined by
* {@link android.telephony.CarrierConfigManager#KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT}.
- * Notifications upon provider changes can be received using a
- * {@link android.database.ContentObserver}.
* </p>
*
* <h3> Permissions </h3>
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index e7c4a07..e2ae133 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>
*/
@@ -196,6 +197,12 @@
public static final int REJECTED_TYPE = 5;
/** Call log type for calls blocked automatically. */
public static final int BLOCKED_TYPE = 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.
+ */
+ public static final int ANSWERED_EXTERNALLY_TYPE = 7;
/**
* Bit-mask describing features of the call (e.g. video).
@@ -207,6 +214,9 @@
/** Call had video. */
public static final int FEATURES_VIDEO = 0x1;
+ /** Call was pulled externally. */
+ public static final int FEATURES_PULLED_EXTERNALLY = 0x2;
+
/**
* The phone number as the user entered it.
* <P>Type: TEXT</P>
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 4b43f53..3658df9 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -5185,6 +5185,10 @@
* <li>
* Corp contacts will get artificial {@link #LOOKUP_KEY}s too.
* </li>
+ * <li>
+ * Returned work contact IDs and lookup keys are not accepted in places that not
+ * explicitly say to accept them.
+ * </li>
* </ul>
* <p>
* A contact lookup URL built by
@@ -6194,6 +6198,10 @@
* <li>
* Corp contacts will get artificial {@link #LOOKUP_KEY}s too.
* </li>
+ * <li>
+ * Returned work contact IDs and lookup keys are not accepted in places that not
+ * explicitly say to accept them.
+ * </li>
* </ul>
* <p>
* A contact lookup URL built by
@@ -8466,7 +8474,9 @@
* around this {@link View}.
* @param lookupUri A {@link ContactsContract.Contacts#CONTENT_LOOKUP_URI} style
* {@link Uri} that describes a specific contact to feature
- * in this dialog.
+ * in this dialog. A work lookup uri is supported here,
+ * see {@link CommonDataKinds.Email#ENTERPRISE_CONTENT_LOOKUP_URI} and
+ * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
* @param mode Any of {@link #MODE_SMALL}, {@link #MODE_MEDIUM}, or
* {@link #MODE_LARGE}, indicating the desired dialog size,
* when supported.
@@ -8500,7 +8510,9 @@
* @param lookupUri A
* {@link ContactsContract.Contacts#CONTENT_LOOKUP_URI} style
* {@link Uri} that describes a specific contact to feature
- * in this dialog.
+ * in this dialog. A work lookup uri is supported here,
+ * see {@link CommonDataKinds.Email#ENTERPRISE_CONTENT_LOOKUP_URI} and
+ * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
* @param mode Any of {@link #MODE_SMALL}, {@link #MODE_MEDIUM}, or
* {@link #MODE_LARGE}, indicating the desired dialog size,
* when supported.
@@ -8531,7 +8543,9 @@
* @param lookupUri A
* {@link ContactsContract.Contacts#CONTENT_LOOKUP_URI} style
* {@link Uri} that describes a specific contact to feature
- * in this dialog.
+ * in this dialog. A work lookup uri is supported here,
+ * see {@link CommonDataKinds.Email#ENTERPRISE_CONTENT_LOOKUP_URI} and
+ * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
* @param excludeMimes Optional list of {@link Data#MIMETYPE} MIME-types
* to exclude when showing this dialog. For example, when
* already viewing the contact details card, this can be used
@@ -8569,7 +8583,9 @@
* @param lookupUri A
* {@link ContactsContract.Contacts#CONTENT_LOOKUP_URI} style
* {@link Uri} that describes a specific contact to feature
- * in this dialog.
+ * in this dialog. A work lookup uri is supported here,
+ * see {@link CommonDataKinds.Email#ENTERPRISE_CONTENT_LOOKUP_URI} and
+ * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
* @param excludeMimes Optional list of {@link Data#MIMETYPE} MIME-types
* to exclude when showing this dialog. For example, when
* already viewing the contact details card, this can be used
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 03a1ca6..e7a9b7d 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -119,8 +119,6 @@
public static final String EXTRA_PROMPT = "android.provider.extra.PROMPT";
/** {@hide} */
- public static final String ACTION_MANAGE_ROOT = "android.provider.action.MANAGE_ROOT";
- /** {@hide} */
public static final String ACTION_MANAGE_DOCUMENT = "android.provider.action.MANAGE_DOCUMENT";
/** {@hide} */
@@ -383,18 +381,6 @@
*/
public static final int FLAG_ARCHIVE = 1 << 15;
- /**
- * Flag indicating that document titles should be hidden when viewing
- * this directory in a larger format grid. For example, a directory
- * containing only images may want the image thumbnails to speak for
- * themselves. Only valid when {@link #COLUMN_MIME_TYPE} is
- * {@link #MIME_TYPE_DIR}.
- *
- * @see #COLUMN_FLAGS
- * @see #FLAG_DIR_PREFERS_GRID
- * @hide
- */
- public static final int FLAG_DIR_HIDE_GRID_TITLES = 1 << 16;
}
/**
@@ -478,7 +464,6 @@
/**
* Capacity of a root in bytes. This column is optional, and may be
* {@code null} if unknown or unbounded.
- * {@hide}
* <p>
* Type: INTEGER (long)
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f13f242..d4ff766 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1241,6 +1241,32 @@
public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS
= "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
+ /**
+ * Activity Action: Show a dialog for remote bugreport flow.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SHOW_REMOTE_BUGREPORT_DIALOG
+ = "android.settings.SHOW_REMOTE_BUGREPORT_DIALOG";
+
+ /**
+ * Activity Action: Show VR listener settings.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ *
+ * @see android.service.vr.VrListenerService
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_VR_LISTENER_SETTINGS
+ = "android.settings.VR_LISTENER_SETTINGS";
+
// End of Intent actions for Settings
/**
@@ -5841,15 +5867,6 @@
public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
/**
- * Names of the service component that the current user has explicitly allowed to
- * see and change the importance of all of the user's notifications.
- *
- * @hide
- */
- public static final String ENABLED_NOTIFICATION_ASSISTANT
- = "enabled_notification_assistant";
-
- /**
* Names of the service components that the current user has explicitly allowed to
* see all of the user's notifications, separated by ':'.
*
@@ -6028,6 +6045,14 @@
public static final String BRIGHTNESS_USE_TWILIGHT = "brightness_use_twilight";
/**
+ * Names of the service components that the current user has explicitly allowed to
+ * be a VR mode listener, separated by ':'.
+ *
+ * @hide
+ */
+ public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -6054,6 +6079,7 @@
BACKUP_AUTO_RESTORE,
ENABLED_ACCESSIBILITY_SERVICES,
ENABLED_NOTIFICATION_LISTENERS,
+ ENABLED_VR_LISTENERS,
ENABLED_INPUT_METHODS,
TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
TOUCH_EXPLORATION_ENABLED,
@@ -7760,6 +7786,31 @@
public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants";
/**
+ * ShortcutManager specific settings.
+ * This is encoded as a key=value list, separated by commas. Ex:
+ *
+ * "reset_interval_sec=86400,max_daily_updates=5"
+ *
+ * The following keys are supported:
+ *
+ * <pre>
+ * reset_interval_sec (long)
+ * max_daily_updates (int)
+ * max_icon_dimension_dp (int, DP)
+ * max_icon_dimension_dp_lowram (int, DP)
+ * max_shortcuts (int)
+ * icon_quality (int, 0-100)
+ * icon_format (String)
+ * </pre>
+ *
+ * <p>
+ * Type: string
+ * @hide
+ * @see com.android.server.pm.ShortcutService.ConfigConstants
+ */
+ public static final String SHORTCUT_MANAGER_CONSTANTS = "shortcut_manager_constants";
+
+ /**
* Get the key that retrieves a bluetooth headset's priority.
* @hide
*/
@@ -8247,6 +8298,13 @@
"allow_user_switching_when_system_user_locked";
/**
+ * Boot count since the device starts running APK level 24.
+ * <p>
+ * Type: int
+ */
+ public static final String BOOT_COUNT = "boot_count";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
index 2a8773c..d57d0f5 100644
--- a/core/java/android/security/net/config/XmlConfigSource.java
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -339,7 +339,7 @@
}
if (mDebugBuild) {
debugConfigBuilder =
- parseConfigEntry(parser, seenDomains, null, CONFIG_DEBUG).get(0).first;
+ parseConfigEntry(parser, null, null, CONFIG_DEBUG).get(0).first;
} else {
XmlUtils.skipCurrentTag(parser);
}
@@ -348,6 +348,11 @@
XmlUtils.skipCurrentTag(parser);
}
}
+ // If debug is true and there was no debug-overrides in the file check for an extra
+ // _debug resource.
+ if (mDebugBuild && debugConfigBuilder == null) {
+ debugConfigBuilder = parseDebugOverridesResource();
+ }
// Use the platform default as the parent of the base config for any values not provided
// there. If there is no base config use the platform default.
@@ -385,6 +390,43 @@
mDomainMap = configs;
}
+ private NetworkSecurityConfig.Builder parseDebugOverridesResource()
+ throws IOException, XmlPullParserException, ParserException {
+ Resources resources = mContext.getResources();
+ String packageName = resources.getResourcePackageName(mResourceId);
+ String entryName = resources.getResourceEntryName(mResourceId);
+ int resId = resources.getIdentifier(entryName + "_debug", "xml", packageName);
+ // No debug-overrides resource was found, nothing to parse.
+ if (resId == 0) {
+ return null;
+ }
+ NetworkSecurityConfig.Builder debugConfigBuilder = null;
+ // Parse debug-overrides out of the _debug resource.
+ try (XmlResourceParser parser = resources.getXml(resId)) {
+ XmlUtils.beginDocument(parser, "network-security-config");
+ int outerDepth = parser.getDepth();
+ boolean seenDebugOverrides = false;
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if ("debug-overrides".equals(parser.getName())) {
+ if (seenDebugOverrides) {
+ throw new ParserException(parser, "Only one debug-overrides allowed");
+ }
+ if (mDebugBuild) {
+ debugConfigBuilder =
+ parseConfigEntry(parser, null, null, CONFIG_DEBUG).get(0).first;
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ seenDebugOverrides = true;
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ return debugConfigBuilder;
+ }
+
public static class ParserException extends Exception {
public ParserException(XmlPullParser parser, String message, Throwable cause) {
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 11737c6..0163b47 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -16,32 +16,56 @@
package android.service.notification;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
+import android.app.AutomaticZenRule;
import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
- * Condition information from condition providers. Used to tell the system to enter Do Not Disturb
- * mode and request that the system exit Do Not Disturb mode.
+ * The current condition of an {@link android.app.AutomaticZenRule}, provided by the
+ * {@link ConditionProviderService} that owns the rule. Used to tell the system to enter Do Not
+ * Disturb mode and request that the system exit Do Not Disturb mode.
*/
public class Condition implements Parcelable {
+ @SystemApi
public static final String SCHEME = "condition";
+ /** @hide */
+ @IntDef({STATE_FALSE, STATE_TRUE, STATE_TRUE, STATE_ERROR})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
+ /**
+ * Indicates that Do Not Disturb should be turned off. Note that all Conditions from all
+ * {@link ConditionProviderService} providers must be off for Do Not Disturb to be turned off on
+ * the device.
+ */
public static final int STATE_FALSE = 0;
+ /**
+ * Indicates that Do Not Disturb should be turned on.
+ */
public static final int STATE_TRUE = 1;
+
+ @SystemApi
public static final int STATE_UNKNOWN = 2;
+ @SystemApi
public static final int STATE_ERROR = 3;
+ @SystemApi
public static final int FLAG_RELEVANT_NOW = 1 << 0;
+ @SystemApi
public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
/**
- * The URI representing the condition being updated.
+ * The URI representing the rule being updated.
* See {@link android.app.AutomaticZenRule#getConditionId()}.
*/
public final Uri id;
@@ -52,23 +76,17 @@
*/
public final String summary;
- /**
- * Additional information about what the rule encoded in {@link #id} means when it is enabled.
- * User visible if the state of the condition is {@link #STATE_TRUE}.
- */
+ @SystemApi
public final String line1;
-
- /**
- * Additional information about what the rule encoded in {@link #id} means when it is enabled.
- * User visible if the state of the condition is {@link #STATE_TRUE}.
- */
+ @SystemApi
public final String line2;
/**
- * The state of this condition. {@link #STATE_TRUE} will enable Do Not Disturb mode. Any other
- * state will turn Do Not Disturb off for this rule. Note that Do Not Disturb might still be
- * enabled globally if other conditions are in a {@link #STATE_TRUE} state.
+ * The state of this condition. {@link #STATE_TRUE} will enable Do Not Disturb mode.
+ * {@link #STATE_FALSE} will turn Do Not Disturb off for this rule. Note that Do Not Disturb
+ * might still be enabled globally if other conditions are in a {@link #STATE_TRUE} state.
*/
+ @State
public final int state;
@SystemApi
@@ -76,8 +94,13 @@
@SystemApi
public final int icon;
- public Condition(Uri id, String summary, String line1, String line2, int state) {
- this(id, summary, line1, line2, -1, state, FLAG_RELEVANT_ALWAYS);
+ /**
+ * An object representing the current state of a {@link android.app.AutomaticZenRule}.
+ * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
+ * @param summary a user visible description of the rule state.
+ */
+ public Condition(Uri id, String summary, int state) {
+ this(id, summary, "", "", -1, state, FLAG_RELEVANT_ALWAYS);
}
@SystemApi
@@ -85,8 +108,6 @@
int state, int flags) {
if (id == null) throw new IllegalArgumentException("id is required");
if (summary == null) throw new IllegalArgumentException("summary is required");
- if (line1 == null) throw new IllegalArgumentException("line1 is required");
- if (line2 == null) throw new IllegalArgumentException("line2 is required");
if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
this.id = id;
this.summary = summary;
@@ -97,7 +118,7 @@
this.flags = flags;
}
- private Condition(Parcel source) {
+ public Condition(Parcel source) {
this((Uri)source.readParcelable(Condition.class.getClassLoader()),
source.readString(),
source.readString(),
@@ -135,6 +156,7 @@
.append(']').toString();
}
+ @SystemApi
public static String stateToString(int state) {
if (state == STATE_FALSE) return "STATE_FALSE";
if (state == STATE_TRUE) return "STATE_TRUE";
@@ -143,6 +165,7 @@
throw new IllegalArgumentException("state is invalid: " + state);
}
+ @SystemApi
public static String relevanceToString(int flags) {
final boolean now = (flags & FLAG_RELEVANT_NOW) != 0;
final boolean always = (flags & FLAG_RELEVANT_ALWAYS) != 0;
@@ -175,6 +198,7 @@
return 0;
}
+ @SystemApi
public Condition copy() {
final Parcel parcel = Parcel.obtain();
try {
@@ -186,10 +210,14 @@
}
}
+ @SystemApi
public static Uri.Builder newId(Context context) {
- return new Uri.Builder().scheme(SCHEME).authority(context.getPackageName());
+ return new Uri.Builder()
+ .scheme(Condition.SCHEME)
+ .authority(context.getPackageName());
}
+ @SystemApi
public static boolean isValidId(Uri id, String pkg) {
return id != null && SCHEME.equals(id.getScheme()) && pkg.equals(id.getAuthority());
}
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index adcc9d6..44c3887e 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -102,9 +102,6 @@
*/
abstract public void onConnected();
- /**
- * @removed
- */
@SystemApi
public void onRequestConditions(int relevance) {}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index a0de17f..8c35901 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -23,7 +23,7 @@
/** @hide */
oneway interface INotificationListener
{
- // listeners and assistants
+ // listeners and rankers
void onListenerConnected(in NotificationRankingUpdate update);
void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update);
@@ -33,7 +33,7 @@
void onListenerHintsChanged(int hints);
void onInterruptionFilterChanged(int interruptionFilter);
- // assistants only
+ // rankers only
void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
void onNotificationVisibilityChanged(String key, long time, boolean visible);
void onNotificationClick(String key, long time);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 73a890f..afdd1d4 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -69,16 +69,6 @@
* <action android:name="android.service.notification.NotificationListenerService" />
* </intent-filter>
* </service></pre>
- * <p> Typically, while enabled in user settings, this service will be bound on boot or when a
- * settings change occurs that could affect whether this service should run. However, for some
- * system usage modes, the you may instead specify that this service is instead bound and unbound
- * in response to mode changes by including a category in the intent filter. Currently
- * supported categories are:
- * <ul>
- * <li>{@link #CATEGORY_VR_NOTIFICATIONS} - this service is bound when an Activity has enabled
- * VR mode. {@see android.app.Activity#setVrMode(boolean)}.</li>
- * </ul>
- * </p>
*/
public abstract class NotificationListenerService extends Service {
// TAG = "NotificationListenerService[MySubclass]"
@@ -180,13 +170,18 @@
private INotificationManager mNoMan;
- /** Only valid after a successful call to (@link registerAsService}. */
- private int mCurrentUser;
+ /**
+ * Only valid after a successful call to (@link registerAsService}.
+ * @hide
+ */
+ protected int mCurrentUser;
-
- // This context is required for system services since NotificationListenerService isn't
- // started as a real Service and hence no context is available.
- private Context mSystemContext;
+ /**
+ * This context is required for system services since NotificationListenerService isn't
+ * started as a real Service and hence no context is available..
+ * @hide
+ */
+ protected Context mSystemContext;
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -195,17 +190,6 @@
public static final String SERVICE_INTERFACE
= "android.service.notification.NotificationListenerService";
- /**
- * If this category is declared in the application manifest for a service of this type, this
- * service will be bound when VR mode is enabled, and unbound when VR mode is disabled rather
- * than the normal lifecycle for a notification service.
- *
- * {@see android.app.Activity#setVrMode(boolean)}
- */
- @SdkConstant(SdkConstant.SdkConstantType.INTENT_CATEGORY)
- public static final String CATEGORY_VR_NOTIFICATIONS =
- "android.intent.category.vr.notifications";
-
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
@@ -696,10 +680,10 @@
@SystemApi
public void registerAsSystemService(Context context, ComponentName componentName,
int currentUser) throws RemoteException {
- mSystemContext = context;
if (mWrapper == null) {
mWrapper = new NotificationListenerWrapper();
}
+ mSystemContext = context;
INotificationManager noMan = getNotificationInterface();
noMan.registerListener(mWrapper, componentName, currentUser);
mCurrentUser = currentUser;
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationRankerService.java
similarity index 89%
rename from core/java/android/service/notification/NotificationAssistantService.java
rename to core/java/android/service/notification/NotificationRankerService.java
index 41af837..47fdac6 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationRankerService.java
@@ -31,31 +31,20 @@
import com.android.internal.os.SomeArgs;
/**
- * A service that helps the user manage notifications by modifying the
- * relative importance of notifications.
- * <p>To extend this class, you must declare the service in your manifest file with
- * the {@link android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE} permission
- * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
- * <pre>
- * <service android:name=".NotificationAssistant"
- * android:label="@string/service_name"
- * android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE">
- * <intent-filter>
- * <action android:name="android.service.notification.NotificationAssistantService" />
- * </intent-filter>
- * </service></pre>
+ * A service that helps the user manage notifications. This class is only used to
+ * extend the framework service and may not be implemented by non-framework components.
* @hide
*/
@SystemApi
-public abstract class NotificationAssistantService extends NotificationListenerService {
- private static final String TAG = "NotificationAssistant";
+public abstract class NotificationRankerService extends NotificationListenerService {
+ private static final String TAG = "NotificationRankers";
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE
- = "android.service.notification.NotificationAssistantService";
+ = "android.service.notification.NotificationRankerService";
/** Notification was canceled by the status bar reporting a click. */
public static final int REASON_DELEGATE_CLICK = 1;
@@ -129,9 +118,14 @@
/** @hide */
@Override
public void registerAsSystemService(Context context, ComponentName componentName,
- int currentUser) throws RemoteException {
- super.registerAsSystemService(context, componentName, currentUser);
- mHandler = new MyHandler(getContext().getMainLooper());
+ int currentUser) {
+ throw new UnsupportedOperationException("the ranker lifecycle is managed by the system.");
+ }
+
+ /** @hide */
+ @Override
+ public void unregisterAsSystemService() {
+ throw new UnsupportedOperationException("the ranker lifecycle is managed by the system.");
}
@Override
@@ -143,7 +137,7 @@
@Override
public final IBinder onBind(Intent intent) {
if (mWrapper == null) {
- mWrapper = new NotificationAssistantWrapper();
+ mWrapper = new NotificationRankingServiceWrapper();
}
return mWrapper;
}
@@ -216,14 +210,14 @@
public final void adjustImportance(String key, Adjustment adjustment) {
if (!isBound()) return;
try {
- getNotificationInterface().setImportanceFromAssistant(mWrapper, key,
+ getNotificationInterface().setImportanceFromRankerService(mWrapper, key,
adjustment.mImportance, adjustment.mExplanation);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
}
- private class NotificationAssistantWrapper extends NotificationListenerWrapper {
+ private class NotificationRankingServiceWrapper extends NotificationListenerWrapper {
@Override
public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
int importance, boolean user) {
diff --git a/core/java/android/service/vr/IVrListener.aidl b/core/java/android/service/vr/IVrListener.aidl
new file mode 100644
index 0000000..b7273ba
--- /dev/null
+++ b/core/java/android/service/vr/IVrListener.aidl
@@ -0,0 +1,22 @@
+/**
+ * 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.service.vr;
+
+/** @hide */
+oneway interface IVrListener {
+
+}
\ No newline at end of file
diff --git a/core/java/android/service/vr/VrListenerService.java b/core/java/android/service/vr/VrListenerService.java
new file mode 100644
index 0000000..5f1f659
--- /dev/null
+++ b/core/java/android/service/vr/VrListenerService.java
@@ -0,0 +1,88 @@
+/**
+ * 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.service.vr;
+
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.app.ActivityManager;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * A service that is bound from the system while running in virtual reality (VR) mode.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with
+ * the {@link android.Manifest.permission#BIND_VR_LISTENER_SERVICE} permission
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
+ * <pre>
+ * <service android:name=".VrListener"
+ * android:label="@string/service_name"
+ * android:permission="android.permission.BIND_VR_LISTENER_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.vr.VrListenerService" />
+ * </intent-filter>
+ * </service>
+ * </pre>
+ *
+ * <p>
+ * This service is bound when the system enters VR mode and is unbound when the system leaves VR
+ * mode.
+ * {@see android.app.Activity#setVrMode(boolean)}
+ * </p>
+ */
+public abstract class VrListenerService extends Service {
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
+
+ /**
+ * @hide
+ */
+ public static class VrListenerBinder extends IVrListener.Stub {
+ }
+
+ private final VrListenerBinder mBinder = new VrListenerBinder();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ /**
+ * Check if the given package is available to be enabled/disabled in VR mode settings.
+ *
+ * @param context the {@link Context} to use for looking up the requested component.
+ * @param requestedComponent the name of the component that implements
+ * {@link android.service.vr.VrListenerService} to check.
+ *
+ * @return {@code true} if this package is enabled in settings.
+ */
+ public static final boolean isVrModePackageEnabled(@NonNull Context context,
+ @NonNull ComponentName requestedComponent) {
+ ActivityManager am = context.getSystemService(ActivityManager.class);
+ if (am == null) {
+ return false;
+ }
+ return am.isVrModePackageEnabled(requestedComponent);
+ }
+}
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 568b267..085613f 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -185,7 +185,6 @@
"de-1901", "de-1996", "de-CH-1901",
"en-US",
"es",
- "eu",
"hu",
"hy",
"nb",
diff --git a/core/java/android/transition/ArcMotion.java b/core/java/android/transition/ArcMotion.java
index fa4c8d2..70443ba 100644
--- a/core/java/android/transition/ArcMotion.java
+++ b/core/java/android/transition/ArcMotion.java
@@ -15,13 +15,13 @@
*/
package android.transition;
-import com.android.internal.R;
-
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Path;
import android.util.AttributeSet;
+import com.android.internal.R;
+
/**
* A PathMotion that generates a curved path along an arc on an imaginary circle containing
* the two points. If the horizontal distance between the points is less than the vertical
@@ -207,7 +207,7 @@
ey = (startY + endY) / 2;
} else {
float deltaX = endX - startX;
- float deltaY = startY - endY; // Y is inverted compared to diagram above.
+ float deltaY = endY - startY;
// hypotenuse squared.
float h2 = deltaX * deltaX + deltaY * deltaY;
@@ -219,24 +219,35 @@
float midDist2 = h2 * 0.25f;
float minimumArcDist2 = 0;
+ boolean isQuadrant1Or3 = (deltaX * deltaY) > 0;
- if (Math.abs(deltaX) < Math.abs(deltaY)) {
+ if ((Math.abs(deltaX) < Math.abs(deltaY))) {
// Similar triangles bfa and bde mean that (ab/fb = eb/bd)
// Therefore, eb = ab * bd / fb
// ab = hypotenuse
// bd = hypotenuse/2
// fb = deltaY
float eDistY = h2 / (2 * deltaY);
- ey = endY + eDistY;
- ex = endX;
+ if (isQuadrant1Or3) {
+ ey = startY + eDistY;
+ ex = startX;
+ } else {
+ ey = endY - eDistY;
+ ex = endX;
+ }
minimumArcDist2 = midDist2 * mMinimumVerticalTangent
* mMinimumVerticalTangent;
} else {
// Same as above, but flip X & Y
float eDistX = h2 / (2 * deltaX);
- ex = endX + eDistX;
- ey = endY;
+ if (isQuadrant1Or3) {
+ ex = endX - eDistX;
+ ey = endY;
+ } else {
+ ex = startX + eDistX;
+ ey = startY;
+ }
minimumArcDist2 = midDist2 * mMinimumHorizontalTangent
* mMinimumHorizontalTangent;
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index 9af65e4..2645f86 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -47,6 +47,7 @@
private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
private CalculateSlide mSlideCalculator = sCalculateBottom;
private @GravityFlag int mSlideEdge = Gravity.BOTTOM;
+ private float mSlideFraction = 1;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -56,16 +57,16 @@
private interface CalculateSlide {
/** Returns the translation value for view when it goes out of the scene */
- float getGoneX(ViewGroup sceneRoot, View view);
+ float getGoneX(ViewGroup sceneRoot, View view, float fraction);
/** Returns the translation value for view when it goes out of the scene */
- float getGoneY(ViewGroup sceneRoot, View view);
+ float getGoneY(ViewGroup sceneRoot, View view, float fraction);
}
private static abstract class CalculateSlideHorizontal implements CalculateSlide {
@Override
- public float getGoneY(ViewGroup sceneRoot, View view) {
+ public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
return view.getTranslationY();
}
}
@@ -73,27 +74,27 @@
private static abstract class CalculateSlideVertical implements CalculateSlide {
@Override
- public float getGoneX(ViewGroup sceneRoot, View view) {
+ public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
return view.getTranslationX();
}
}
private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
@Override
- public float getGoneX(ViewGroup sceneRoot, View view) {
- return view.getTranslationX() - sceneRoot.getWidth();
+ public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
+ return view.getTranslationX() - sceneRoot.getWidth() * fraction;
}
};
private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
@Override
- public float getGoneX(ViewGroup sceneRoot, View view) {
+ public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
final float x;
if (isRtl) {
- x = view.getTranslationX() + sceneRoot.getWidth();
+ x = view.getTranslationX() + sceneRoot.getWidth() * fraction;
} else {
- x = view.getTranslationX() - sceneRoot.getWidth();
+ x = view.getTranslationX() - sceneRoot.getWidth() * fraction;
}
return x;
}
@@ -101,27 +102,27 @@
private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
@Override
- public float getGoneY(ViewGroup sceneRoot, View view) {
- return view.getTranslationY() - sceneRoot.getHeight();
+ public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
+ return view.getTranslationY() - sceneRoot.getHeight() * fraction;
}
};
private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
@Override
- public float getGoneX(ViewGroup sceneRoot, View view) {
- return view.getTranslationX() + sceneRoot.getWidth();
+ public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
+ return view.getTranslationX() + sceneRoot.getWidth() * fraction;
}
};
private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
@Override
- public float getGoneX(ViewGroup sceneRoot, View view) {
+ public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
final float x;
if (isRtl) {
- x = view.getTranslationX() - sceneRoot.getWidth();
+ x = view.getTranslationX() - sceneRoot.getWidth() * fraction;
} else {
- x = view.getTranslationX() + sceneRoot.getWidth();
+ x = view.getTranslationX() + sceneRoot.getWidth() * fraction;
}
return x;
}
@@ -129,8 +130,8 @@
private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
@Override
- public float getGoneY(ViewGroup sceneRoot, View view) {
- return view.getTranslationY() + sceneRoot.getHeight();
+ public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
+ return view.getTranslationY() + sceneRoot.getHeight() * fraction;
}
};
@@ -237,8 +238,8 @@
int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
float endX = view.getTranslationX();
float endY = view.getTranslationY();
- float startX = mSlideCalculator.getGoneX(sceneRoot, view);
- float startY = mSlideCalculator.getGoneY(sceneRoot, view);
+ float startX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction);
+ float startY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction);
return TranslationAnimationCreator
.createAnimation(view, endValues, position[0], position[1],
startX, startY, endX, endY, sDecelerate, this);
@@ -253,10 +254,15 @@
int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
float startX = view.getTranslationX();
float startY = view.getTranslationY();
- float endX = mSlideCalculator.getGoneX(sceneRoot, view);
- float endY = mSlideCalculator.getGoneY(sceneRoot, view);
+ float endX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction);
+ float endY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction);
return TranslationAnimationCreator
.createAnimation(view, startValues, position[0], position[1],
startX, startY, endX, endY, sAccelerate, this);
}
+
+ /** @hide */
+ public void setSlideFraction(float slideFraction) {
+ mSlideFraction = slideFraction;
+ }
}
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 4afa9fe..316c7e3 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -41,12 +41,12 @@
import android.widget.ListView;
import android.widget.Spinner;
+import com.android.internal.R;
+
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
-import com.android.internal.R;
-
/**
* A Transition holds information about animations that will be run on its
* targets during a scene change. Subclasses of this abstract class may
@@ -192,7 +192,7 @@
private TransitionValuesMaps mStartValues = new TransitionValuesMaps();
private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
TransitionSet mParent = null;
- private int[] mMatchOrder = DEFAULT_MATCH_ORDER;
+ int[] mMatchOrder = DEFAULT_MATCH_ORDER;
ArrayList<TransitionValues> mStartValuesList; // only valid after playTransition starts
ArrayList<TransitionValues> mEndValuesList; // only valid after playTransitions starts
@@ -246,7 +246,7 @@
// The function used to interpolate along two-dimensional points. Typically used
// for adding curves to x/y View motion.
- private PathMotion mPathMotion = STRAIGHT_PATH_MOTION;
+ PathMotion mPathMotion = STRAIGHT_PATH_MOTION;
/**
* Constructs a Transition object with no target objects. A transition with
@@ -780,7 +780,7 @@
}
}
}
- if (minStartDelay != 0) {
+ if (startDelays.size() != 0) {
for (int i = 0; i < startDelays.size(); i++) {
int index = startDelays.keyAt(i);
Animator animator = mAnimators.get(index);
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index a747f16..d201adef 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -154,11 +154,11 @@
public static final int DENSITY_DEVICE_STABLE = getDeviceDensity();
/**
- * The absolute width of the display in pixels.
+ * The absolute width of the available display size in pixels.
*/
public int widthPixels;
/**
- * The absolute height of the display in pixels.
+ * The absolute height of the available display size in pixels.
*/
public int heightPixels;
/**
diff --git a/core/java/android/util/LocaleList.java b/core/java/android/util/LocaleList.java
index fc39004..fa3921c 100644
--- a/core/java/android/util/LocaleList.java
+++ b/core/java/android/util/LocaleList.java
@@ -478,8 +478,6 @@
/**
* Returns the default locale list, adjusted by moving the default locale to its first
* position.
- *
- * {@hide}
*/
@NonNull @Size(min=1)
public static LocaleList getAdjustedDefault() {
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index 98aaa81..bd00aba 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -17,6 +17,7 @@
package android.util;
import android.annotation.AnyRes;
+import android.content.pm.ActivityInfo.Config;
/**
* Container for a dynamically typed data value. Primarily used with
@@ -183,9 +184,11 @@
@AnyRes
public int resourceId;
- /** If Value came from a resource, these are the configurations for which
- * its contents can change. */
- public int changingConfigurations = -1;
+ /**
+ * If the value came from a resource, these are the configurations for
+ * which its contents can change.
+ */
+ public @Config int changingConfigurations = -1;
/**
* If the Value came from a resource, this holds the corresponding pixel density.
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 728f723..dcf987b 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -75,6 +75,47 @@
public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 2;
/**
+ * Returns {@code true} if the provided APK contains an APK Signature Scheme V2
+ * signature. The signature will not be verified.
+ */
+ public static boolean hasSignature(String apkFile) throws IOException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
+ long fileSize = apk.length();
+ if (fileSize > Integer.MAX_VALUE) {
+ return false;
+ }
+ MappedByteBuffer apkContents;
+ try {
+ apkContents = apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
+ } catch (IOException e) {
+ if (e.getCause() instanceof OutOfMemoryError) {
+ // TODO: Remove this temporary workaround once verifying large APKs is
+ // supported. Very large APKs cannot be memory-mapped. This verification code
+ // needs to change to use a different approach for verifying such APKs.
+ return false; // Pretend that this APK does not have a v2 signature.
+ } else {
+ throw new IOException("Failed to memory-map APK", e);
+ }
+ }
+ // ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order.
+ apkContents.order(ByteOrder.LITTLE_ENDIAN);
+
+ final int centralDirOffset =
+ (int) getCentralDirOffset(apkContents, getEocdOffset(apkContents));
+ // Find the APK Signing Block.
+ int apkSigningBlockOffset = findApkSigningBlock(apkContents, centralDirOffset);
+ ByteBuffer apkSigningBlock =
+ sliceFromTo(apkContents, apkSigningBlockOffset, centralDirOffset);
+
+ // Find the APK Signature Scheme v2 Block inside the APK Signing Block.
+ findApkSignatureSchemeV2Block(apkSigningBlock);
+ return true;
+ } catch (SignatureNotFoundException e) {
+ }
+ return false;
+ }
+
+ /**
* Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
* associated with each signer.
*
@@ -104,11 +145,26 @@
if (fileSize > Integer.MAX_VALUE) {
throw new IOException("File too large: " + apk.length() + " bytes");
}
- MappedByteBuffer apkContents =
- apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
- // Attempt to preload the contents into memory for faster overall verification (v2 and
- // older) at the expense of somewhat increased latency for rejecting malformed APKs.
- apkContents.load();
+ MappedByteBuffer apkContents;
+ try {
+ apkContents = apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
+ // Attempt to preload the contents into memory for faster overall verification (v2 and
+ // older) at the expense of somewhat increased latency for rejecting malformed APKs.
+ apkContents.load();
+ } catch (IOException e) {
+ if (e.getCause() instanceof OutOfMemoryError) {
+ // TODO: Remove this temporary workaround once verifying large APKs is supported.
+ // Very large APKs cannot be memory-mapped. This verification code needs to change
+ // to use a different approach for verifying such APKs.
+ // This workaround pretends that this APK does not have a v2 signature. This works
+ // fine provided the APK is not actually v2-signed. If the APK is v2 signed, v2
+ // signature stripping protection inside v1 signature verification code will reject
+ // this APK.
+ throw new SignatureNotFoundException("Failed to memory-map APK", e);
+ } else {
+ throw new IOException("Failed to memory-map APK", e);
+ }
+ }
return verify(apkContents);
}
@@ -130,31 +186,8 @@
// ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order.
apkContents.order(ByteOrder.LITTLE_ENDIAN);
- // Find the offset of ZIP End of Central Directory (EoCD)
- int eocdOffset = ZipUtils.findZipEndOfCentralDirectoryRecord(apkContents);
- if (eocdOffset == -1) {
- throw new SignatureNotFoundException(
- "Not an APK file: ZIP End of Central Directory record not found");
- }
- if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apkContents, eocdOffset)) {
- throw new SignatureNotFoundException("ZIP64 APK not supported");
- }
- ByteBuffer eocd = sliceFromTo(apkContents, eocdOffset, apkContents.capacity());
-
- // Look up the offset of ZIP Central Directory.
- long centralDirOffsetLong = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
- if (centralDirOffsetLong >= eocdOffset) {
- throw new SignatureNotFoundException(
- "ZIP Central Directory offset out of range: " + centralDirOffsetLong
- + ". ZIP End of Central Directory offset: " + eocdOffset);
- }
- long centralDirSizeLong = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd);
- if (centralDirOffsetLong + centralDirSizeLong != eocdOffset) {
- throw new SignatureNotFoundException(
- "ZIP Central Directory is not immediately followed by End of Central"
- + " Directory");
- }
- int centralDirOffset = (int) centralDirOffsetLong;
+ final int eocdOffset = getEocdOffset(apkContents);
+ final int centralDirOffset = (int) getCentralDirOffset(apkContents, eocdOffset);
// Find the APK Signing Block.
int apkSigningBlockOffset = findApkSigningBlock(apkContents, centralDirOffset);
@@ -499,6 +532,43 @@
return result;
}
+ /**
+ * Finds the offset of ZIP End of Central Directory (EoCD).
+ *
+ * @throws SignatureNotFoundException If the EoCD could not be found
+ */
+ private static int getEocdOffset(ByteBuffer apkContents) throws SignatureNotFoundException {
+ int eocdOffset = ZipUtils.findZipEndOfCentralDirectoryRecord(apkContents);
+ if (eocdOffset == -1) {
+ throw new SignatureNotFoundException(
+ "Not an APK file: ZIP End of Central Directory record not found");
+ }
+ return eocdOffset;
+ }
+
+ private static long getCentralDirOffset(ByteBuffer apkContents, int eocdOffset)
+ throws SignatureNotFoundException {
+ if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apkContents, eocdOffset)) {
+ throw new SignatureNotFoundException("ZIP64 APK not supported");
+ }
+ ByteBuffer eocd = sliceFromTo(apkContents, eocdOffset, apkContents.capacity());
+
+ // Look up the offset of ZIP Central Directory.
+ long centralDirOffsetLong = ZipUtils.getZipEocdCentralDirectoryOffset(eocd);
+ if (centralDirOffsetLong >= eocdOffset) {
+ throw new SignatureNotFoundException(
+ "ZIP Central Directory offset out of range: " + centralDirOffsetLong
+ + ". ZIP End of Central Directory offset: " + eocdOffset);
+ }
+ long centralDirSizeLong = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocd);
+ if (centralDirOffsetLong + centralDirSizeLong != eocdOffset) {
+ throw new SignatureNotFoundException(
+ "ZIP Central Directory is not immediately followed by End of Central"
+ + " Directory");
+ }
+ return centralDirOffsetLong;
+ }
+
private static final int getChunkCount(int inputSizeBytes) {
return (inputSizeBytes + CHUNK_SIZE_BYTES - 1) / CHUNK_SIZE_BYTES;
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 16bc19d..22d5ed8 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -773,14 +773,23 @@
/**
* Gets display metrics that describe the size and density of this display.
- * <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 decor elements that are always visible.
- * It may also be scaled to provide compatibility with older applications that
+ * actual raw size (native resolution) of the display.
+ * <p>
+ * 1. The returned size may be adjusted to exclude certain system decor elements
+ * that are always visible.
+ * </p><p>
+ * 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.
+ * <pre>
+ * - 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>
* </p>
*
* @param outMetrics A {@link DisplayMetrics} object to receive the metrics.
@@ -818,7 +827,7 @@
* The size is adjusted based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
- * window manager is emulating a smaller display (using adb shell am display-size).
+ * window manager is emulating a smaller display (using adb shell wm size).
* </p>
*
* @param outMetrics A {@link DisplayMetrics} object to receive the metrics.
@@ -827,8 +836,7 @@
synchronized (this) {
updateDisplayInfoLocked();
mDisplayInfo.getLogicalMetrics(outMetrics,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
- mDisplayAdjustments.getConfiguration());
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
}
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9543acf..d8b7421 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -167,7 +167,7 @@
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);
+ void notifyAppStopped(IBinder token, boolean stopped);
void startAppFreezingScreen(IBinder token, int configChanges);
void stopAppFreezingScreen(IBinder token, boolean force);
void removeAppToken(IBinder token);
@@ -193,8 +193,7 @@
boolean isKeyguardSecure();
boolean inKeyguardRestrictedInputMode();
void dismissKeyguard();
- void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade);
+ void keyguardGoingAway(int flags);
void closeSystemDialogs(String reason);
@@ -363,6 +362,12 @@
void setDockedStackResizing(boolean resizing);
/**
+ * Sets the region the user can touch the divider. This region will be excluded from the region
+ * which is used to cause a focus switch when dispatching touch.
+ */
+ void setDockedStackDividerTouchRegion(in Rect touchableRegion);
+
+ /**
* Registers a listener that will be called when the dock divider changes its visibility or when
* the docked stack gets added/removed.
*/
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index b9a7421..cff9d8e 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -35,7 +35,6 @@
public class NotificationHeaderView extends ViewGroup {
public static final int NO_COLOR = -1;
private final int mChildMinWidth;
- private final int mExpandTopPadding;
private final int mContentEndMargin;
private View mAppName;
private View mSubTextView;
@@ -43,12 +42,10 @@
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
private ImageView mExpandButton;
private View mIcon;
- private TextView mChildCount;
private View mProfileBadge;
private View mInfo;
private int mIconColor;
private int mOriginalNotificationColor;
- private boolean mGroupHeader;
private boolean mExpanded;
private boolean mShowWorkBadgeAtEnd;
@@ -70,7 +67,6 @@
com.android.internal.R.dimen.notification_header_shrink_min_width);
mContentEndMargin = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin_end);
- mExpandTopPadding = (int) (1 * getResources().getDisplayMetrics().density);
}
@Override
@@ -80,7 +76,6 @@
mSubTextView = findViewById(com.android.internal.R.id.header_sub_text);
mExpandButton = (ImageView) findViewById(com.android.internal.R.id.expand_button);
mIcon = findViewById(com.android.internal.R.id.icon);
- mChildCount = (TextView) findViewById(com.android.internal.R.id.number_of_children);
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
mInfo = findViewById(com.android.internal.R.id.header_content_info);
}
@@ -193,17 +188,6 @@
updateTouchListener();
}
- public void setChildCount(int childCount) {
- if (childCount > 0) {
- mChildCount.setText(getContext().getString(
- com.android.internal.R.string.notification_children_count_bracketed,
- childCount));
- mChildCount.setVisibility(VISIBLE);
- } else {
- mChildCount.setVisibility(GONE);
- }
- }
-
@RemotableViewMethod
public void setOriginalIconColor(int color) {
mIconColor = color;
@@ -222,11 +206,6 @@
return mOriginalNotificationColor;
}
- public void setIsGroupHeader(boolean isGroupHeader) {
- mGroupHeader = isGroupHeader;
- updateExpandButton();
- }
-
@RemotableViewMethod
public void setExpanded(boolean expanded) {
mExpanded = expanded;
@@ -235,24 +214,13 @@
private void updateExpandButton() {
int drawableId;
- int paddingTop = 0;
- if (mGroupHeader) {
- if (mExpanded) {
- drawableId = com.android.internal.R.drawable.ic_collapse_bundle;
- } else {
- drawableId =com.android.internal.R.drawable.ic_expand_bundle;
- }
+ if (mExpanded) {
+ drawableId = com.android.internal.R.drawable.ic_collapse_notification;
} else {
- if (mExpanded) {
- drawableId = com.android.internal.R.drawable.ic_collapse_notification;
- } else {
- drawableId = com.android.internal.R.drawable.ic_expand_notification;
- }
- paddingTop = mExpandTopPadding;
+ drawableId = com.android.internal.R.drawable.ic_expand_notification;
}
mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
mExpandButton.setColorFilter(mOriginalNotificationColor);
- mExpandButton.setPadding(0, paddingTop, 0, 0);
}
public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9bd3df0..57ab6d40 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17505,7 +17505,7 @@
* {@link SystemClock#uptimeMillis} timebase.
*/
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
if (verifyDrawable(who) && what != null) {
final long delay = when - SystemClock.uptimeMillis();
if (mAttachInfo != null) {
@@ -17527,7 +17527,7 @@
* @param what the action to cancel
*/
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
if (verifyDrawable(who) && what != null) {
if (mAttachInfo != null) {
mAttachInfo.mViewRootImpl.mChoreographer.removeCallbacks(
@@ -17637,7 +17637,7 @@
* @see #drawableStateChanged()
*/
@CallSuper
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
// Avoid verifying the scroll bar drawable so that we don't end up in
// an invalidation loop. This effectively prevents the scroll bar
// drawable from triggering invalidations and scheduling runnables.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c54ce80..56ee478 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1382,6 +1382,13 @@
if (mIsInterestedInDrag) {
retval = true;
}
+
+ if (!retval) {
+ // Neither us nor any of our children are interested in this drag, so stop tracking
+ // the current drag event.
+ mCurrentDragStartEvent.recycle();
+ mCurrentDragStartEvent = null;
+ }
} break;
case DragEvent.ACTION_DRAG_ENDED: {
@@ -5357,9 +5364,6 @@
void offsetRectBetweenParentAndChild(View descendant, Rect rect,
boolean offsetFromChildToParent, boolean clipToBounds) {
- final RectF rectF = mAttachInfo != null ? mAttachInfo.mTmpTransformRect1 : new RectF();
- final Matrix inverse = mAttachInfo != null ? mAttachInfo.mTmpMatrix : new Matrix();
-
// already in the same coord system :)
if (descendant == this) {
return;
@@ -5373,16 +5377,8 @@
&& (theParent != this)) {
if (offsetFromChildToParent) {
- rect.offset(-descendant.mScrollX, -descendant.mScrollY);
-
- if (!descendant.hasIdentityMatrix()) {
- rectF.set(rect);
- descendant.getMatrix().mapRect(rectF);
- rectF.roundOut(rect);
- }
-
- rect.offset(descendant.mLeft, descendant.mTop);
-
+ rect.offset(descendant.mLeft - descendant.mScrollX,
+ descendant.mTop - descendant.mScrollY);
if (clipToBounds) {
View p = (View) theParent;
boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
@@ -5400,16 +5396,8 @@
rect.setEmpty();
}
}
- rect.offset(-descendant.mLeft, -descendant.mTop);
-
- if (!descendant.hasIdentityMatrix()) {
- descendant.getMatrix().invert(inverse);
- rectF.set(rect);
- inverse.mapRect(rectF);
- rectF.roundOut(rect);
- }
-
- rect.offset(descendant.mScrollX, descendant.mScrollY);
+ rect.offset(descendant.mScrollX - descendant.mLeft,
+ descendant.mScrollY - descendant.mTop);
}
descendant = (View) theParent;
@@ -5420,26 +5408,11 @@
// to get into our coordinate space
if (theParent == this) {
if (offsetFromChildToParent) {
- rect.offset(-descendant.mScrollX, -descendant.mScrollY);
-
- if (!descendant.hasIdentityMatrix()) {
- rectF.set(rect);
- descendant.getMatrix().mapRect(rectF);
- rectF.roundOut(rect);
- }
-
- rect.offset(descendant.mLeft, descendant.mTop);
+ rect.offset(descendant.mLeft - descendant.mScrollX,
+ descendant.mTop - descendant.mScrollY);
} else {
- rect.offset(-descendant.mLeft, -descendant.mTop);
-
- if (!descendant.hasIdentityMatrix()) {
- descendant.getMatrix().invert(inverse);
- rectF.set(rect);
- inverse.mapRect(rectF);
- rectF.roundOut(rect);
- }
-
- rect.offset(descendant.mScrollX, descendant.mScrollY);
+ rect.offset(descendant.mScrollX - descendant.mLeft,
+ descendant.mScrollY - descendant.mTop);
}
} else {
throw new IllegalArgumentException("parameter must be a descendant of this view");
diff --git a/core/java/android/view/ViewGroupOverlay.java b/core/java/android/view/ViewGroupOverlay.java
index 16afc5d..c2807ba 100644
--- a/core/java/android/view/ViewGroupOverlay.java
+++ b/core/java/android/view/ViewGroupOverlay.java
@@ -15,6 +15,7 @@
*/
package android.view;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -37,7 +38,7 @@
}
/**
- * Adds a View to the overlay. The bounds of the added view should be
+ * Adds a {@code View} to the overlay. The bounds of the added view should be
* relative to the host view. Any view added to the overlay should be
* removed when it is no longer needed or no longer visible.
*
@@ -54,23 +55,32 @@
* and 200 pixels down from the origin of the overlay's
* host view, then the view will be offset by (100, 200).</p>
*
- * @param view The View to be added to the overlay. The added view will be
+ * <p>{@code View}s added with this API will be drawn in the order they were
+ * added. Drawing of the overlay views will happen before drawing of any of the
+ * {@code Drawable}s added with {@link #add(Drawable)} API even if a call to
+ * this API happened after the call to {@link #add(Drawable)}.</p>
+ *
+ * <p>Passing <code>null</code> parameter will result in an
+ * {@link IllegalArgumentException} being thrown.</p>
+ *
+ * @param view The {@code View} to be added to the overlay. The added view will be
* drawn when the overlay is drawn.
* @see #remove(View)
* @see ViewOverlay#add(Drawable)
*/
- public void add(View view) {
+ public void add(@NonNull View view) {
mOverlayViewGroup.add(view);
}
/**
- * Removes the specified View from the overlay.
+ * Removes the specified {@code View} from the overlay. Passing <code>null</code> parameter
+ * will result in an {@link IllegalArgumentException} being thrown.
*
- * @param view The View to be removed from the overlay.
+ * @param view The {@code View} to be removed from the overlay.
* @see #add(View)
* @see ViewOverlay#remove(Drawable)
*/
- public void remove(View view) {
+ public void remove(@NonNull View view) {
mOverlayViewGroup.remove(view);
}
}
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 5e5ef29..b770bd5 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -16,6 +16,7 @@
package android.view;
import android.animation.LayoutTransition;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -59,25 +60,30 @@
}
/**
- * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
+ * Adds a {@link Drawable} to the overlay. The bounds of the drawable should be relative to
* the host view. Any drawable added to the overlay should be removed when it is no longer
- * needed or no longer visible.
+ * needed or no longer visible. Adding an already existing {@link Drawable}
+ * is a no-op. Passing <code>null</code> parameter will result in an
+ * {@link IllegalArgumentException} being thrown.
*
- * @param drawable The Drawable to be added to the overlay. This drawable will be
- * drawn when the view redraws its overlay.
+ * @param drawable The {@link Drawable} to be added to the overlay. This drawable will be
+ * drawn when the view redraws its overlay. {@link Drawable}s will be drawn in the order that
+ * they were added.
* @see #remove(Drawable)
*/
- public void add(Drawable drawable) {
+ public void add(@NonNull Drawable drawable) {
mOverlayViewGroup.add(drawable);
}
/**
- * Removes the specified Drawable from the overlay.
+ * Removes the specified {@link Drawable} from the overlay. Removing a {@link Drawable} that was
+ * not added with {@link #add(Drawable)} is a no-op. Passing <code>null</code> parameter will
+ * result in an {@link IllegalArgumentException} being thrown.
*
- * @param drawable The Drawable to be removed from the overlay.
+ * @param drawable The {@link Drawable} to be removed from the overlay.
* @see #add(Drawable)
*/
- public void remove(Drawable drawable) {
+ public void remove(@NonNull Drawable drawable) {
mOverlayViewGroup.remove(drawable);
}
@@ -119,7 +125,7 @@
* The View for which this is an overlay. Invalidations of the overlay are redirected to
* this host view.
*/
- View mHostView;
+ final View mHostView;
/**
* The set of drawables to draw when the overlay is rendered.
@@ -137,10 +143,12 @@
mRenderNode.setLeftTopRightBottom(0, 0, mRight, mBottom);
}
- public void add(Drawable drawable) {
+ public void add(@NonNull Drawable drawable) {
+ if (drawable == null) {
+ throw new IllegalArgumentException("drawable must be non-null");
+ }
if (mDrawables == null) {
-
- mDrawables = new ArrayList<Drawable>();
+ mDrawables = new ArrayList<>();
}
if (!mDrawables.contains(drawable)) {
// Make each drawable unique in the overlay; can't add it more than once
@@ -150,7 +158,10 @@
}
}
- public void remove(Drawable drawable) {
+ public void remove(@NonNull Drawable drawable) {
+ if (drawable == null) {
+ throw new IllegalArgumentException("drawable must be non-null");
+ }
if (mDrawables != null) {
mDrawables.remove(drawable);
invalidate(drawable.getBounds());
@@ -159,11 +170,15 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || (mDrawables != null && mDrawables.contains(who));
}
- public void add(View child) {
+ public void add(@NonNull View child) {
+ if (child == null) {
+ throw new IllegalArgumentException("view must be non-null");
+ }
+
if (child.getParent() instanceof ViewGroup) {
ViewGroup parent = (ViewGroup) child.getParent();
if (parent != mHostView && parent.getParent() != null &&
@@ -190,13 +205,20 @@
super.addView(child);
}
- public void remove(View view) {
+ public void remove(@NonNull View view) {
+ if (view == null) {
+ throw new IllegalArgumentException("view must be non-null");
+ }
+
super.removeView(view);
}
public void clear() {
removeAllViews();
if (mDrawables != null) {
+ for (Drawable drawable : mDrawables) {
+ drawable.setCallback(null);
+ }
mDrawables.clear();
}
}
@@ -210,7 +232,7 @@
}
@Override
- public void invalidateDrawable(Drawable drawable) {
+ public void invalidateDrawable(@NonNull Drawable drawable) {
invalidate(drawable.getBounds());
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index afe2f10..f7405e2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1078,8 +1078,7 @@
scheduleTraversals();
} else {
if (mAttachInfo.mHardwareRenderer != null) {
- // TODO: Temporary to help track down b/27286867
- Log.d(mTag, "WindowStopped on " + getTitle());
+ if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle());
mAttachInfo.mHardwareRenderer.updateSurface(null);
mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6e02516..17f1991 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -135,6 +135,18 @@
}
/**
+ * Message for taking fullscreen screenshot
+ * @hide
+ */
+ final int TAKE_SCREENSHOT_FULLSCREEN = 1;
+
+ /**
+ * Message for taking screenshot of selected region.
+ * @hide
+ */
+ final int TAKE_SCREENSHOT_SELECTED_REGION = 2;
+
+ /**
* @hide
*/
public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
@@ -269,6 +281,7 @@
@ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "TYPE_VOICE_INTERACTION_STARTING"),
@ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER, to = "TYPE_DOCK_DIVIDER"),
@ViewDebug.IntToString(from = TYPE_QS_DIALOG, to = "TYPE_QS_DIALOG"),
+ @ViewDebug.IntToString(from = TYPE_SCREENSHOT, to = "TYPE_SCREENSHOT")
})
public int type;
@@ -622,6 +635,13 @@
public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
/**
+ * Window type: shares similar characteristics with {@link #TYPE_DREAM}. The layer is
+ * reserved for screenshot region selection.
+ * @hide
+ */
+ public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
+
+ /**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index b011414..c1392fe 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -91,6 +91,11 @@
public final static int FLAG_INTERACTIVE = 0x20000000;
public final static int FLAG_PASS_TO_USER = 0x40000000;
+ // Flags for IActivityManager.keyguardGoingAway()
+ public final static int KEYGUARD_GOING_AWAY_FLAG_TO_SHADE = 1 << 0;
+ public final static int KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS = 1 << 1;
+ public final static int KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER = 1 << 2;
+
// Flags used for indicating whether the internal and/or external input devices
// of some type are available.
public final static int PRESENCE_INTERNAL = 1 << 0;
@@ -405,6 +410,12 @@
* not attached to any stack.
*/
int getStackId();
+
+ /**
+ * Returns true if the window is current in multi-windowing mode. i.e. it shares the
+ * screen with other application windows.
+ */
+ public boolean inMultiWindowMode();
}
/**
diff --git a/core/java/android/view/animation/BaseInterpolator.java b/core/java/android/view/animation/BaseInterpolator.java
index 9c0014c..a78fa1e 100644
--- a/core/java/android/view/animation/BaseInterpolator.java
+++ b/core/java/android/view/animation/BaseInterpolator.java
@@ -16,22 +16,24 @@
package android.view.animation;
+import android.content.pm.ActivityInfo.Config;
+
/**
* An abstract class which is extended by default interpolators.
*/
abstract public class BaseInterpolator implements Interpolator {
- private int mChangingConfiguration;
+ private @Config int mChangingConfiguration;
/**
* @hide
*/
- public int getChangingConfiguration() {
+ public @Config int getChangingConfiguration() {
return mChangingConfiguration;
}
/**
* @hide
*/
- void setChangingConfiguration(int changingConfiguration) {
+ void setChangingConfiguration(@Config int changingConfiguration) {
mChangingConfiguration = changingConfiguration;
}
}
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java
index 471b6d4..fc17f7a 100644
--- a/core/java/android/view/textservice/SpellCheckerInfo.java
+++ b/core/java/android/view/textservice/SpellCheckerInfo.java
@@ -31,10 +31,12 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.Xml;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.ArrayList;
/**
@@ -55,7 +57,7 @@
/**
* The array of subtypes.
*/
- private final ArrayList<SpellCheckerSubtype> mSubtypes = new ArrayList<SpellCheckerSubtype>();
+ private final ArrayList<SpellCheckerSubtype> mSubtypes = new ArrayList<>();
/**
* Constructor.
@@ -267,4 +269,22 @@
public int describeContents() {
return 0;
}
+
+ /**
+ * @hide
+ */
+ public void dump(final PrintWriter pw, final String prefix) {
+ pw.println(prefix + "mId=" + mId);
+ pw.println(prefix + "mSettingsActivityName=" + mSettingsActivityName);
+ pw.println(prefix + "Service:");
+ mService.dump(new PrintWriterPrinter(pw), prefix + " ");
+ final int N = getSubtypeCount();
+ for (int i = 0; i < N; i++) {
+ final SpellCheckerSubtype st = getSubtypeAt(i);
+ pw.println(prefix + " " + "Subtype #" + i + ":");
+ pw.println(prefix + " " + "locale=" + st.getLocale()
+ + " languageTag=" + st.getLanguageTag());
+ pw.println(prefix + " " + "extraValue=" + st.getExtraValue());
+ }
+ }
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index ad50ff6..3d72260 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -262,11 +262,11 @@
"For security reasons, WebView is not allowed in privileged processes");
}
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
try {
Class<WebViewFactoryProvider> providerClass = getProviderClass();
- StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()");
try {
sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
@@ -278,10 +278,10 @@
throw new AndroidRuntimeException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
- StrictMode.setThreadPolicy(oldPolicy);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
+ StrictMode.setThreadPolicy(oldPolicy);
}
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index d2aef0a..7cbe8de 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2864,7 +2864,7 @@
}
@Override
- public boolean verifyDrawable(Drawable dr) {
+ public boolean verifyDrawable(@NonNull Drawable dr) {
return mSelector == dr || super.verifyDrawable(dr);
}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 34f3a47..878a9eb 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -18,6 +18,7 @@
import com.android.internal.R;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -485,7 +486,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return who == mThumb || who == mTickMark || super.verifyDrawable(who);
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 9f94005..df506ca 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -308,7 +308,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return who == mCheckMarkDrawable || super.verifyDrawable(who);
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index b19fe17..5d7585f 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -474,7 +474,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == mButtonDrawable;
}
diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
index f5840dc..8ce2f9c 100644
--- a/core/java/android/widget/DayPickerPagerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -282,7 +282,7 @@
public CharSequence getPageTitle(int position) {
final SimpleMonthView v = mItems.get(position).calendar;
if (v != null) {
- return v.getTitle();
+ return v.getMonthYearLabel();
}
return null;
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5c06638..4bcb406 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -18,6 +18,7 @@
import android.R;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
@@ -215,7 +216,8 @@
boolean mInBatchEditControllers;
boolean mShowSoftInputOnFocus = true;
- boolean mPreserveDetachedSelection;
+ private boolean mPreserveSelection;
+ private boolean mRestartActionModeOnNextRefresh;
boolean mTemporaryDetach;
boolean mIsBeingLongClicked;
@@ -290,7 +292,6 @@
boolean mIsInsertionActionModeStartPending = false;
private final SuggestionHelper mSuggestionHelper = new SuggestionHelper();
- private SuggestionInfo[] mSuggestionInfosInContextMenu;
Editor(TextView textView) {
mTextView = textView;
@@ -351,10 +352,14 @@
}
void replace() {
+ if (mSuggestionsPopupWindow == null) {
+ mSuggestionsPopupWindow = new SuggestionsPopupWindow();
+ }
+ hideCursorAndSpanControllers();
+ mSuggestionsPopupWindow.show();
+
int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2;
- stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), middle);
- showSuggestions();
}
void onAttachedToWindow() {
@@ -377,15 +382,8 @@
updateSpellCheckSpans(0, mTextView.getText().length(),
true /* create the spell checker if needed */);
- if (mTextView.hasTransientState() &&
- mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
- // Since transient state is reference counted make sure it stays matched
- // with our own calls to it for managing selection.
- // The action mode callback will set this back again when/if the action mode starts.
- mTextView.setHasTransientState(false);
-
- // We had an active selection from before, start the selection mode.
- startSelectionActionMode();
+ if (mTextView.hasSelection()) {
+ refreshTextActionMode();
}
getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
@@ -429,10 +427,8 @@
mSpellChecker = null;
}
- mPreserveDetachedSelection = true;
hideCursorAndSpanControllers();
- stopTextActionMode();
- mPreserveDetachedSelection = false;
+ stopTextActionModeWithPreservingSelection();
mTemporaryDetach = false;
}
@@ -930,7 +926,8 @@
}
void onLocaleChanged() {
- // Will be re-created on demand in getWordIterator with the proper new locale
+ // Will be re-created on demand in getWordIterator and getWordIteratorWithText with the
+ // proper new locale
mWordIterator = null;
mWordIteratorWithText = null;
}
@@ -1085,6 +1082,10 @@
}
private void startDragAndDrop() {
+ // TODO: Fix drag and drop in full screen extracted mode.
+ if (mTextView.isInExtractedMode()) {
+ return;
+ }
final int start = mTextView.getSelectionStart();
final int end = mTextView.getSelectionEnd();
CharSequence selectedText = mTextView.getTransformedText(start, end);
@@ -1104,7 +1105,6 @@
mInsertionControllerEnabled) {
final int offset = mTextView.getOffsetForPosition(mLastDownPositionX,
mLastDownPositionY);
- stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), offset);
getInsertionController().show();
mIsInsertionActionModeStartPending = true;
@@ -1208,18 +1208,15 @@
mTextView.onEndBatchEdit();
if (mTextView.isInExtractedMode()) {
- // terminateTextSelectionMode removes selection, which we want to keep when
- // ExtractEditText goes out of focus.
- final int selStart = mTextView.getSelectionStart();
- final int selEnd = mTextView.getSelectionEnd();
hideCursorAndSpanControllers();
- stopTextActionMode();
- Selection.setSelection((Spannable) mTextView.getText(), selStart, selEnd);
+ stopTextActionModeWithPreservingSelection();
} else {
- if (mTemporaryDetach) mPreserveDetachedSelection = true;
hideCursorAndSpanControllers();
- stopTextActionMode();
- if (mTemporaryDetach) mPreserveDetachedSelection = false;
+ if (mTemporaryDetach) {
+ stopTextActionModeWithPreservingSelection();
+ } else {
+ stopTextActionMode();
+ }
downgradeEasyCorrectionSpans();
}
// No need to create the controller
@@ -1290,10 +1287,8 @@
makeBlink();
}
final InputMethodManager imm = InputMethodManager.peekInstance();
- final boolean immFullScreen = (imm != null && imm.isFullscreenMode());
- if (mSelectionModifierCursorController != null && mTextView.hasSelection()
- && !immFullScreen && mTextActionMode != null) {
- mSelectionModifierCursorController.show();
+ if (mTextView.hasSelection() && !extractedTextModeWillBeStarted()) {
+ refreshTextActionMode();
}
} else {
if (mBlink != null) {
@@ -1304,9 +1299,7 @@
}
// Order matters! Must be done before onParentLostFocus to rely on isShowingUp
hideCursorAndSpanControllers();
- if (mSelectionModifierCursorController != null) {
- mSelectionModifierCursorController.hide();
- }
+ stopTextActionModeWithPreservingSelection();
if (mSuggestionsPopupWindow != null) {
mSuggestionsPopupWindow.onParentLostFocus();
}
@@ -1856,6 +1849,47 @@
}
}
+ void refreshTextActionMode() {
+ if (extractedTextModeWillBeStarted()) {
+ mRestartActionModeOnNextRefresh = false;
+ return;
+ }
+ final boolean hasSelection = mTextView.hasSelection();
+ final SelectionModifierCursorController selectionController = getSelectionController();
+ final InsertionPointCursorController insertionController = getInsertionController();
+ if ((selectionController != null && selectionController.isCursorBeingModified())
+ || (insertionController != null && insertionController.isCursorBeingModified())) {
+ // ActionMode should be managed by the currently active cursor controller.
+ mRestartActionModeOnNextRefresh = false;
+ return;
+ }
+ if (hasSelection) {
+ hideInsertionPointCursorController();
+ if (mTextActionMode == null) {
+ if (mRestartActionModeOnNextRefresh || mTextView.isInExtractedMode()) {
+ // To avoid distraction, newly start action mode only when selection action
+ // mode is being restarted or in full screen extracted mode.
+ startSelectionActionMode();
+ }
+ } else if (selectionController == null || !selectionController.isActive()) {
+ // Insertion action mode is active. Avoid dismissing the selection.
+ stopTextActionModeWithPreservingSelection();
+ startSelectionActionMode();
+ } else {
+ mTextActionMode.invalidateContentRect();
+ }
+ } else {
+ // Insertion action mode is started only when insertion controller is explicitly
+ // activated.
+ if (insertionController == null || !insertionController.isActive()) {
+ stopTextActionMode();
+ } else if (mTextActionMode != null) {
+ mTextActionMode.invalidateContentRect();
+ }
+ }
+ mRestartActionModeOnNextRefresh = false;
+ }
+
/**
* Start an Insertion action mode.
*/
@@ -1879,8 +1913,8 @@
/**
* Starts a Selection Action Mode with the current selection and ensures the selection handles
- * are shown if there is a selection, otherwise the insertion handle is shown. This should be
- * used when the mode is started from a non-touch event.
+ * are shown if there is a selection. This should be used when the mode is started from a
+ * non-touch event.
*
* @return true if the selection mode was actually started.
*/
@@ -1888,9 +1922,8 @@
boolean selectionStarted = startSelectionActionModeInternal();
if (selectionStarted) {
getSelectionController().show();
- } else if (getInsertionController() != null) {
- getInsertionController().show();
}
+ mRestartActionModeOnNextRefresh = false;
return selectionStarted;
}
@@ -1907,66 +1940,52 @@
if (extractedTextModeWillBeStarted()) {
return false;
}
- if (mTextActionMode != null) {
- mTextActionMode.finish();
- }
- if (!checkFieldAndSelectCurrentWord()) {
+ if (!checkField()) {
return false;
}
-
- // Avoid dismissing the selection if it exists.
- mPreserveDetachedSelection = true;
- stopTextActionMode();
- mPreserveDetachedSelection = false;
-
+ if (!mTextView.hasSelection() && !selectCurrentWord()) {
+ // No selection and cannot select a word.
+ return false;
+ }
+ stopTextActionModeWithPreservingSelection();
getSelectionController().enterDrag(
SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_WORD);
return true;
}
/**
- * Checks whether a selection can be performed on the current TextView and if so selects
- * the current word.
+ * Checks whether a selection can be performed on the current TextView.
*
- * @return true if there already was a selection or if the current word was selected.
+ * @return true if a selection can be performed
*/
- boolean checkFieldAndSelectCurrentWord() {
+ boolean checkField() {
if (!mTextView.canSelectText() || !mTextView.requestFocus()) {
Log.w(TextView.LOG_TAG,
"TextView does not support text selection. Selection cancelled.");
return false;
}
-
- if (!mTextView.hasSelection()) {
- // There may already be a selection on device rotation
- return selectCurrentWord();
- }
return true;
}
private boolean startSelectionActionModeInternal() {
+ if (extractedTextModeWillBeStarted()) {
+ return false;
+ }
if (mTextActionMode != null) {
// Text action mode is already started
mTextActionMode.invalidate();
return false;
}
- if (!checkFieldAndSelectCurrentWord()) {
+ if (!checkField() || !mTextView.hasSelection()) {
return false;
}
- boolean willExtract = extractedTextModeWillBeStarted();
+ ActionMode.Callback actionModeCallback =
+ new TextActionModeCallback(true /* hasSelection */);
+ mTextActionMode = mTextView.startActionMode(actionModeCallback, ActionMode.TYPE_FLOATING);
- // Do not start the action mode when extracted text will show up full screen, which would
- // immediately hide the newly created action bar and would be visually distracting.
- if (!willExtract) {
- ActionMode.Callback actionModeCallback =
- new TextActionModeCallback(true /* hasSelection */);
- mTextActionMode = mTextView.startActionMode(
- actionModeCallback, ActionMode.TYPE_FLOATING);
- }
-
- final boolean selectionStarted = mTextActionMode != null || willExtract;
+ final boolean selectionStarted = mTextActionMode != null;
if (selectionStarted && !mTextView.isTextSelectable() && mShowSoftInputOnFocus) {
// Show the IME to be able to replace text, except when selecting non editable text.
final InputMethodManager imm = InputMethodManager.peekInstance();
@@ -2087,7 +2106,7 @@
mShowSuggestionRunnable = new Runnable() {
public void run() {
- showSuggestions();
+ replace();
}
};
// removeCallbacks is performed on every touch
@@ -2107,6 +2126,15 @@
}
}
+ private void stopTextActionModeWithPreservingSelection() {
+ if (mTextActionMode != null) {
+ mRestartActionModeOnNextRefresh = true;
+ }
+ mPreserveSelection = true;
+ stopTextActionMode();
+ mPreserveSelection = false;
+ }
+
/**
* @return True if this view supports insertion handles.
*/
@@ -2222,15 +2250,6 @@
mCorrectionHighlighter.highlight(info);
}
- void showSuggestions() {
- if (mSuggestionsPopupWindow == null) {
- mSuggestionsPopupWindow = new SuggestionsPopupWindow();
- }
- hideCursorAndSpanControllers();
- stopTextActionMode();
- mSuggestionsPopupWindow.show();
- }
-
void onScrollChanged() {
if (mPositionListener != null) {
mPositionListener.onScrollChanged();
@@ -2394,17 +2413,22 @@
dragSourceEnd += shift;
}
- // Delete original selection
- mTextView.deleteText_internal(dragSourceStart, dragSourceEnd);
+ mUndoInputFilter.setForceMerge(true);
+ try {
+ // Delete original selection
+ mTextView.deleteText_internal(dragSourceStart, dragSourceEnd);
- // Make sure we do not leave two adjacent spaces.
- final int prevCharIdx = Math.max(0, dragSourceStart - 1);
- final int nextCharIdx = Math.min(mTextView.getText().length(), dragSourceStart + 1);
- if (nextCharIdx > prevCharIdx + 1) {
- CharSequence t = mTextView.getTransformedText(prevCharIdx, nextCharIdx);
- if (Character.isSpaceChar(t.charAt(0)) && Character.isSpaceChar(t.charAt(1))) {
- mTextView.deleteText_internal(prevCharIdx, prevCharIdx + 1);
+ // Make sure we do not leave two adjacent spaces.
+ final int prevCharIdx = Math.max(0, dragSourceStart - 1);
+ final int nextCharIdx = Math.min(mTextView.getText().length(), dragSourceStart + 1);
+ if (nextCharIdx > prevCharIdx + 1) {
+ CharSequence t = mTextView.getTransformedText(prevCharIdx, nextCharIdx);
+ if (Character.isSpaceChar(t.charAt(0)) && Character.isSpaceChar(t.charAt(1))) {
+ mTextView.deleteText_internal(prevCharIdx, prevCharIdx + 1);
+ }
}
+ } finally {
+ mUndoInputFilter.setForceMerge(false);
}
}
}
@@ -2436,34 +2460,35 @@
if (offset == -1) {
return;
}
- mPreserveDetachedSelection = true;
- stopTextActionMode();
- mPreserveDetachedSelection = false;
+ stopTextActionModeWithPreservingSelection();
final boolean isOnSelection = mTextView.hasSelection()
&& offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd();
if (!isOnSelection) {
// Right clicked position is not on the selection. Remove the selection and move the
// cursor to the right clicked position.
- stopTextActionMode();
Selection.setSelection((Spannable) mTextView.getText(), offset);
+ stopTextActionMode();
}
if (shouldOfferToShowSuggestions()) {
- if (mSuggestionInfosInContextMenu == null) {
- mSuggestionInfosInContextMenu =
- new SuggestionInfo[SuggestionSpan.SUGGESTIONS_MAX_SIZE];
- for (int i = 0; i < mSuggestionInfosInContextMenu.length; i++) {
- mSuggestionInfosInContextMenu[i] = new SuggestionInfo();
- }
+ final SuggestionInfo[] suggestionInfoArray =
+ new SuggestionInfo[SuggestionSpan.SUGGESTIONS_MAX_SIZE];
+ for (int i = 0; i < suggestionInfoArray.length; i++) {
+ suggestionInfoArray[i] = new SuggestionInfo();
}
final SubMenu subMenu = menu.addSubMenu(Menu.NONE, Menu.NONE, MENU_ITEM_ORDER_REPLACE,
com.android.internal.R.string.replace);
- mSuggestionHelper.getSuggestionInfo(mSuggestionInfosInContextMenu);
- int i = 0;
- for (final SuggestionInfo info : mSuggestionInfosInContextMenu) {
- info.mSuggestionEnd = info.mText.length();
- subMenu.add(Menu.NONE, Menu.NONE, i++, info.mText)
- .setOnMenuItemClickListener(mOnContextMenuReplaceItemClickListener);
+ final int numItems = mSuggestionHelper.getSuggestionInfo(suggestionInfoArray, null);
+ for (int i = 0; i < numItems; i++) {
+ final SuggestionInfo info = suggestionInfoArray[i];
+ subMenu.add(Menu.NONE, Menu.NONE, i, info.mText)
+ .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ replaceWithSuggestion(info);
+ return true;
+ }
+ });
}
}
@@ -2506,11 +2531,52 @@
.setEnabled(mTextView.canSelectAllText())
.setOnMenuItemClickListener(mOnContextMenuItemClickListener);
- mPreserveDetachedSelection = true;
+ mPreserveSelection = true;
}
- private void replaceWithSuggestion(SuggestionInfo suggestionInfo, int spanStart, int spanEnd) {
+ @Nullable
+ private SuggestionSpan findEquivalentSuggestionSpan(
+ @NonNull SuggestionSpanInfo suggestionSpanInfo) {
final Editable editable = (Editable) mTextView.getText();
+ if (editable.getSpanStart(suggestionSpanInfo.mSuggestionSpan) >= 0) {
+ // Exactly same span is found.
+ return suggestionSpanInfo.mSuggestionSpan;
+ }
+ // Suggestion span couldn't be found. Try to find a suggestion span that has the same
+ // contents.
+ final SuggestionSpan[] suggestionSpans = editable.getSpans(suggestionSpanInfo.mSpanStart,
+ suggestionSpanInfo.mSpanEnd, SuggestionSpan.class);
+ for (final SuggestionSpan suggestionSpan : suggestionSpans) {
+ final int start = editable.getSpanStart(suggestionSpan);
+ if (start != suggestionSpanInfo.mSpanStart) {
+ continue;
+ }
+ final int end = editable.getSpanEnd(suggestionSpan);
+ if (end != suggestionSpanInfo.mSpanEnd) {
+ continue;
+ }
+ if (suggestionSpan.equals(suggestionSpanInfo.mSuggestionSpan)) {
+ return suggestionSpan;
+ }
+ }
+ return null;
+ }
+
+ private void replaceWithSuggestion(@NonNull final SuggestionInfo suggestionInfo) {
+ final SuggestionSpan targetSuggestionSpan = findEquivalentSuggestionSpan(
+ suggestionInfo.mSuggestionSpanInfo);
+ if (targetSuggestionSpan == null) {
+ // Span has been removed
+ return;
+ }
+ final Editable editable = (Editable) mTextView.getText();
+ final int spanStart = editable.getSpanStart(targetSuggestionSpan);
+ final int spanEnd = editable.getSpanEnd(targetSuggestionSpan);
+ if (spanStart < 0 || spanEnd <= spanStart) {
+ // Span has been removed
+ return;
+ }
+
final String originalText = TextUtils.substring(editable, spanStart, spanEnd);
// SuggestionSpans are removed by replace: save them before
SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
@@ -2535,7 +2601,7 @@
}
// Notify source IME of the suggestion pick. Do this before swapping texts.
- suggestionInfo.mSuggestionSpan.notifySelection(
+ targetSuggestionSpan.notifySelection(
mTextView.getContext(), originalText, suggestionInfo.mSuggestionIndex);
// Swap text content between actual text and Suggestion span
@@ -2545,7 +2611,7 @@
suggestionStart, suggestionEnd).toString();
mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
- String[] suggestions = suggestionInfo.mSuggestionSpan.getSuggestions();
+ String[] suggestions = targetSuggestionSpan.getSuggestions();
suggestions[suggestionInfo.mSuggestionIndex] = originalText;
// Restore previous SuggestionSpans
@@ -2575,31 +2641,6 @@
}
};
- private final MenuItem.OnMenuItemClickListener mOnContextMenuReplaceItemClickListener =
- new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- int index = item.getOrder();
- if (index < 0 || index >= mSuggestionInfosInContextMenu.length) {
- clear();
- return false;
- }
- final Spannable spannable = (Spannable) mTextView.getText();
- final SuggestionSpan suggestionSpan =
- mSuggestionInfosInContextMenu[index].mSuggestionSpan;
- replaceWithSuggestion(mSuggestionInfosInContextMenu[index],
- spannable.getSpanStart(suggestionSpan), spannable.getSpanEnd(suggestionSpan));
- clear();
- return true;
- }
-
- private void clear() {
- for (final SuggestionInfo info : mSuggestionInfosInContextMenu) {
- info.clear();
- }
- }
- };
-
/**
* Controls the {@link EasyEditSpan} monitoring when it is added, and when the related
* pop-up should be displayed.
@@ -3018,13 +3059,12 @@
}
}
- private static class SuggestionInfo {
- // Range of actual suggestion within text
+ private static final class SuggestionInfo {
+ // Range of actual suggestion within mText
int mSuggestionStart, mSuggestionEnd;
// The SuggestionSpan that this TextView represents
- @Nullable
- SuggestionSpan mSuggestionSpan;
+ final SuggestionSpanInfo mSuggestionSpanInfo = new SuggestionSpanInfo();
// The index of this suggestion inside suggestionSpan
int mSuggestionIndex;
@@ -3032,9 +3072,32 @@
final SpannableStringBuilder mText = new SpannableStringBuilder();
void clear() {
- mSuggestionSpan = null;
+ mSuggestionSpanInfo.clear();
mText.clear();
}
+
+ // Utility method to set attributes about a SuggestionSpan.
+ void setSpanInfo(SuggestionSpan span, int spanStart, int spanEnd) {
+ mSuggestionSpanInfo.mSuggestionSpan = span;
+ mSuggestionSpanInfo.mSpanStart = spanStart;
+ mSuggestionSpanInfo.mSpanEnd = spanEnd;
+ }
+ }
+
+ private static final class SuggestionSpanInfo {
+ // The SuggestionSpan;
+ @Nullable
+ SuggestionSpan mSuggestionSpan;
+
+ // The SuggestionSpan start position
+ int mSpanStart;
+
+ // The SuggestionSpan end position
+ int mSpanEnd;
+
+ void clear() {
+ mSuggestionSpan = null;
+ }
}
private class SuggestionHelper {
@@ -3092,43 +3155,48 @@
* position.
*
* @param suggestionInfos SuggestionInfo array the results will be set.
+ * @param misspelledSpanInfo a struct the misspelled SuggestionSpan info will be set.
* @return the number of suggestions actually fetched.
*/
- public int getSuggestionInfo(SuggestionInfo[] suggestionInfos) {
+ public int getSuggestionInfo(SuggestionInfo[] suggestionInfos,
+ @Nullable SuggestionSpanInfo misspelledSpanInfo) {
final Spannable spannable = (Spannable) mTextView.getText();
final SuggestionSpan[] suggestionSpans = getSortedSuggestionSpans();
final int nbSpans = suggestionSpans.length;
if (nbSpans == 0) return 0;
int numberOfSuggestions = 0;
- for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
- final SuggestionSpan suggestionSpan = suggestionSpans[spanIndex];
+ for (final SuggestionSpan suggestionSpan : suggestionSpans) {
final int spanStart = spannable.getSpanStart(suggestionSpan);
final int spanEnd = spannable.getSpanEnd(suggestionSpan);
+ if (misspelledSpanInfo != null
+ && (suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) {
+ misspelledSpanInfo.mSuggestionSpan = suggestionSpan;
+ misspelledSpanInfo.mSpanStart = spanStart;
+ misspelledSpanInfo.mSpanEnd = spanEnd;
+ }
+
final String[] suggestions = suggestionSpan.getSuggestions();
final int nbSuggestions = suggestions.length;
+ suggestionLoop:
for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
final String suggestion = suggestions[suggestionIndex];
- boolean suggestionIsDuplicate = false;
for (int i = 0; i < numberOfSuggestions; i++) {
- if (suggestionInfos[i].mText.toString().equals(suggestion)) {
- final SuggestionSpan otherSuggestionSpan =
- suggestionInfos[i].mSuggestionSpan;
- final int otherSpanStart = spannable.getSpanStart(otherSuggestionSpan);
- final int otherSpanEnd = spannable.getSpanEnd(otherSuggestionSpan);
+ final SuggestionInfo otherSuggestionInfo = suggestionInfos[i];
+ if (otherSuggestionInfo.mText.toString().equals(suggestion)) {
+ final int otherSpanStart =
+ otherSuggestionInfo.mSuggestionSpanInfo.mSpanStart;
+ final int otherSpanEnd =
+ otherSuggestionInfo.mSuggestionSpanInfo.mSpanEnd;
if (spanStart == otherSpanStart && spanEnd == otherSpanEnd) {
- suggestionIsDuplicate = true;
- break;
+ continue suggestionLoop;
}
}
}
- if (suggestionIsDuplicate) {
- continue;
- }
SuggestionInfo suggestionInfo = suggestionInfos[numberOfSuggestions];
- suggestionInfo.mSuggestionSpan = suggestionSpan;
+ suggestionInfo.setSpanInfo(suggestionSpan, spanStart, spanEnd);
suggestionInfo.mSuggestionIndex = suggestionIndex;
suggestionInfo.mSuggestionStart = 0;
suggestionInfo.mSuggestionEnd = suggestion.length();
@@ -3161,7 +3229,7 @@
private TextView mAddToDictionaryButton;
private TextView mDeleteButton;
private ListView mSuggestionListView;
- private SuggestionSpan mMisspelledSpan;
+ private final SuggestionSpanInfo mMisspelledSpanInfo = new SuggestionSpanInfo();
private int mContainerMarginWidth;
private int mContainerMarginTop;
@@ -3178,7 +3246,7 @@
((Spannable) mTextView.getText()).removeSpan(mSuggestionRangeSpan);
mTextView.setCursorVisible(mCursorWasVisibleBeforeSuggestions);
- if (hasInsertionController()) {
+ if (hasInsertionController() && !extractedTextModeWillBeStarted()) {
getInsertionController().show();
}
}
@@ -3233,9 +3301,18 @@
com.android.internal.R.id.addToDictionaryButton);
mAddToDictionaryButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
+ final SuggestionSpan misspelledSpan =
+ findEquivalentSuggestionSpan(mMisspelledSpanInfo);
+ if (misspelledSpan == null) {
+ // Span has been removed.
+ return;
+ }
final Editable editable = (Editable) mTextView.getText();
- final int spanStart = editable.getSpanStart(mMisspelledSpan);
- final int spanEnd = editable.getSpanEnd(mMisspelledSpan);
+ final int spanStart = editable.getSpanStart(misspelledSpan);
+ final int spanEnd = editable.getSpanEnd(misspelledSpan);
+ if (spanStart < 0 || spanEnd <= spanStart) {
+ return;
+ }
final String originalText = TextUtils.substring(editable, spanStart, spanEnd);
final Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT);
@@ -3246,7 +3323,7 @@
mTextView.getContext().startActivity(intent);
// There is no way to know if the word was indeed added. Re-check.
// TODO The ExtractEditText should remove the span in the original text instead
- editable.removeSpan(mMisspelledSpan);
+ editable.removeSpan(mMisspelledSpanInfo.mSuggestionSpan);
Selection.setSelection(editable, spanEnd);
updateSpellCheckSpans(spanStart, spanEnd, false);
hideWithCleanUp();
@@ -3328,6 +3405,9 @@
@Override
public void show() {
if (!(mTextView.getText() instanceof Editable)) return;
+ if (extractedTextModeWillBeStarted()) {
+ return;
+ }
if (updateSuggestions()) {
mCursorWasVisibleBeforeSuggestions = mCursorVisible;
@@ -3381,7 +3461,7 @@
@Override
protected int getTextOffset() {
- return mTextView.getSelectionStart();
+ return (mTextView.getSelectionStart() + mTextView.getSelectionStart()) / 2;
}
@Override
@@ -3400,32 +3480,29 @@
for (final SuggestionInfo info : mSuggestionInfos) {
info.clear();
}
- mMisspelledSpan = null;
+ mMisspelledSpanInfo.clear();
hide();
}
private boolean updateSuggestions() {
Spannable spannable = (Spannable) mTextView.getText();
mNumberOfSuggestions =
- mSuggestionHelper.getSuggestionInfo(mSuggestionInfos);
- if (mNumberOfSuggestions == 0) {
+ mSuggestionHelper.getSuggestionInfo(mSuggestionInfos, mMisspelledSpanInfo);
+ if (mNumberOfSuggestions == 0 && mMisspelledSpanInfo.mSuggestionSpan == null) {
return false;
}
int spanUnionStart = mTextView.getText().length();
int spanUnionEnd = 0;
- mMisspelledSpan = null;
for (int i = 0; i < mNumberOfSuggestions; i++) {
- final SuggestionInfo suggestionInfo = mSuggestionInfos[i];
- final SuggestionSpan suggestionSpan = suggestionInfo.mSuggestionSpan;
- if ((suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) {
- mMisspelledSpan = suggestionSpan;
- }
- final int spanStart = spannable.getSpanStart(suggestionSpan);
- final int spanEnd = spannable.getSpanEnd(suggestionSpan);
- spanUnionStart = Math.min(spanUnionStart, spanStart);
- spanUnionEnd = Math.max(spanUnionEnd, spanEnd);
+ final SuggestionSpanInfo spanInfo = mSuggestionInfos[i].mSuggestionSpanInfo;
+ spanUnionStart = Math.min(spanUnionStart, spanInfo.mSpanStart);
+ spanUnionEnd = Math.max(spanUnionEnd, spanInfo.mSpanEnd);
+ }
+ if (mMisspelledSpanInfo.mSuggestionSpan != null) {
+ spanUnionStart = Math.min(spanUnionStart, mMisspelledSpanInfo.mSpanStart);
+ spanUnionEnd = Math.max(spanUnionEnd, mMisspelledSpanInfo.mSpanEnd);
}
for (int i = 0; i < mNumberOfSuggestions; i++) {
@@ -3434,17 +3511,23 @@
// Make "Add to dictionary" item visible if there is a span with the misspelled flag
int addToDictionaryButtonVisibility = View.GONE;
- if (mMisspelledSpan != null) {
- final int misspelledStart = spannable.getSpanStart(mMisspelledSpan);
- final int misspelledEnd = spannable.getSpanEnd(mMisspelledSpan);
- if (misspelledStart >= 0 && misspelledEnd > misspelledStart) {
+ if (mMisspelledSpanInfo.mSuggestionSpan != null) {
+ if (mMisspelledSpanInfo.mSpanStart >= 0
+ && mMisspelledSpanInfo.mSpanEnd > mMisspelledSpanInfo.mSpanStart) {
addToDictionaryButtonVisibility = View.VISIBLE;
}
}
mAddToDictionaryButton.setVisibility(addToDictionaryButtonVisibility);
if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
- final int underlineColor = mSuggestionInfos[0].mSuggestionSpan.getUnderlineColor();
+ final int underlineColor;
+ if (mNumberOfSuggestions != 0) {
+ underlineColor =
+ mSuggestionInfos[0].mSuggestionSpanInfo.mSuggestionSpan.getUnderlineColor();
+ } else {
+ underlineColor = mMisspelledSpanInfo.mSuggestionSpan.getUnderlineColor();
+ }
+
if (underlineColor == 0) {
// Fallback on the default highlight color when the first span does not provide one
mSuggestionRangeSpan.setBackgroundColor(mTextView.mHighlightColor);
@@ -3464,8 +3547,8 @@
private void highlightTextDifferences(SuggestionInfo suggestionInfo, int unionStart,
int unionEnd) {
final Spannable text = (Spannable) mTextView.getText();
- final int spanStart = text.getSpanStart(suggestionInfo.mSuggestionSpan);
- final int spanEnd = text.getSpanEnd(suggestionInfo.mSuggestionSpan);
+ final int spanStart = suggestionInfo.mSuggestionSpanInfo.mSpanStart;
+ final int spanEnd = suggestionInfo.mSuggestionSpanInfo.mSpanEnd;
// Adjust the start/end of the suggestion span
suggestionInfo.mSuggestionStart = spanStart - unionStart;
@@ -3483,17 +3566,8 @@
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Editable editable = (Editable) mTextView.getText();
SuggestionInfo suggestionInfo = mSuggestionInfos[position];
-
- final int spanStart = editable.getSpanStart(suggestionInfo.mSuggestionSpan);
- final int spanEnd = editable.getSpanEnd(suggestionInfo.mSuggestionSpan);
- if (spanStart < 0 || spanEnd <= spanStart) {
- // Span has been removed
- hideWithCleanUp();
- return;
- }
- replaceWithSuggestion(suggestionInfo, spanStart, spanEnd);
+ replaceWithSuggestion(suggestionInfo);
hideWithCleanUp();
}
}
@@ -3556,7 +3630,6 @@
}
if (menu.hasVisibleItems() || mode.getCustomView() != null) {
- mTextView.setHasTransientState(true);
return true;
} else {
return false;
@@ -3626,8 +3699,7 @@
}
private void updateReplaceItem(Menu menu) {
- boolean canReplace = mTextView.isSuggestionsEnabled() && shouldOfferToShowSuggestions()
- && !(mTextView.isInExtractedMode() && mTextView.hasSelection());
+ boolean canReplace = mTextView.isSuggestionsEnabled() && shouldOfferToShowSuggestions();
boolean replaceItemExists = menu.findItem(TextView.ID_REPLACE) != null;
if (canReplace && !replaceItemExists) {
menu.add(Menu.NONE, TextView.ID_REPLACE, MENU_ITEM_ORDER_REPLACE,
@@ -3652,27 +3724,26 @@
@Override
public void onDestroyActionMode(ActionMode mode) {
+ // Clear mTextActionMode not to recursively destroy action mode by clearing selection.
+ mTextActionMode = null;
Callback customCallback = getCustomCallback();
if (customCallback != null) {
customCallback.onDestroyActionMode(mode);
}
- /*
- * If we're ending this mode because we're detaching from a window,
- * we still have selection state to preserve. Don't clear it, we'll
- * bring back the selection mode when (if) we get reattached.
- */
- if (!mPreserveDetachedSelection) {
+ if (!mPreserveSelection) {
+ /*
+ * Leave current selection when we tentatively destroy action mode for the
+ * selection. If we're detaching from a window, we'll bring back the selection
+ * mode when (if) we get reattached.
+ */
Selection.setSelection((Spannable) mTextView.getText(),
mTextView.getSelectionEnd());
- mTextView.setHasTransientState(false);
}
if (mSelectionModifierCursorController != null) {
mSelectionModifierCursorController.hide();
}
-
- mTextActionMode = null;
}
@Override
@@ -4388,7 +4459,7 @@
if (distanceSquared < touchSlop * touchSlop) {
// Tapping on the handle toggles the insertion action mode.
if (mTextActionMode != null) {
- mTextActionMode.finish();
+ stopTextActionMode();
} else {
startInsertionActionMode();
}
@@ -4805,6 +4876,10 @@
* preventing the activity from being recycled.
*/
public void onDetached();
+
+ public boolean isCursorBeingModified();
+
+ public boolean isActive();
}
private class InsertionPointCursorController implements CursorController {
@@ -4848,6 +4923,16 @@
if (mHandle != null) mHandle.onDetached();
}
+
+ @Override
+ public boolean isCursorBeingModified() {
+ return mHandle != null && mHandle.isDragging();
+ }
+
+ @Override
+ public boolean isActive() {
+ return mHandle != null && mHandle.isShowing();
+ }
}
class SelectionModifierCursorController implements CursorController {
@@ -5037,9 +5122,7 @@
if (mStartOffset != offset) {
// Start character based drag accelerator.
- if (mTextActionMode != null) {
- mTextActionMode.finish();
- }
+ stopTextActionMode();
enterDrag(DRAG_ACCELERATOR_MODE_CHARACTER);
mDiscardNextActionUp = true;
mHaventMovedEnoughToStartDrag = false;
@@ -5063,27 +5146,12 @@
// No longer dragging to select text, let the parent intercept events.
mTextView.getParent().requestDisallowInterceptTouchEvent(false);
- int startOffset = mTextView.getSelectionStart();
- int endOffset = mTextView.getSelectionEnd();
-
- // Since we don't let drag handles pass once they're visible, we need to
- // make sure the start / end locations are correct because the user *can*
- // switch directions during the initial drag.
- if (endOffset < startOffset) {
- int tmp = endOffset;
- endOffset = startOffset;
- startOffset = tmp;
-
- // Also update the selection with the right offsets in this case.
- Selection.setSelection((Spannable) mTextView.getText(),
- startOffset, endOffset);
- }
- if (startOffset != endOffset) {
- startSelectionActionMode();
- }
-
// No longer the first dragging motion, reset.
resetDragAcceleratorState();
+
+ if (mTextView.hasSelection()) {
+ startSelectionActionMode();
+ }
break;
}
}
@@ -5113,9 +5181,7 @@
if (mInsertionActionModeRunnable != null) {
mTextView.removeCallbacks(mInsertionActionModeRunnable);
}
- if (mTextActionMode != null) {
- mTextActionMode.finish();
- }
+ stopTextActionMode();
if (!selectCurrentParagraph()) {
return false;
}
@@ -5224,6 +5290,12 @@
mStartOffset = -1;
mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE;
mSwitchedLines = false;
+ final int selectionStart = mTextView.getSelectionStart();
+ final int selectionEnd = mTextView.getSelectionEnd();
+ if (selectionStart > selectionEnd) {
+ Selection.setSelection((Spannable) mTextView.getText(),
+ selectionEnd, selectionStart);
+ }
}
/**
@@ -5233,6 +5305,12 @@
return mStartHandle != null && mStartHandle.isDragging();
}
+ @Override
+ public boolean isCursorBeingModified() {
+ return isDragAcceleratorActive() || isSelectionStartDragged()
+ || (mEndHandle != null && mEndHandle.isDragging());
+ }
+
/**
* @return true if the user is selecting text using the drag accelerator.
*/
@@ -5254,6 +5332,11 @@
if (mStartHandle != null) mStartHandle.onDetached();
if (mEndHandle != null) mEndHandle.onDetached();
}
+
+ @Override
+ public boolean isActive() {
+ return mStartHandle != null && mStartHandle.isShowing();
+ }
}
private class CorrectionHighlighter {
@@ -5449,6 +5532,9 @@
// rotates the screen during composition.
private boolean mHasComposition;
+ // Whether to merge events into one operation.
+ private boolean mForceMerge;
+
public UndoInputFilter(Editor editor) {
mEditor = editor;
}
@@ -5463,6 +5549,10 @@
mHasComposition = parcel.readInt() != 0;
}
+ public void setForceMerge(boolean forceMerge) {
+ mForceMerge = forceMerge;
+ }
+
/**
* Signals that a user-triggered edit is starting.
*/
@@ -5522,7 +5612,7 @@
// Otherwise the user inserted the composition.
String newText = TextUtils.substring(source, start, end);
EditOperation edit = new EditOperation(mEditor, "", dstart, newText);
- recordEdit(edit, false /* forceMerge */);
+ recordEdit(edit, mForceMerge);
return true;
}
@@ -5536,7 +5626,7 @@
// the initial input filters run (e.g. a credit card formatter that adds spaces to a
// string). This results in multiple filter() calls for what the user considers to be
// a single operation. Always undo the whole set of changes in one step.
- final boolean forceMerge = isInTextWatcher();
+ final boolean forceMerge = mForceMerge || isInTextWatcher();
// Build a new operation with all the information from this edit.
String newText = TextUtils.substring(source, start, end);
@@ -5977,7 +6067,7 @@
private boolean fireIntent(Intent intent) {
if (intent != null && Intent.ACTION_PROCESS_TEXT.equals(intent.getAction())) {
intent.putExtra(Intent.EXTRA_PROCESS_TEXT, mTextView.getSelectedText());
- mEditor.mPreserveDetachedSelection = true;
+ mEditor.mPreserveSelection = true;
mTextView.startActivityForResult(intent, TextView.PROCESS_TEXT_REQUEST_CODE);
return true;
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index f601f7d..3400873 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -212,7 +212,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable dr) {
+ protected boolean verifyDrawable(@NonNull Drawable dr) {
return mDrawable == dr || super.verifyDrawable(dr);
}
@@ -223,7 +223,7 @@
}
@Override
- public void invalidateDrawable(Drawable dr) {
+ public void invalidateDrawable(@NonNull Drawable dr) {
if (dr == mDrawable) {
if (dr != null) {
// update cached drawable dimensions if they've changed
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index df2f575..dd6a41f 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1651,10 +1651,11 @@
// to executing this method, so we can rely on that instead.
final Transition exitTransition = mExitTransition;
if (mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) {
- // The decor view is non-interactive during exit transitions.
+ // The decor view is non-interactive and non-IME-focusable during exit transitions.
final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
+ p.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
mWindowManager.updateViewLayout(decorView, p);
// Once we start dismissing the decor view, all state (including
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 2099b04..ce94870 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -17,21 +17,15 @@
package android.widget;
import android.animation.ObjectAnimator;
+import android.annotation.InterpolatorRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.graphics.PorterDuff;
-
-import android.util.FloatProperty;
-import android.util.IntProperty;
-import android.view.accessibility.AccessibilityNodeInfo;
-import com.android.internal.R;
-
-import android.annotation.InterpolatorRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Animatable;
@@ -46,6 +40,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.util.MathUtils;
import android.util.Pools.SynchronizedPool;
import android.view.Gravity;
@@ -55,6 +50,7 @@
import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -63,6 +59,7 @@
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.R;
import java.util.ArrayList;
@@ -606,15 +603,30 @@
if (indeterminate) {
// swap between indeterminate and regular backgrounds
- mCurrentDrawable = mIndeterminateDrawable;
+ swapCurrentDrawable(mIndeterminateDrawable);
startAnimation();
} else {
- mCurrentDrawable = mProgressDrawable;
+ swapCurrentDrawable(mProgressDrawable);
stopAnimation();
}
}
}
+ private void swapCurrentDrawable(Drawable newDrawable) {
+ final Drawable oldDrawable = mCurrentDrawable;
+ mCurrentDrawable = newDrawable;
+ if (oldDrawable != mCurrentDrawable) {
+ if (oldDrawable != null) {
+ oldDrawable.setVisible(false, false);
+ }
+ if (mCurrentDrawable != null) {
+ mCurrentDrawable.setVisible(
+ getVisibility() == VISIBLE && getWindowVisibility() == VISIBLE,
+ false);
+ }
+ }
+ }
+
/**
* <p>Get the drawable used to draw the progress bar in
* indeterminate mode.</p>
@@ -654,7 +666,7 @@
}
if (mIndeterminate) {
- mCurrentDrawable = d;
+ swapCurrentDrawable(d);
postInvalidate();
}
}
@@ -820,7 +832,7 @@
}
if (!mIndeterminate) {
- mCurrentDrawable = d;
+ swapCurrentDrawable(d);
postInvalidate();
}
@@ -1217,7 +1229,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return who == mProgressDrawable || who == mIndeterminateDrawable
|| super.verifyDrawable(who);
}
@@ -1555,7 +1567,7 @@
* <p>Start the indeterminate progress animation.</p>
*/
void startAnimation() {
- if (getVisibility() != VISIBLE) {
+ if (getVisibility() != VISIBLE || getWindowVisibility() != VISIBLE) {
return;
}
@@ -1653,18 +1665,34 @@
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
+ updateVisibility();
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(@Visibility int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+
+ updateVisibility();
+ }
+
+ private void updateVisibility() {
+ final boolean isVisible = getVisibility() == VISIBLE && getWindowVisibility() == VISIBLE;
if (mIndeterminate) {
// let's be nice with the UI thread
- if (visibility == GONE || visibility == INVISIBLE) {
- stopAnimation();
- } else {
+ if (isVisible) {
startAnimation();
+ } else {
+ stopAnimation();
}
}
+
+ if (mCurrentDrawable != null) {
+ mCurrentDrawable.setVisible(isVisible, false);
+ }
}
@Override
- public void invalidateDrawable(Drawable dr) {
+ public void invalidateDrawable(@NonNull Drawable dr) {
if (!mInDrawing) {
if (verifyDrawable(dr)) {
final Rect dirty = dr.getBounds();
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index 8880217..11eab2a 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -18,6 +18,7 @@
import com.android.internal.widget.ScrollBarUtils;
+import android.annotation.NonNull;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
@@ -362,17 +363,17 @@
}
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
invalidateSelf();
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
scheduleSelf(what, when);
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
unscheduleSelf(what);
}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 6edce91..43cf5a1 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -33,6 +33,7 @@
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.IntArray;
+import android.util.Log;
import android.util.MathUtils;
import android.util.StateSet;
import android.view.KeyEvent;
@@ -47,6 +48,7 @@
import com.android.internal.widget.ExploreByTouchHelper;
import java.text.NumberFormat;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Locale;
@@ -55,17 +57,22 @@
* within the specified month.
*/
class SimpleMonthView extends View {
+ private static final String LOG_TAG = "SimpleMonthView";
+
private static final int DAYS_IN_WEEK = 7;
private static final int MAX_WEEKS_IN_MONTH = 6;
private static final int DEFAULT_SELECTED_DAY = -1;
private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
- private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
+ private static final String MONTH_YEAR_FORMAT = "MMMMy";
private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
private static final int SELECTED_HIGHLIGHT_ALPHA = 0xB0;
+ /** Temporary until we figure out why the date gets messed up. */
+ private static final boolean DEBUG_WRONG_DATE = true;
+
private final TextPaint mMonthPaint = new TextPaint();
private final TextPaint mDayOfWeekPaint = new TextPaint();
private final TextPaint mDayPaint = new TextPaint();
@@ -73,13 +80,13 @@
private final Paint mDayHighlightPaint = new Paint();
private final Paint mDayHighlightSelectorPaint = new Paint();
- private final Calendar mCalendar = Calendar.getInstance();
- private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance();
+ private final String[] mDayOfWeekLabels = new String[7];
+
+ private final Calendar mCalendar;
+ private final Locale mLocale;
private final MonthViewTouchHelper mTouchHelper;
- private final SimpleDateFormat mTitleFormatter;
- private final SimpleDateFormat mDayOfWeekFormatter;
private final NumberFormat mDayFormatter;
// Desired dimensions.
@@ -89,7 +96,7 @@
private final int mDesiredCellWidth;
private final int mDesiredDaySelectorRadius;
- private CharSequence mTitle;
+ private String mMonthYearLabel;
private int mMonth;
private int mYear;
@@ -168,15 +175,44 @@
setAccessibilityDelegate(mTouchHelper);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- final Locale locale = res.getConfiguration().locale;
- final String titleFormat = DateFormat.getBestDateTimePattern(locale, DEFAULT_TITLE_FORMAT);
- mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
- mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
- mDayFormatter = NumberFormat.getIntegerInstance(locale);
+ mLocale = res.getConfiguration().locale;
+ mCalendar = Calendar.getInstance(mLocale);
+
+ mDayFormatter = NumberFormat.getIntegerInstance(mLocale);
+
+ updateMonthYearLabel();
+ updateDayOfWeekLabels();
initPaints(res);
}
+ private void updateMonthYearLabel() {
+ final String format = DateFormat.getBestDateTimePattern(mLocale, MONTH_YEAR_FORMAT);
+ final SimpleDateFormat formatter = new SimpleDateFormat(format, mLocale);
+ mMonthYearLabel = formatter.format(mCalendar.getTime());
+ }
+
+ private void updateDayOfWeekLabels() {
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "enter updateDayOfWeekLabels()", new Exception());
+ Log.d(LOG_TAG, "mLocale => " + mLocale);
+ Log.d(LOG_TAG, "mWeekStart => " + mWeekStart);
+ }
+
+ final Calendar calendar = Calendar.getInstance(mLocale);
+ calendar.setFirstDayOfWeek(mWeekStart);
+
+ final SimpleDateFormat formatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, mLocale);
+ for (int i = 0; i < 7; i++) {
+ calendar.set(Calendar.DAY_OF_WEEK, i);
+ mDayOfWeekLabels[i] = formatter.format(calendar.getTime());
+ }
+
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "mDayOfWeekLabels <= " + Arrays.toString(mDayOfWeekLabels));
+ }
+ }
+
/**
* Applies the specified text appearance resource to a paint, returning the
* text color if one is set in the text appearance.
@@ -236,13 +272,6 @@
invalidate();
}
- public CharSequence getTitle() {
- if (mTitle == null) {
- mTitle = mTitleFormatter.format(mCalendar.getTime());
- }
- return mTitle;
- }
-
/**
* Sets up the text and style properties for painting.
*/
@@ -607,7 +636,11 @@
final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
final float y = (mMonthHeight - lineHeight) / 2f;
- canvas.drawText(getTitle().toString(), x, y, mMonthPaint);
+ canvas.drawText(mMonthYearLabel, x, y, mMonthPaint);
+ }
+
+ public String getMonthYearLabel() {
+ return mMonthYearLabel;
}
private void drawDaysOfWeek(Canvas canvas) {
@@ -630,16 +663,11 @@
}
final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
- final String label = getDayOfWeekLabel(dayOfWeek);
+ final String label = mDayOfWeekLabels[dayOfWeek];
canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p);
}
}
- private String getDayOfWeekLabel(int dayOfWeek) {
- mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek);
- return mDayOfWeekFormatter.format(mDayOfWeekLabelCalendar.getTime());
- }
-
/**
* Draws the month days.
*/
@@ -746,12 +774,22 @@
* {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
*/
public void setFirstDayOfWeek(int weekStart) {
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "enter setFirstDayOfWeek(" + weekStart + ")", new Exception());
+ }
+
if (isValidDayOfWeek(weekStart)) {
mWeekStart = weekStart;
} else {
mWeekStart = mCalendar.getFirstDayOfWeek();
}
+ if (DEBUG_WRONG_DATE) {
+ Log.d(LOG_TAG, "mWeekStart <=" + mWeekStart);
+ }
+
+ updateDayOfWeekLabels();
+
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
invalidate();
@@ -807,11 +845,10 @@
mEnabledDayStart = MathUtils.constrain(enabledDayStart, 1, mDaysInMonth);
mEnabledDayEnd = MathUtils.constrain(enabledDayEnd, mEnabledDayStart, mDaysInMonth);
- // Invalidate the old title.
- mTitle = null;
-
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
+
+ updateMonthYearLabel();
}
private static int getDaysInMonth(int month, int year) {
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 434516d..c4a1771 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -18,6 +18,7 @@
import android.animation.ObjectAnimator;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.content.Context;
@@ -1371,7 +1372,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 18f1ae5..fbedbda 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1511,6 +1511,9 @@
if (result != null) {
if (isTextEditable()) {
replaceSelectionWithText(result);
+ if (mEditor != null) {
+ mEditor.refreshTextActionMode();
+ }
} else {
if (result.length() > 0) {
Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG)
@@ -1520,12 +1523,7 @@
}
} else if (mText instanceof Spannable) {
// Reset the selection.
- stopTextActionMode();
- Selection.setSelection((Spannable) mText, getSelectionStart(), getSelectionEnd());
- }
-
- if (mEditor.hasSelectionController()) {
- mEditor.startSelectionActionMode();
+ Selection.setSelection((Spannable) mText, getSelectionEnd());
}
}
}
@@ -3073,7 +3071,14 @@
* @attr ref android.R.styleable#TextView_elegantTextHeight
*/
public void setElegantTextHeight(boolean elegant) {
- mTextPaint.setElegantTextHeight(elegant);
+ if (elegant != mTextPaint.isElegantTextHeight()) {
+ mTextPaint.setElegantTextHeight(elegant);
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
+ }
}
/**
@@ -5393,11 +5398,7 @@
// - onFocusChanged cannot start it when focus is given to a view with selected text (after
// a screen rotation) since layout is not yet initialized at that point.
if (mEditor != null && mEditor.mCreatedWithASelection) {
- if (mEditor.extractedTextModeWillBeStarted()) {
- mEditor.checkFieldAndSelectCurrentWord();
- } else {
- mEditor.startSelectionActionMode();
- }
+ mEditor.refreshTextActionMode();
mEditor.mCreatedWithASelection = false;
}
@@ -5469,7 +5470,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
final boolean verified = super.verifyDrawable(who);
if (!verified && mDrawables != null) {
for (Drawable dr : mDrawables.mShowing) {
@@ -5494,7 +5495,7 @@
}
@Override
- public void invalidateDrawable(Drawable drawable) {
+ public void invalidateDrawable(@NonNull Drawable drawable) {
boolean handled = false;
if (verifyDrawable(drawable)) {
@@ -6594,6 +6595,9 @@
// in the extracted view.
mEditor.hideCursorAndSpanControllers();
stopTextActionMode();
+ if (mEditor.mSelectionModifierCursorController != null) {
+ mEditor.mSelectionModifierCursorController.resetTouchOffsets();
+ }
}
/**
@@ -8289,6 +8293,16 @@
if (newSelEnd < 0) {
newSelEnd = Selection.getSelectionEnd(buf);
}
+
+ if (newSelStart == newSelEnd && hasTransientState()) {
+ setHasTransientState(false);
+ } else if (newSelStart != newSelEnd && !hasTransientState()) {
+ setHasTransientState(true);
+ }
+
+ if (mEditor != null) {
+ mEditor.refreshTextActionMode();
+ }
onSelectionChanged(newSelStart, newSelEnd);
}
}
@@ -8909,8 +8923,7 @@
}
void onLocaleChanged() {
- // Will be re-created on demand in getWordIterator with the proper new locale
- mEditor.mWordIterator = null;
+ mEditor.onLocaleChanged();
}
/**
@@ -9393,16 +9406,7 @@
switch (id) {
case ID_SELECT_ALL:
- // This starts an action mode if triggered from another action mode. Text is
- // highlighted, so that it can be bulk edited, like selectAllOnFocus does. Returns
- // true even if text is empty.
- boolean shouldRestartActionMode =
- mEditor != null && mEditor.mTextActionMode != null;
- stopTextActionMode();
selectAllText();
- if (shouldRestartActionMode) {
- mEditor.startSelectionActionMode();
- }
return true;
case ID_UNDO:
@@ -9428,7 +9432,6 @@
case ID_CUT:
setPrimaryClip(ClipData.newPlainText(null, getTransformedText(min, max)));
deleteText_internal(min, max);
- stopTextActionMode();
return true;
case ID_COPY:
@@ -9684,12 +9687,6 @@
}
boolean selectAllText() {
- // Need to hide insert point cursor controller before settings selection, otherwise insert
- // point cursor controller obtains cursor update event and update cursor with cancelling
- // selection.
- if (mEditor != null) {
- mEditor.hideInsertionPointCursorController();
- }
final int length = mText.length();
Selection.setSelection((Spannable) mText, 0, length);
return length > 0;
@@ -9728,7 +9725,6 @@
}
}
}
- stopTextActionMode();
sLastCutCopyOrTextChangedTime = 0;
}
}
@@ -9741,7 +9737,7 @@
sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT);
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
getContext().startActivity(Intent.createChooser(sharingIntent, null));
- stopTextActionMode();
+ Selection.setSelection((Spannable) mText, getSelectionEnd());
}
}
@@ -10059,6 +10055,12 @@
&& getAccessibilitySelectionEnd() == end) {
return;
}
+ CharSequence text = getIterableTextForAccessibility();
+ if (Math.min(start, end) >= 0 && Math.max(start, end) <= text.length()) {
+ Selection.setSelection((Spannable) text, start, end);
+ } else {
+ Selection.removeSelection((Spannable) text);
+ }
// Hide all selection controllers used for adjusting selection
// since we are doing so explicitlty by other means and these
// controllers interact with how selection behaves.
@@ -10066,12 +10068,6 @@
mEditor.hideCursorAndSpanControllers();
mEditor.stopTextActionMode();
}
- CharSequence text = getIterableTextForAccessibility();
- if (Math.min(start, end) >= 0 && Math.max(start, end) <= text.length()) {
- Selection.setSelection((Spannable) text, start, end);
- } else {
- Selection.removeSelection((Spannable) text);
- }
}
/** @hide */
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index b13be97..3a31b37 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -45,6 +45,6 @@
void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages);
void setUserRestrictions(in Bundle restrictions, IBinder token, int userHandle);
- void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle);
+ void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in String[] exceptionPackages);
void removeUser(int userHandle);
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 74fe94f..8e38c5a 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -20,6 +20,7 @@
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
+import android.os.health.HealthStatsParceler;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.SignalStrength;
@@ -125,4 +126,7 @@
void noteBleScanStarted(in WorkSource ws);
void noteBleScanStopped(in WorkSource ws);
void noteResetBleScan();
+
+ HealthStatsParceler takeUidSnapshot(int uid);
+ HealthStatsParceler[] takeUidSnapshots(in int[] uid);
}
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 2ea225f..ea62899 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -21,6 +21,7 @@
import android.app.ListFragment;
import android.content.Context;
import android.os.Bundle;
+import android.text.TextUtils;
import android.util.LocaleList;
import android.view.Menu;
import android.view.MenuInflater;
@@ -50,6 +51,11 @@
private Set<LocaleStore.LocaleInfo> mLocaleList;
private LocaleStore.LocaleInfo mParentLocale;
private boolean mTranslatedOnly = false;
+ private SearchView mSearchView = null;
+ private CharSequence mPreviousSearch = null;
+ private boolean mPreviousSearchHadFocus = false;
+ private int mFirstVisiblePosition = 0;
+ private int mTopDistance = 0;
/**
* Other classes can register to be notified when a locale was selected.
@@ -154,15 +160,35 @@
super.onResume();
if (mParentLocale != null) {
- this.getActivity().setTitle(mParentLocale.getFullNameNative());
+ getActivity().setTitle(mParentLocale.getFullNameNative());
} else {
- this.getActivity().setTitle(R.string.language_selection_title);
+ getActivity().setTitle(R.string.language_selection_title);
}
getListView().requestFocus();
}
@Override
+ public void onPause() {
+ super.onPause();
+
+ // Save search status
+ if (mSearchView != null) {
+ mPreviousSearchHadFocus = mSearchView.hasFocus();
+ mPreviousSearch = mSearchView.getQuery();
+ } else {
+ mPreviousSearchHadFocus = false;
+ mPreviousSearch = null;
+ }
+
+ // Save scroll position
+ final ListView list = getListView();
+ final View firstChild = list.getChildAt(0);
+ mFirstVisiblePosition = list.getFirstVisiblePosition();
+ mTopDistance = (firstChild == null) ? 0 : (firstChild.getTop() - list.getPaddingTop());
+ }
+
+ @Override
public void onListItemClick(ListView l, View v, int position, long id) {
final LocaleStore.LocaleInfo locale =
(LocaleStore.LocaleInfo) getListAdapter().getItem(position);
@@ -193,12 +219,27 @@
if (mParentLocale == null) {
inflater.inflate(R.menu.language_selection_list, menu);
- MenuItem mSearchMenuItem = menu.findItem(R.id.locale_search_menu);
- SearchView mSearchView = (SearchView) mSearchMenuItem.getActionView();
+ final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu);
+ mSearchView = (SearchView) searchMenuItem.getActionView();
mSearchView.setQueryHint(getText(R.string.search_language_hint));
mSearchView.setOnQueryTextListener(this);
- mSearchView.setQuery("", false /* submit */);
+
+ // Restore previous search status
+ if (!TextUtils.isEmpty(mPreviousSearch)) {
+ searchMenuItem.expandActionView();
+ mSearchView.setIconified(false);
+ mSearchView.setActivated(true);
+ if (mPreviousSearchHadFocus) {
+ mSearchView.requestFocus();
+ }
+ mSearchView.setQuery(mPreviousSearch, true /* submit */);
+ } else {
+ mSearchView.setQuery(null, false /* submit */);
+ }
+
+ // Restore previous scroll position
+ getListView().setSelectionFromTop(mFirstVisiblePosition, mTopDistance);
}
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index f04bcf2..90ee05e 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -505,7 +505,6 @@
final int numSubtypes = subtypes.size();
// Handle overridesImplicitlyEnabledSubtype mechanism.
- final String systemLanguage = systemLocales.get(0).getLanguage();
final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<>();
for (int i = 0; i < numSubtypes; ++i) {
// scan overriding implicitly enabled subtypes.
@@ -521,25 +520,20 @@
return new ArrayList<>(applicableModeAndSubtypesMap.values());
}
+ final HashMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
+ new HashMap<>();
final ArrayList<InputMethodSubtype> keyboardSubtypes = new ArrayList<>();
+
for (int i = 0; i < numSubtypes; ++i) {
final InputMethodSubtype subtype = subtypes.get(i);
- if (TextUtils.equals(SUBTYPE_MODE_KEYBOARD, subtype.getMode())) {
+ final String mode = subtype.getMode();
+ if (SUBTYPE_MODE_KEYBOARD.equals(mode)) {
keyboardSubtypes.add(subtype);
} else {
- final Locale locale = subtype.getLocaleObject();
- final String mode = subtype.getMode();
- // TODO: Take secondary system locales into consideration.
- if (locale != null && locale.equals(systemLanguage)) {
- final InputMethodSubtype applicableSubtype =
- applicableModeAndSubtypesMap.get(mode);
- // If more applicable subtypes are contained, skip.
- if (applicableSubtype != null) {
- if (systemLocale.equals(applicableSubtype.getLocaleObject())) continue;
- if (!systemLocale.equals(locale)) continue;
- }
- applicableModeAndSubtypesMap.put(mode, subtype);
+ if (!nonKeyboardSubtypesMap.containsKey(mode)) {
+ nonKeyboardSubtypesMap.put(mode, new ArrayList<>());
}
+ nonKeyboardSubtypesMap.get(mode).add(subtype);
}
}
@@ -578,7 +572,12 @@
}
}
- applicableSubtypes.addAll(applicableModeAndSubtypesMap.values());
+ // For each non-keyboard mode, extract subtypes with system locales.
+ for (final ArrayList<InputMethodSubtype> subtypeList : nonKeyboardSubtypesMap.values()) {
+ LocaleUtils.filterByLanguage(subtypeList, sSubtypeToLocale, systemLocales,
+ applicableSubtypes);
+ }
+
return applicableSubtypes;
}
diff --git a/core/java/com/android/internal/inputmethod/LocaleUtils.java b/core/java/com/android/internal/inputmethod/LocaleUtils.java
index 99bb4cb..2aa660e 100644
--- a/core/java/com/android/internal/inputmethod/LocaleUtils.java
+++ b/core/java/com/android/internal/inputmethod/LocaleUtils.java
@@ -18,15 +18,17 @@
import com.android.internal.annotations.VisibleForTesting;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.text.TextUtils;
+import android.icu.util.ULocale;
import android.util.LocaleList;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
-import java.util.Objects;
public final class LocaleUtils {
@@ -36,12 +38,138 @@
Locale get(@Nullable T source);
}
- @Nullable
- private static String getLanguage(@Nullable Locale locale) {
- if (locale == null) {
- return null;
+ /**
+ * Calculates a matching score for the single desired locale.
+ *
+ * @see LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])
+ *
+ * @param supported The locale supported by IME subtype.
+ * @param desired The locale preferred by user.
+ * @return A score based on the locale matching for the default subtype enabling.
+ */
+ @IntRange(from=1, to=3)
+ private static byte calculateMatchingSubScore(@NonNull final ULocale supported,
+ @NonNull final ULocale desired) {
+ // Assuming supported/desired is fully expanded.
+ if (supported.equals(desired)) {
+ return 3; // Exact match.
}
- return locale.getLanguage();
+
+ // Skip language matching since it was already done in calculateMatchingScore.
+
+ final String supportedScript = supported.getScript();
+ if (supportedScript.isEmpty() || !supportedScript.equals(desired.getScript())) {
+ // TODO: Need subscript matching. For example, Hanb should match with Bopo.
+ return 1;
+ }
+
+ final String supportedCountry = supported.getCountry();
+ if (supportedCountry.isEmpty() || !supportedCountry.equals(desired.getCountry())) {
+ return 2;
+ }
+
+ // Ignore others e.g. variants, extensions.
+ return 3;
+ }
+
+ /**
+ * Calculates a matching score for the desired locale list.
+ *
+ * <p>The supported locale gets a matching score of 3 if all language, script and country of the
+ * supported locale matches with the desired locale. The supported locale gets a matching
+ * score of 2 if the language and script of the supported locale matches with the desired
+ * locale. The supported locale gets a matching score of 1 if only language of the supported
+ * locale matches with the desired locale. The supported locale gets a matching score of 0 if
+ * the language of the supported locale doesn't match with the desired locale.</p>
+ *
+ * @param supported The locale supported by IME subtyle.
+ * @param desired The locale list preferred by user. Typically system locale list.
+ * @param out The output buffer to be stored the individual score for the desired language list.
+ * The length of {@code out} must be same as the length of {@code desired} language list.
+ * @return {@code false} if supported locale doesn't match with any desired locale list.
+ * Otherwise {@code true}.
+ */
+ private static boolean calculateMatchingScore(@NonNull final ULocale supported,
+ @NonNull final LocaleList desired, @NonNull byte[] out) {
+ if (desired.isEmpty()) {
+ return false;
+ }
+
+ boolean allZeros = true;
+ final int N = desired.size();
+ for (int i = 0; i < N; ++i) {
+ final Locale locale = desired.get(i);
+
+ if (!locale.getLanguage().equals(supported.getLanguage())) {
+ // TODO: cache the result of addLikelySubtags if it is slow.
+ out[i] = 0;
+ } else {
+ out[i] = calculateMatchingSubScore(
+ supported, ULocale.addLikelySubtags(ULocale.forLocale(locale)));
+ if (allZeros && out[i] != 0) {
+ allZeros = false;
+ }
+ }
+ }
+ return !allZeros;
+ }
+
+ private static final class ScoreEntry implements Comparable<ScoreEntry> {
+ public int mIndex = -1;
+ @NonNull public final byte[] mScore; // matching score of the i-th system languages.
+
+ ScoreEntry(@NonNull byte[] score, int index) {
+ mScore = new byte[score.length];
+ set(score, index);
+ }
+
+ private void set(@NonNull byte[] score, int index) {
+ for (int i = 0; i < mScore.length; ++i) {
+ mScore[i] = score[i];
+ }
+ mIndex = index;
+ }
+
+ /**
+ * Update score and index if the given score is better than this.
+ */
+ public void updateIfBetter(@NonNull byte[] score, int index) {
+ if (compare(mScore, score) == -1) { // mScore < score
+ set(score, index);
+ }
+ }
+
+ /**
+ * Provides comaprison for bytes[].
+ *
+ * <p> Comparison does as follows. If the first value of {@code left} is larger than the
+ * first value of {@code right}, {@code left} is large than {@code right}. If the first
+ * value of {@code left} is less than the first value of {@code right}, {@code left} is less
+ * than {@code right}. If the first value of {@code left} and the first value of
+ * {@code right} is equal, do the same comparison to the next value. Finally if all values
+ * in {@code left} and {@code right} are equal, {@code left} and {@code right} is equal.</p>
+ *
+ * @param left The length must be equal to {@code right}.
+ * @param right The length must be equal to {@code left}.
+ * @return 1 if {@code left} is larger than {@code right}. -1 if {@code left} is less than
+ * {@code right}. 0 if {@code left} and {@code right} is equal.
+ */
+ @IntRange(from=-1, to=1)
+ private static int compare(@NonNull byte[] left, @NonNull byte[] right) {
+ for (int i = 0; i < left.length; ++i) {
+ if (left[i] > right[i]) {
+ return 1;
+ } else if (left[i] < right[i]) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public int compareTo(final ScoreEntry other) {
+ return -1 * compare(mScore, other.mScore); // Order by descending order.
+ }
}
/**
@@ -52,14 +180,8 @@
* {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLanguages},
* this method tries to copy at most one English locale, at most one Japanese, and at most one
* French locale from {@code source} to {@code dest}. Here the best matching English locale
- * will be searched from {@code source} as follows.
- * <ol>
- * <li>The first instance in {@code sources} that exactly matches {@code "en-GB"}</li>
- * <li>The first instance in {@code sources} that exactly matches {@code "en-AU"}</li>
- * <li>The first instance in {@code sources} that exactly matches {@code "en-IN"}</li>
- * <li>The first instance in {@code sources} that partially matches {@code "en"}</li>
- * </ol>
- * <p>Then this method iterates the same algorithm for Japanese then French.</p>
+ * will be searched from {@code source} based on matching score. For the score design, see
+ * {@link LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])}</p>
*
* @param sources Source items to be filtered.
* @param extractor Type converter from the source items to {@link Locale} object.
@@ -74,69 +196,31 @@
@NonNull LocaleExtractor<T> extractor,
@NonNull LocaleList preferredLanguages,
@NonNull ArrayList<T> dest) {
- final Locale[] availableLocales = new Locale[sources.size()];
- for (int i = 0; i < availableLocales.length; ++i) {
- availableLocales[i] = extractor.get(sources.get(i));
- }
- final Locale[] sortedPreferredLanguages = new Locale[preferredLanguages.size()];
- if (sortedPreferredLanguages.length > 0) {
- int nextIndex = 0;
- final int N = preferredLanguages.size();
- languageLoop:
- for (int i = 0; i < N; ++i) {
- final String language = getLanguage(preferredLanguages.get(i));
- for (int j = 0; j < nextIndex; ++j) {
- if (TextUtils.equals(getLanguage(sortedPreferredLanguages[j]), language)) {
- continue languageLoop;
- }
- }
- for (int j = i; j < N; ++j) {
- final Locale locale = preferredLanguages.get(j);
- if (TextUtils.equals(language, getLanguage(locale))) {
- sortedPreferredLanguages[nextIndex] = locale;
- ++nextIndex;
- }
- }
+ final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
+ final byte[] score = new byte[preferredLanguages.size()];
+
+ final int sourceSize = sources.size();
+ for (int i = 0; i < sourceSize; ++i) {
+ final Locale locale = extractor.get(sources.get(i));
+ if (locale == null ||
+ !calculateMatchingScore(ULocale.addLikelySubtags(ULocale.forLocale(locale)),
+ preferredLanguages, score)) {
+ continue;
+ }
+
+ final String lang = locale.getLanguage();
+ final ScoreEntry bestScore = scoreboard.get(lang);
+ if (bestScore == null) {
+ scoreboard.put(lang, new ScoreEntry(score, i));
+ } else {
+ bestScore.updateIfBetter(score, i);
}
}
-
- for (int languageIndex = 0; languageIndex < sortedPreferredLanguages.length;) {
- // Finding the range.
- final String language = getLanguage(sortedPreferredLanguages[languageIndex]);
- int nextLanguageIndex = languageIndex;
- for (; nextLanguageIndex < sortedPreferredLanguages.length; ++nextLanguageIndex) {
- final Locale locale = sortedPreferredLanguages[nextLanguageIndex];
- if (!TextUtils.equals(getLanguage(locale), language)) {
- break;
- }
- }
-
- // Check exact match
- boolean found = false;
- for (int i = languageIndex; !found && i < nextLanguageIndex; ++i) {
- final Locale locale = sortedPreferredLanguages[i];
- for (int j = 0; j < availableLocales.length; ++j) {
- if (!Objects.equals(locale, availableLocales[j])) {
- continue;
- }
- dest.add(sources.get(j));
- found = true;
- break;
- }
- }
-
- if (!found) {
- // No exact match. Use language match.
- for (int j = 0; j < availableLocales.length; ++j) {
- if (!TextUtils.equals(language, getLanguage(availableLocales[j]))) {
- continue;
- }
- dest.add(sources.get(j));
- break;
- }
- }
- languageIndex = nextLanguageIndex;
+ final ScoreEntry[] result = scoreboard.values().toArray(new ScoreEntry[scoreboard.size()]);
+ Arrays.sort(result);
+ for (final ScoreEntry entry : result) {
+ dest.add(sources.get(entry.mIndex));
}
}
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 60c9e14..c484121 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5300,6 +5300,12 @@
}
@Override
+ public Timer getProcessStateTimer(int state) {
+ if (state < 0 || state >= NUM_PROCESS_STATE) return null;
+ return mProcessStateTimer[state];
+ }
+
+ @Override
public Timer getVibratorOnTimer() {
return mVibratorOnTimer;
}
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index 84d0fc7..9907ea9 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -279,6 +279,26 @@
}
/**
+ * Cycles through all non-dismiss targets with a stepping of {@param increment}. It moves left
+ * if {@param increment} is negative and moves right otherwise.
+ */
+ public SnapTarget cycleNonDismissTarget(SnapTarget snapTarget, int increment) {
+ int index = mTargets.indexOf(snapTarget);
+ if (index != -1) {
+ SnapTarget newTarget = mTargets.get((index + mTargets.size() + increment)
+ % mTargets.size());
+ if (newTarget == mDismissStartTarget) {
+ return mLastSplitTarget;
+ } else if (newTarget == mDismissEndTarget) {
+ return mFirstSplitTarget;
+ } else {
+ return newTarget;
+ }
+ }
+ return snapTarget;
+ }
+
+ /**
* Represents a snap target for the divider.
*/
public static class SnapTarget {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 8026949..ee73b90 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -27,6 +27,8 @@
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
/**
@@ -126,6 +128,13 @@
/**
* Checks if given array is null or has zero elements.
*/
+ public static boolean isEmpty(@Nullable List<?> array) {
+ return array == null || array.isEmpty();
+ }
+
+ /**
+ * Checks if given array is null or has zero elements.
+ */
public static <T> boolean isEmpty(@Nullable T[] array) {
return array == null || array.length == 0;
}
@@ -469,4 +478,48 @@
}
return !diff;
}
+
+ /**
+ * Removes elements that match the predicate in an efficient way that alters the order of
+ * elements in the collection. This should only be used if order is not important.
+ * @param collection The ArrayList from which to remove elements.
+ * @param predicate The predicate that each element is tested against.
+ * @return the number of elements removed.
+ */
+ public static <T> int unstableRemoveIf(@Nullable ArrayList<T> collection,
+ @NonNull java.util.function.Predicate<T> predicate) {
+ if (collection == null) {
+ return 0;
+ }
+
+ final int size = collection.size();
+ int leftIdx = 0;
+ int rightIdx = size - 1;
+ while (leftIdx <= rightIdx) {
+ // Find the next element to remove moving left to right.
+ while (leftIdx < size && !predicate.test(collection.get(leftIdx))) {
+ leftIdx++;
+ }
+
+ // Find the next element to keep moving right to left.
+ while (rightIdx > leftIdx && predicate.test(collection.get(rightIdx))) {
+ rightIdx--;
+ }
+
+ if (leftIdx >= rightIdx) {
+ // Done.
+ break;
+ }
+
+ Collections.swap(collection, leftIdx, rightIdx);
+ leftIdx++;
+ rightIdx--;
+ }
+
+ // leftIdx is now at the end.
+ for (int i = size - 1; i >= leftIdx; i--) {
+ collection.remove(i);
+ }
+ return size - leftIdx;
+ }
}
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index 398bbe7..baf3188 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -147,7 +147,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return (who == mBackground && !mIsSplit) || (who == mStackedBackground && mIsStacked) ||
(who == mSplitBackground && mIsSplit) || super.verifyDrawable(who);
}
diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
index c4ed2e1..78c5e34 100644
--- a/core/java/com/android/internal/widget/ImageFloatingTextView.java
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -65,6 +65,8 @@
.setTextDirection(getTextDirectionHeuristic())
.setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
.setIncludePad(getIncludeFontPadding())
+ .setEllipsize(shouldEllipsize ? effectiveEllipsize : null)
+ .setEllipsizedWidth(ellipsisWidth)
.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
// we set the endmargin on the first 2 lines. this works just in our case but that's
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 795012d..9d14478 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -887,8 +887,7 @@
* @return true if device encryption is enabled
*/
public static boolean isDeviceEncryptionEnabled() {
- final String status = SystemProperties.get("ro.crypto.state", "unsupported");
- return "encrypted".equalsIgnoreCase(status);
+ return StorageManager.isEncrypted();
}
/**
@@ -896,7 +895,7 @@
* @return true if device is file encrypted
*/
public static boolean isFileEncryptionEnabled() {
- return StorageManager.isFileBasedEncryptionEnabled();
+ return StorageManager.isFileEncryptedNativeOrEmulated();
}
/**
diff --git a/core/java/com/android/internal/widget/MediaNotificationView.java b/core/java/com/android/internal/widget/MediaNotificationView.java
index 6bba1b3..f63afad 100644
--- a/core/java/com/android/internal/widget/MediaNotificationView.java
+++ b/core/java/com/android/internal/widget/MediaNotificationView.java
@@ -89,36 +89,50 @@
int topMargin = getMeasuredHeight() - mRightIcon.getMeasuredHeight()
- iconParams.bottomMargin;
// If the topMargin is high enough we can also remove the header constraint!
+ boolean reMeasure = false;
if (!hasIcon || topMargin >= mImageMinTopMargin) {
- resetHeaderIndention();
+ reMeasure = resetHeaderIndention();
} else {
int paddingEnd = mNotificationContentImageMarginEnd;
ViewGroup.MarginLayoutParams headerParams =
(MarginLayoutParams) mHeader.getLayoutParams();
- headerParams.setMarginEnd(mRightIcon.getMeasuredWidth() + iconParams.getMarginEnd());
- if (mHeader.getPaddingEnd() != paddingEnd) {
- mHeader.setPadding(
- isLayoutRtl() ? paddingEnd : mHeader.getPaddingLeft(),
- mHeader.getPaddingTop(),
- isLayoutRtl() ? mHeader.getPaddingLeft() : paddingEnd,
- mHeader.getPaddingBottom());
+ int newMarginEnd = mRightIcon.getMeasuredWidth() + iconParams.getMarginEnd();
+ if (headerParams.getMarginEnd() != newMarginEnd) {
+ headerParams.setMarginEnd(newMarginEnd);
mHeader.setLayoutParams(headerParams);
+ reMeasure = true;
}
+ if (mHeader.getPaddingEnd() != paddingEnd) {
+ mHeader.setPaddingRelative(mHeader.getPaddingStart(),
+ mHeader.getPaddingTop(),
+ paddingEnd,
+ mHeader.getPaddingBottom());
+ reMeasure = true;
+ }
+ }
+ if (reMeasure) {
+ measureChildWithMargins(mHeader, widthMeasureSpec, 0, heightMeasureSpec, 0);
}
}
- private void resetHeaderIndention() {
+ private boolean resetHeaderIndention() {
+ boolean remeasure = false;
if (mHeader.getPaddingEnd() != mNotificationContentMarginEnd) {
- ViewGroup.MarginLayoutParams headerParams =
- (MarginLayoutParams) mHeader.getLayoutParams();
- headerParams.setMarginEnd(0);
- mHeader.setPadding(
- isLayoutRtl() ? mNotificationContentMarginEnd : mHeader.getPaddingLeft(),
+ mHeader.setPaddingRelative(mHeader.getPaddingStart(),
mHeader.getPaddingTop(),
- isLayoutRtl() ? mHeader.getPaddingLeft() : mNotificationContentMarginEnd,
+ mNotificationContentMarginEnd,
mHeader.getPaddingBottom());
- mHeader.setLayoutParams(headerParams);
+ remeasure = true;
}
+ ViewGroup.MarginLayoutParams headerParams =
+ (MarginLayoutParams) mHeader.getLayoutParams();
+ headerParams.setMarginEnd(0);
+ if (headerParams.getMarginEnd() != 0) {
+ headerParams.setMarginEnd(0);
+ mHeader.setLayoutParams(headerParams);
+ remeasure = true;
+ }
+ return remeasure;
}
public MediaNotificationView(Context context, AttributeSet attrs, int defStyleAttr,
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 948a6bb..277fafd 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -17,6 +17,7 @@
package com.android.internal.widget;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -746,7 +747,7 @@
}
@Override
- protected boolean verifyDrawable(Drawable who) {
+ protected boolean verifyDrawable(@NonNull Drawable who) {
return super.verifyDrawable(who) || who == mMarginDrawable;
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index ab75b7c..6d6c162 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.storage.StorageManager;
import android.provider.Downloads;
import android.util.AtomicFile;
import android.util.Slog;
@@ -143,8 +144,7 @@
HashMap<String, Long> timestamps = readTimestamps();
if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
- if ("encrypted".equals(SystemProperties.get("ro.crypto.state"))
- && "trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))) {
+ if (StorageManager.inCryptKeeperBounce()) {
// Encrypted, first boot to get PIN/pattern/password so data is tmpfs
// Don't set ro.runtime.firstboot so that we will do this again
// when data is properly mounted
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index cee98b8..181ed51 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -70,6 +70,8 @@
private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
private static final String WALLPAPER_INFO_KEY = WallpaperBackupHelper.WALLPAPER_INFO_KEY;
+ private WallpaperBackupHelper mWallpaperHelper = null;
+
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
@@ -121,13 +123,16 @@
@Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
- // On restore, we also support a previous data schema "system_files"
- addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this,
+ mWallpaperHelper = new WallpaperBackupHelper(this,
new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO },
- new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} ));
+ new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} );
+ addHelper(WALLPAPER_HELPER, mWallpaperHelper);
+
+ // On restore, we also support a previous data schema "system_files"
addHelper("system_files", new WallpaperBackupHelper(this,
new String[] { WALLPAPER_IMAGE },
new String[] { WALLPAPER_IMAGE_KEY} ));
+
addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
@@ -202,4 +207,9 @@
}
}
}
+
+ @Override
+ public void onRestoreFinished() {
+ mWallpaperHelper.onRestoreFinished();
+ }
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 816a2c4..623b603 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -7,7 +7,7 @@
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-non-virtual-dtor
LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
-#LOCAL_CFLAGS += -DHWUI_NEW_OPS
+LOCAL_CFLAGS += -DHWUI_NEW_OPS
LOCAL_CPPFLAGS += -Wno-conversion-null
ifeq ($(TARGET_ARCH), arm)
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2a04526..798a6de 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1184,8 +1184,7 @@
void** args = (void**) malloc(3 * sizeof(void*)); // javaThreadShell must free
int result;
- if (!threadName)
- threadName = "unnamed thread";
+ LOG_ALWAYS_FATAL_IF(threadName == nullptr, "threadName not provided to javaCreateThreadEtc");
args[0] = (void*) entryFunction;
args[1] = userData;
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 29c1075..fb9b1e5 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -201,6 +201,26 @@
const unsigned int mSize;
};
+// Necessary for decodes when the native decoder cannot scale to appropriately match the sampleSize
+// (for example, RAW). If the sampleSize divides evenly into the dimension, we require that the
+// scale matches exactly. If sampleSize does not divide evenly, we allow the decoder to choose how
+// best to round.
+static bool needsFineScale(const int fullSize, const int decodedSize, const int sampleSize) {
+ if (fullSize % sampleSize == 0 && fullSize / sampleSize != decodedSize) {
+ return true;
+ } else if ((fullSize / sampleSize + 1) != decodedSize &&
+ (fullSize / sampleSize) != decodedSize) {
+ return true;
+ }
+ return false;
+}
+
+static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize,
+ const int sampleSize) {
+ return needsFineScale(fullSize.width(), decodedSize.width(), sampleSize) ||
+ needsFineScale(fullSize.height(), decodedSize.height(), sampleSize);
+}
+
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
// This function takes ownership of the input stream. Since the SkAndroidCodec
// will take ownership of the stream, we don't necessarily need to take ownership
@@ -250,7 +270,6 @@
}
}
}
- const bool willScale = scale != 1.0f;
// Create the codec.
NinePatchPeeker peeker;
@@ -269,15 +288,28 @@
prefColorType = kN32_SkColorType;
}
- // Determine the output size and return if the client only wants the size.
+ // Determine the output size.
SkISize size = codec->getSampledDimensions(sampleSize);
+
+ int scaledWidth = size.width();
+ int scaledHeight = size.height();
+ bool willScale = false;
+
+ // Apply a fine scaling step if necessary.
+ if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) {
+ willScale = true;
+ scaledWidth = codec->getInfo().width() / sampleSize;
+ scaledHeight = codec->getInfo().height() / sampleSize;
+ }
+
+ // Set the options and return if the client only wants the size.
if (options != NULL) {
jstring mimeType = encodedFormatToString(env, codec->getEncodedFormat());
if (env->ExceptionCheck()) {
return nullObjectReturn("OOM in encodedFormatToString()");
}
- env->SetIntField(options, gOptions_widthFieldID, size.width());
- env->SetIntField(options, gOptions_heightFieldID, size.height());
+ env->SetIntField(options, gOptions_widthFieldID, scaledWidth);
+ env->SetIntField(options, gOptions_heightFieldID, scaledHeight);
env->SetObjectField(options, gOptions_mimeFieldID, mimeType);
if (onlyDecodeSize) {
@@ -285,6 +317,13 @@
}
}
+ // Scale is necessary due to density differences.
+ if (scale != 1.0f) {
+ willScale = true;
+ scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f);
+ scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f);
+ }
+
android::Bitmap* reuseBitmap = nullptr;
unsigned int existingBufferSize = 0;
if (javaBitmap != NULL) {
@@ -381,13 +420,6 @@
return nullObjectReturn("codec->getAndroidPixels() failed.");
}
- int scaledWidth = size.width();
- int scaledHeight = size.height();
- if (willScale) {
- scaledWidth = int(scaledWidth * scale + 0.5f);
- scaledHeight = int(scaledHeight * scale + 0.5f);
- }
-
jbyteArray ninePatchChunk = NULL;
if (peeker.mPatch != NULL) {
if (willScale) {
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index 7a13fe4..5d496e5 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -94,8 +94,6 @@
SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
&(page->mContentRect));
- canvas->clipRect(page->mContentRect);
- canvas->translate(page->mContentRect.left(), page->mContentRect.top());
canvas->drawPicture(page->mPicture);
document->endPage();
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index cb0abb6..d80d8f2 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "DngCreator_JNI"
#include <inttypes.h>
#include <string.h>
@@ -80,6 +80,13 @@
return nullptr; \
}
+#define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \
+ if (expr) { \
+ jniThrowExceptionFmt(jnienv, "java/lang/IllegalArgumentException", \
+ "Invalid metadata for tag %s (%x)", (writer)->getTagName(tagId), (tagId)); \
+ return nullptr; \
+ }
+
#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
@@ -195,8 +202,8 @@
NativeContext::NativeContext(const CameraMetadata& characteristics, const CameraMetadata& result) :
mCharacteristics(std::make_shared<CameraMetadata>(characteristics)),
mResult(std::make_shared<CameraMetadata>(result)), mThumbnailWidth(0),
- mThumbnailHeight(0), mOrientation(0), mThumbnailSet(false), mGpsSet(false),
- mDescriptionSet(false), mCaptureTimeSet(false) {}
+ mThumbnailHeight(0), mOrientation(TAG_ORIENTATION_UNKNOWN), mThumbnailSet(false),
+ mGpsSet(false), mDescriptionSet(false), mCaptureTimeSet(false) {}
NativeContext::~NativeContext() {}
@@ -1096,7 +1103,7 @@
{
// Set orientation
- uint16_t orientation = 1; // Normal
+ uint16_t orientation = TAG_ORIENTATION_NORMAL;
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
env, TAG_ORIENTATION, writer);
}
@@ -1138,12 +1145,27 @@
}
{
- // Set blacklevel tags
+ // Set blacklevel tags, using dynamic black level if available
camera_metadata_entry entry =
- characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
- BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_BLACKLEVEL, writer);
- const uint32_t* blackLevel = reinterpret_cast<const uint32_t*>(entry.data.i32);
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, entry.count, blackLevel,
+ results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
+ uint32_t blackLevelRational[8] = {0};
+ if (entry.count != 0) {
+ BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
+ for (size_t i = 0; i < entry.count; i++) {
+ blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.f[i] * 100);
+ blackLevelRational[i * 2 + 1] = 100;
+ }
+ } else {
+ // Fall back to static black level which is guaranteed
+ entry = characteristics.find(ANDROID_SENSOR_BLACK_LEVEL_PATTERN);
+ BAIL_IF_EXPR_RET_NULL_SP(entry.count != 4, env, TAG_BLACKLEVEL, writer);
+ for (size_t i = 0; i < entry.count; i++) {
+ blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
+ blackLevelRational[i * 2 + 1] = 1;
+ }
+
+ }
+ BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, 4, blackLevelRational,
TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
uint16_t repeatDim[2] = {2, 2};
@@ -1770,6 +1792,8 @@
{
// Set up orientation tags.
+ // Note: There's only one orientation field for the whole file, in IFD0
+ // The main image and any thumbnails therefore have the same orientation.
uint16_t orientation = nativeContext->getOrientation();
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_ORIENTATION, 1, &orientation, TIFF_IFD_0),
env, TAG_ORIENTATION, writer);
@@ -1851,7 +1875,6 @@
}
Vector<uint16_t> tagsToMove;
- tagsToMove.add(TAG_ORIENTATION);
tagsToMove.add(TAG_NEWSUBFILETYPE);
tagsToMove.add(TAG_ACTIVEAREA);
tagsToMove.add(TAG_BITSPERSAMPLE);
@@ -1882,12 +1905,6 @@
return nullptr;
}
- // Make sure both IFDs get the same orientation tag
- sp<TiffEntry> orientEntry = writer->getEntry(TAG_ORIENTATION, TIFF_IFD_SUB1);
- if (orientEntry.get() != nullptr) {
- writer->addEntry(orientEntry, TIFF_IFD_0);
- }
-
// Setup thumbnail tags
{
@@ -1913,8 +1930,10 @@
{
// Set bits per sample
- uint16_t bits = BITS_PER_RGB_SAMPLE;
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BITSPERSAMPLE, 1, &bits, TIFF_IFD_0),
+ uint16_t bits[SAMPLES_PER_RGB_PIXEL];
+ for (int i = 0; i < SAMPLES_PER_RGB_PIXEL; i++) bits[i] = BITS_PER_RGB_SAMPLE;
+ BAIL_IF_INVALID_RET_NULL_SP(
+ writer->addEntry(TAG_BITSPERSAMPLE, SAMPLES_PER_RGB_PIXEL, bits, TIFF_IFD_0),
env, TAG_BITSPERSAMPLE, writer);
}
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index f1ea7ec..80f9d57 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -29,6 +29,7 @@
#include <gui/Surface.h>
#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
#include <ui/GraphicBuffer.h>
#include <system/window.h>
#include <hardware/camera3.h>
@@ -93,27 +94,17 @@
cStep, yStride, cStride);
}
-static status_t configureSurface(const sp<ANativeWindow>& anw,
- int32_t width,
- int32_t height,
- int32_t pixelFmt,
- int32_t maxBufferSlack) {
+static status_t connectSurface(const sp<Surface>& surface, int32_t maxBufferSlack) {
status_t err = NO_ERROR;
- err = native_window_set_buffers_dimensions(anw.get(), width, height);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to set native window buffer dimensions, error %s (%d).", __FUNCTION__,
+
+ err = surface->connect(NATIVE_WINDOW_API_CAMERA, /*listener*/NULL);
+ if (err != OK) {
+ ALOGE("%s: Unable to connect to surface, error %s (%d).", __FUNCTION__,
strerror(-err), err);
return err;
}
- err = native_window_set_buffers_format(anw.get(), pixelFmt);
- if (err != NO_ERROR) {
- ALOGE("%s: Failed to set native window buffer format, error %s (%d).", __FUNCTION__,
- strerror(-err), err);
- return err;
- }
-
- err = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
+ err = native_window_set_usage(surface.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
if (err != NO_ERROR) {
ALOGE("%s: Failed to set native window usage flag, error %s (%d).", __FUNCTION__,
strerror(-err), err);
@@ -121,19 +112,17 @@
}
int minUndequeuedBuffers;
- err = anw.get()->query(anw.get(),
- NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers);
+ err = static_cast<ANativeWindow*>(surface.get())->query(surface.get(),
+ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);
if (err != NO_ERROR) {
ALOGE("%s: Failed to get native window min undequeued buffers, error %s (%d).",
__FUNCTION__, strerror(-err), err);
return err;
}
- ALOGV("%s: Setting buffer count to %d, size to (%dx%d), fmt (0x%x)", __FUNCTION__,
- maxBufferSlack + 1 + minUndequeuedBuffers,
- width, height, pixelFmt);
- err = native_window_set_buffer_count(anw.get(), maxBufferSlack + 1 + minUndequeuedBuffers);
+ ALOGV("%s: Setting buffer count to %d", __FUNCTION__,
+ maxBufferSlack + 1 + minUndequeuedBuffers);
+ err = native_window_set_buffer_count(surface.get(), maxBufferSlack + 1 + minUndequeuedBuffers);
if (err != NO_ERROR) {
ALOGE("%s: Failed to set native window buffer count, error %s (%d).", __FUNCTION__,
strerror(-err), err);
@@ -509,6 +498,26 @@
return usage;
}
+static jint LegacyCameraDevice_nativeDisconnectSurface(JNIEnv* env, jobject thiz,
+ jobject surface) {
+ ALOGV("nativeDisconnectSurface");
+ if (surface == nullptr) return NO_ERROR;
+
+ sp<ANativeWindow> anw;
+ if ((anw = getNativeWindow(env, surface)) == NULL) {
+ ALOGV("Buffer queue has already been abandoned.");
+ return NO_ERROR;
+ }
+
+ status_t err = native_window_api_disconnect(anw.get(), NATIVE_WINDOW_API_CAMERA);
+ if(err != NO_ERROR) {
+ jniThrowException(env, "Ljava/lang/UnsupportedOperationException;",
+ "Error while disconnecting surface");
+ return err;
+ }
+ return NO_ERROR;
+}
+
static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz,
jobject surfaceTexture, jintArray dimens) {
ALOGV("nativeDetectTextureDimens");
@@ -540,15 +549,14 @@
return NO_ERROR;
}
-static jint LegacyCameraDevice_nativeConfigureSurface(JNIEnv* env, jobject thiz, jobject surface,
- jint width, jint height, jint pixelFormat) {
- ALOGV("nativeConfigureSurface");
- sp<ANativeWindow> anw;
- if ((anw = getNativeWindow(env, surface)) == NULL) {
- ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+static jint LegacyCameraDevice_nativeConnectSurface(JNIEnv* env, jobject thiz, jobject surface) {
+ ALOGV("nativeConnectSurface");
+ sp<Surface> s;
+ if ((s = getSurface(env, surface)) == NULL) {
+ ALOGE("%s: Could not retrieve surface.", __FUNCTION__);
return BAD_VALUE;
}
- status_t err = configureSurface(anw, width, height, pixelFormat, CAMERA_DEVICE_BUFFER_SLACK);
+ status_t err = connectSurface(s, CAMERA_DEVICE_BUFFER_SLACK);
if (err != NO_ERROR) {
ALOGE("%s: Error while configuring surface %s (%d).", __FUNCTION__, strerror(-err), err);
return err;
@@ -740,9 +748,9 @@
{ "nativeDetectSurfaceDimens",
"(Landroid/view/Surface;[I)I",
(void *)LegacyCameraDevice_nativeDetectSurfaceDimens },
- { "nativeConfigureSurface",
- "(Landroid/view/Surface;III)I",
- (void *)LegacyCameraDevice_nativeConfigureSurface },
+ { "nativeConnectSurface",
+ "(Landroid/view/Surface;)I",
+ (void *)LegacyCameraDevice_nativeConnectSurface },
{ "nativeProduceFrame",
"(Landroid/view/Surface;[BIII)I",
(void *)LegacyCameraDevice_nativeProduceFrame },
@@ -773,6 +781,9 @@
{ "nativeSetScalingMode",
"(Landroid/view/Surface;I)I",
(void *)LegacyCameraDevice_nativeSetScalingMode },
+ { "nativeDisconnectSurface",
+ "(Landroid/view/Surface;)I",
+ (void *)LegacyCameraDevice_nativeDisconnectSurface },
};
// Get all the required offsets in java class and register native functions
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 660cbdc..302cf63 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -541,7 +541,6 @@
return;
}
//ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
- lpTrack->stop();
// delete the JNI data
AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index 5751add..156e7bd 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -888,9 +888,69 @@
static jint
android_glCreateShaderProgramv
(JNIEnv *_env, jobject _this, jint type, jobjectArray strings) {
+ jint _exception = 0;
+ const char * _exceptionType = NULL;
+ const char * _exceptionMessage = NULL;
+ GLsizei _count;
+ const GLchar** _strings = NULL;
+ jstring* _jstrings = NULL;
+ GLuint _returnValue = 0;
- jniThrowException(_env, "java/lang/UnsupportedOperationException", "not yet implemented");
- return 0;
+ if (!strings) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "strings == null";
+ goto exit;
+ }
+
+ _count = _env->GetArrayLength(strings);
+
+ _strings = (const GLchar**) calloc(_count, sizeof(const GLchar*));
+ if (!_strings) {
+ _exception = 1;
+ _exceptionType = "java/lang/OutOfMemoryError";
+ _exceptionMessage = "out of memory";
+ goto exit;
+ }
+
+ _jstrings = (jstring*) calloc(_count, sizeof(jstring));
+ if (!_jstrings) {
+ _exception = 1;
+ _exceptionType = "java/lang/OutOfMemoryError";
+ _exceptionMessage = "out of memory";
+ goto exit;
+ }
+
+ for(int i = 0; i < _count; i++) {
+ _jstrings[i] = (jstring) _env->GetObjectArrayElement(strings, i);
+ if (!_jstrings[i]) {
+ _exception = 1;
+ _exceptionType = "java/lang/IllegalArgumentException";
+ _exceptionMessage = "strings == null";
+ goto exit;
+ }
+ _strings[i] = _env->GetStringUTFChars(_jstrings[i], 0);
+ }
+
+ _returnValue = glCreateShaderProgramv((GLenum)type, _count, _strings);
+exit:
+ if (_strings && _jstrings) {
+ for(int i = 0; i < _count; i++) {
+ if (_strings[i] && _jstrings[i]) {
+ _env->ReleaseStringUTFChars(_jstrings[i], _strings[i]);
+ }
+ }
+ }
+ if (_strings) {
+ free(_strings);
+ }
+ if (_jstrings) {
+ free(_jstrings);
+ }
+ if (_exception) {
+ jniThrowException(_env, _exceptionType, _exceptionMessage);
+ }
+ return (jint)_returnValue;
}
/* void glBindProgramPipeline ( GLuint pipeline ) */
static void
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index ee8fb19..f7a5e8a 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "Process"
+// To make sure cpu_set_t is included from sched.h
+#define _GNU_SOURCE 1
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -288,6 +290,139 @@
return (int) sp;
}
+#ifdef ENABLE_CPUSETS
+/** Sample CPUset list format:
+ * 0-3,4,6-8
+ */
+static void parse_cpuset_cpus(char *cpus, cpu_set_t *cpu_set) {
+ unsigned int start, end, matched, i;
+ char *cpu_range = strtok(cpus, ",");
+ while (cpu_range != NULL) {
+ start = end = 0;
+ matched = sscanf(cpu_range, "%u-%u", &start, &end);
+ cpu_range = strtok(NULL, ",");
+ if (start >= CPU_SETSIZE) {
+ ALOGE("parse_cpuset_cpus: ignoring CPU number larger than %d.", CPU_SETSIZE);
+ continue;
+ } else if (end >= CPU_SETSIZE) {
+ ALOGE("parse_cpuset_cpus: ignoring CPU numbers larger than %d.", CPU_SETSIZE);
+ end = CPU_SETSIZE - 1;
+ }
+ if (matched == 1) {
+ CPU_SET(start, cpu_set);
+ } else if (matched == 2) {
+ for (i = start; i <= end; i++) {
+ CPU_SET(i, cpu_set);
+ }
+ } else {
+ ALOGE("Failed to match cpus");
+ }
+ }
+ return;
+}
+
+/**
+ * Stores the CPUs assigned to the cpuset corresponding to the
+ * SchedPolicy in the passed in cpu_set.
+ */
+static void get_cpuset_cores_for_policy(SchedPolicy policy, cpu_set_t *cpu_set)
+{
+ FILE *file;
+ const char *filename;
+
+ CPU_ZERO(cpu_set);
+
+ switch (policy) {
+ case SP_BACKGROUND:
+ filename = "/dev/cpuset/background/cpus";
+ break;
+ case SP_FOREGROUND:
+ case SP_AUDIO_APP:
+ case SP_AUDIO_SYS:
+ filename = "/dev/cpuset/foreground/cpus";
+ break;
+ case SP_TOP_APP:
+ filename = "/dev/cpuset/top-app/cpus";
+ break;
+ default:
+ filename = NULL;
+ }
+
+ if (!filename) return;
+
+ file = fopen(filename, "re");
+ if (file != NULL) {
+ // Parse cpus string
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t num_read = getline(&line, &len, file);
+ fclose (file);
+ if (num_read > 0) {
+ parse_cpuset_cpus(line, cpu_set);
+ } else {
+ ALOGE("Failed to read %s", filename);
+ }
+ free(line);
+ }
+ return;
+}
+#endif
+
+
+/**
+ * Determine CPU cores exclusively assigned to the
+ * cpuset corresponding to the SchedPolicy and store
+ * them in the passed in cpu_set_t
+ */
+void get_exclusive_cpuset_cores(SchedPolicy policy, cpu_set_t *cpu_set) {
+#ifdef ENABLE_CPUSETS
+ int i;
+ cpu_set_t tmp_set;
+ get_cpuset_cores_for_policy(policy, cpu_set);
+ for (i = 0; i < SP_CNT; i++) {
+ if ((SchedPolicy) i == policy) continue;
+ get_cpuset_cores_for_policy((SchedPolicy)i, &tmp_set);
+ // First get cores exclusive to one set or the other
+ CPU_XOR(&tmp_set, cpu_set, &tmp_set);
+ // Then get the ones only in cpu_set
+ CPU_AND(cpu_set, cpu_set, &tmp_set);
+ }
+#else
+ (void) policy;
+ CPU_ZERO(cpu_set);
+#endif
+ return;
+}
+
+jintArray android_os_Process_getExclusiveCores(JNIEnv* env, jobject clazz) {
+ SchedPolicy sp;
+ cpu_set_t cpu_set;
+ jintArray cpus;
+ int pid = getpid();
+ if (get_sched_policy(pid, &sp) != 0) {
+ signalExceptionForGroupError(env, errno);
+ return NULL;
+ }
+ get_exclusive_cpuset_cores(sp, &cpu_set);
+ int num_cpus = CPU_COUNT(&cpu_set);
+ cpus = env->NewIntArray(num_cpus);
+ if (cpus == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ jint* cpu_elements = env->GetIntArrayElements(cpus, 0);
+ int count = 0;
+ for (int i = 0; i < CPU_SETSIZE && count < num_cpus; i++) {
+ if (CPU_ISSET(i, &cpu_set)) {
+ cpu_elements[count++] = i;
+ }
+ }
+
+ env->ReleaseIntArrayElements(cpus, cpu_elements, 0);
+ return cpus;
+}
+
static void android_os_Process_setCanSelfBackground(JNIEnv* env, jobject clazz, jboolean bgOk) {
// Establishes the calling thread as illegal to put into the background.
// Typically used only for the system process's main looper.
@@ -1053,6 +1188,7 @@
{"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup},
{"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup},
{"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup},
+ {"getExclusiveCores", "()[I", (void*)android_os_Process_getExclusiveCores},
{"setSwappiness", "(IZ)Z", (void*)android_os_Process_setSwappiness},
{"setArgV0", "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
{"setUid", "(I)I", (void*)android_os_Process_setUid},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8f85d4a..6444c6c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -168,7 +168,7 @@
android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
- <protected-broadcast
+ <protected-broadcast
android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
@@ -984,6 +984,11 @@
<permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to send SMS to premium shortcodes without user permission.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SEND_SMS_NO_CONFIRMATION"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows an application to filter carrier specific sms.
@hide -->
<permission android:name="android.permission.CARRIER_FILTER_SMS"
@@ -1478,11 +1483,21 @@
<!-- Allows an application to manage access to documents, usually as part
of a document picker.
+ <p>This permission should <em>only</em> be requested by the platform
+ document management app. This permission cannot be granted to
+ third-party apps.
<p>Protection level: signature
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
android:protectionLevel="signature" />
+ <!-- @hide Allows an application to cache content.
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.CACHE_CONTENT"
+ android:protectionLevel="signature" />
+
<!-- ================================== -->
<!-- Permissions for screenlock -->
<!-- ================================== -->
@@ -2769,12 +2784,11 @@
android:protectionLevel="signature" />
<!-- Must be required by an {@link
- android.service.notification.NotificationAssistantService},
- to ensure that only the system can bind to it.
+ android.service.notification.NotificationRankerService to ensure that only the system can bind to it.
<p>Protection level: signature
@hide This is not a third-party API (intended for system apps). -->
-->
- <permission android:name="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"
+ <permission android:name="android.permission.BIND_NOTIFICATION_RANKER_SERVICE"
android:protectionLevel="signature" />
<!-- Must be required by a {@link
@@ -2929,6 +2943,12 @@
<permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"
android:protectionLevel="signature" />
+ <!-- Must be required by an {@link android.service.vr.VrListenerService}, to ensure that only
+ the system can bind to it.
+ <p>Protection level: signature -->
+ <permission android:name="android.permission.BIND_VR_LISTENER_SERVICE"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/assets/images/clock64.png b/core/res/assets/images/clock64.png
new file mode 100644
index 0000000..493a1ea
--- /dev/null
+++ b/core/res/assets/images/clock64.png
Binary files differ
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index 6f3c4d42..c96e280 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -16,7 +16,6 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000"
android:detachWallpaper="true"
android:shareInterpolator="false"
android:startOffset="100">
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 6669bae..0bbaa24 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -20,9 +20,9 @@
android:id="@+id/notification_header"
android:orientation="horizontal"
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="53dp"
android:clipChildren="false"
- android:paddingTop="5dp"
+ android:paddingTop="10dp"
android:paddingBottom="16dp"
android:paddingStart="@dimen/notification_content_margin_start"
android:paddingEnd="16dp">
@@ -33,16 +33,6 @@
android:layout_marginEnd="3dp"
/>
<TextView
- android:id="@+id/number_of_children"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.Material.Notification"
- android:layout_marginEnd="3dp"
- android:layout_marginStart="2dp"
- android:visibility="gone"
- android:singleLine="true"
- />
- <TextView
android:id="@+id/app_name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index a37bfa9..ccd26fb 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -41,7 +41,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="@dimen/notification_content_margin_start"
- android:layout_marginBottom="11dp"
+ android:layout_marginBottom="15dp"
android:layout_marginEnd="@dimen/notification_content_margin_end" >
<include layout="@layout/notification_template_progress" />
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index f302087..d53fb5f 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -49,7 +49,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginStart="@dimen/notification_content_margin_start"
- android:layout_marginBottom="11dp"
+ android:layout_marginBottom="15dp"
android:layout_marginEnd="@dimen/notification_content_margin_end">
<include layout="@layout/notification_template_progress" />
</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index a5ed187..04ea12d 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -25,7 +25,7 @@
>
<include layout="@layout/notification_template_header"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="53dp"
android:layout_gravity="start"/>
<LinearLayout
android:layout_width="match_parent"
@@ -50,9 +50,10 @@
android:id="@+id/media_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="-15dp"
+ android:layout_marginTop="-21dp"
android:paddingStart="8dp"
- android:paddingBottom="8dp"
+ android:paddingBottom="12dp"
+ android:gravity="top"
android:orientation="horizontal"
android:layoutDirection="ltr"
>
@@ -65,7 +66,7 @@
android:layout_height="@dimen/media_notification_expanded_image_max_size"
android:minWidth="40dp"
android:layout_marginEnd="16dp"
- android:layout_marginBottom="16dp"
+ android:layout_marginBottom="20dp"
android:layout_gravity="bottom|end"
android:scaleType="centerCrop"
/>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 9a4b28c..fdfefe1 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -39,7 +39,7 @@
<com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_marginTop="1.5dp"
+ android:layout_marginTop="0.5dp"
android:paddingBottom="@dimen/notification_content_margin_bottom"
android:textAppearance="@style/TextAppearance.Material.Notification"
android:singleLine="false"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index cda0636..809e525 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -24,7 +24,7 @@
>
<include layout="@layout/notification_template_header"
android:layout_width="fill_parent"
- android:layout_height="48dp" />
+ android:layout_height="53dp" />
<LinearLayout
android:id="@+id/notification_main_column"
android:layout_width="match_parent"
@@ -54,7 +54,7 @@
android:layout_height="match_parent"
android:layout_gravity="bottom|end"
android:layout_marginStart="10dp"
- android:layout_marginBottom="8dp"
+ android:layout_marginBottom="12dp"
android:layoutDirection="ltr"
android:orientation="horizontal"
>
diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml
index 3b358ab..15ccc67 100644
--- a/core/res/res/layout/notification_template_right_icon.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -19,7 +19,7 @@
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="16dp"
- android:layout_marginTop="32dp"
+ android:layout_marginTop="36dp"
android:layout_gravity="top|end"
android:scaleType="centerCrop"
/>
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 38470cd..a318bda 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -14,12 +14,12 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.internal.widget.ImageFloatingTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
- android:layout_marginTop="1.5dp"
+ android:layout_marginTop="0.5dp"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:gravity="top"
diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml
index 15b18dd..d1c65c0 100644
--- a/core/res/res/layout/text_edit_suggestion_container_material.xml
+++ b/core/res/res/layout/text_edit_suggestion_container_material.xml
@@ -33,7 +33,7 @@
android:id="@+id/suggestionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="8dp"
+ android:paddingTop="4dp"
android:paddingBottom="0dp"
android:divider="@null" />
<LinearLayout
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 06fcda9..de4ccae 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Stembystand"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Sluit nou"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Inhoud versteek"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Inhoud word versteek volgens beleid"</string>
<string name="safeMode" msgid="2788228061547930246">"Veiligmodus"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Raak vir meer opsies."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-ontfouter gekoppel"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Raak om USB-ontfouting te deaktiveer."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Neem tans foutverslag …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Deel foutverslag?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Deel tans foutverslag …"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Jou IT-administrateur het \'n foutverslag versoek om met die foutsporing van hierdie toestel te help. Programme en data sal dalk gedeel word en jou toestel sal dalk tydelik stadiger wees."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Jou IT-administrateur het \'n foutverslag versoek om met die foutsporing van hierdie toestel te help. Programme en data sal dalk gedeel word."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Dit sal jou toestel dalk tydelik stadiger maak"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"AANVAAR"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"WEIER"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Jou IT-administrateur het \'n foutverslag versoek om met die foutsporing van hierdie toestel te help. Programme en data sal dalk gedeel word."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"DEEL"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"WEIER"</string>
<string name="select_input_method" msgid="8547250819326693584">"Verander sleutelbord"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Kies sleutelborde"</string>
<string name="show_ime" msgid="2506087537466597099">"Hou dit op die skerm terwyl fisieke sleutelbord aktief is"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Muurpapier"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Verander muurpapier"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Kennisgewingluisteraar"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR-luisteraar"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Toestandverskaffer"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Kennisgewingassistent"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Kennisgewingklassifiseringsdiens"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN geaktiveer"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is geaktiveer deur <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak om die netwerk te bestuur."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Voer \'n fabriekterugstelling uit om hierdie toestel normaal te gebruik"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Raak om meer te wete te kom."</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f06d7de..88e9367 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"የድምጽ እርዳታ"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"አሁን ቆልፍ"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"ይዘቶች ተደብቀዋል"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ይዘቶች በመመሪያ ተደብቀዋል"</string>
<string name="safeMode" msgid="2788228061547930246">"የሚያስተማምን ሁነታ"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"ለተጨማሪ አማራጮች ነካ ያድርጉ።"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB አድስ ተያይዟል"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB ማረሚያ ላለማንቃት ዳስስ።"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"የሳንካ ሪፖርትን በመውሰድ ላይ…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"የሳንካ ሪፖርት ይጋራ?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"የሳንካ ሪፖርትን በማጋራት ላይ…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"የእርስዎ አይቲ አስተዳዳሪ ለዚህ መሣሪያ መላ ለመፈለግ እንዲያግዝ የሳንካ ሪፖርት ጠይቀዋል። መተግበሪያዎች እና ውሂብ ሊጋሩ ይችላሉ። ይሄ መሣሪያዎን ለጊዜው ሊያዘገየው ይችላል።"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"የእርስዎ አይቲ አስተዳዳሪ ለዚህ መሣሪያ መላ ለመፈለግ የሳንካ ሪፖርት ጠይቀዋል። መተግበሪያዎች እና ውሂብ ሊጋሩ ይችላሉ።"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"ይሄ መሣሪያዎን ለጊዜው ሊያዘገየው ይችላል"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ተቀበል"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ውድቅ አድርግ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"የእርስዎ አይቲ አስተዳዳሪ ለዚህ መሣሪያ መላ ለመፈለግ የሳንካ ሪፖርት ጠይቀዋል። መተግበሪያዎች እና ውሂብ ሊጋሩ ይችላሉ።"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"አጋራ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"አትቀበል"</string>
<string name="select_input_method" msgid="8547250819326693584">"ቁልፍ ሰሌዳ ይቀይሩ"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"ቁልፍ ሰሌዳዎችን ምረጥ"</string>
<string name="show_ime" msgid="2506087537466597099">"አካላዊ የቁልፍ ሰሌዳ ገቢር ሆኖ ሳለ በማያ ገጽ ላይ አቆየው"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ልጣፍ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ልጣፍ ለውጥ"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"ማሳወቂያ አዳማጭ"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"የምናባዊ እውነታ አዳማጭ"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"የሁኔታ አቅራቢ"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"የማሳወቂያ ረዳት"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"የማሳወቂያ ደረጃ ሰጪ አገልግሎት"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ነቅቷል።"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN በ<xliff:g id="APP">%s</xliff:g>ገብሯል"</string>
<string name="vpn_text" msgid="3011306607126450322">"አውታረመረብ ለማደራጀት ንካ።"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"ይህን መሣሪያ በመደበኛነት ለመጠቀም የፋብሪካ ዳግም ያስጀምሩ"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"የበለጠ ለመረዳት ይንኩ።"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index d8a8377..84bc768 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -237,7 +237,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"المساعد الصوتي"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"قفل الآن"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"المحتويات مخفية"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"تم إخفاء المحتويات بواسطة السياسة"</string>
<string name="safeMode" msgid="2788228061547930246">"الوضع الآمن"</string>
@@ -1084,13 +1083,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"المس للحصول على مزيد من الخيارات."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"تم توصيل تصحيح أخطاء USB"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"المس لتعطيل تصحيح أخطاء USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"جارٍ الحصول على تقرير الخطأ…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"هل تريد مشاركة تقرير الخطأ؟"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"جارٍ مشاركة تقرير الخطأ…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"طلب مشرف تكنولوجيا المعلومات الحصول على تقرير خطأ للمساعدة في تحرِّي مشكلة هذا الجهاز وإصلاحها. قد تتم مشاركة التطبيقات والبيانات. وقد يؤدي هذا إلى حدوث بطء مؤقت في جهازك."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"طلب مشرف تكنولوجيا المعلومات الحصول على تقرير خطأ للمساعدة في تحرِّي مشكلة هذا الجهاز وإصلاحها. قد تتم مشاركة التطبيقات والبيانات."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"قد يؤدي هذا إلى حدوث بطء مؤقت في جهازك"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"قبول"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"رفض"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"طلب مشرف تكنولوجيا المعلومات الحصول على تقرير خطأ للمساعدة في تحرِّي مشكلة هذا الجهاز وإصلاحها؛ ويمكن أن تتم مشاركة التطبيقات والبيانات."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"مشاركة"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"رفض"</string>
<string name="select_input_method" msgid="8547250819326693584">"تغيير لوحة المفاتيح"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"اختيار لوحات المفاتيح"</string>
<string name="show_ime" msgid="2506087537466597099">"استمرار عرضها على الشاشة أثناء نشاط لوحة المفاتيح الفعلية"</string>
@@ -1169,8 +1167,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"الخلفية"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"تغيير الخلفية"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"برنامج تلقّي الإشعارات الصوتية"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"مستمع واقع افتراضي"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"موفر الحالة"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"مساعد الإشعار"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"خدمة ترتيب أهمية الإشعارات"</string>
<string name="vpn_title" msgid="19615213552042827">"تم تنشيط الشبكة الظاهرية الخاصة (VPN)"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"تم تنشيط VPN بواسطة <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"المس لإدارة الشبكة."</string>
@@ -1641,4 +1640,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"يمكنك إعادة تعيين إعدادات المصنع لاستخدام هذا الجهاز بشكل عادي"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"المس للتعرف على مزيد من المعلومات."</string>
</resources>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 33d1616..3953264 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Səs Yardımçısı"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"İndi kilidləyin"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Məzmun gizlidir"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Məzmun siyasət tərəfindən gizlədilib"</string>
<string name="safeMode" msgid="2788228061547930246">"Təhlükəsiz rejim"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Əlavə seçimlər üçün toxunun."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB sazlama qoşuludur"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB debaqı deaktivasiya etmək üçün toxunun."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Baq hesabatı verilir..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Baq hesabatı paylaşılsın?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Baq hesabatı paylaşılır..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"IT admininiz bu cihazda nasazlıqların aşkarlanması üçün baq hesabatı sorğusu göndərdi. Tətbiqlər və data paylaşıla və cihazınız surəti müvəqqəti azala bilər."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"IT admininiz bu cihazda nasazlıqların aşkarlanması üçün baq hesabatı sorğusu göndərdi. Tətbiqlər və data paylaşıla bilər."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Bu cihazınızın sürətini müvəqqəti olaraq azalda bilər"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"QƏBUL EDİN"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"RƏDD ET"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"İT admininiz bu cihazda nasazlıqların aşkarlanması üçün baq hesabatı sorğusu göndərdi. Tətbiqlər və data paylaşıla bilər."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"PAYLAŞIN"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RƏDD EDİN"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klaviaturanı dəyişin"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Klaviaturaları seçin"</string>
<string name="show_ime" msgid="2506087537466597099">"Fiziki klaviatura aktiv olduğu halda ekranda saxlayın"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Divar kağızı"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Divar kağızını dəyişin"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Bildiriş dinləyən"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR dinləyici"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Şərait provayderi"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Bildiriş köməkçisi"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Bildiriş qiymətləndirici xidmət"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktivləşdirildi"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN <xliff:g id="APP">%s</xliff:g> tərəfindən aktivləşdirilib"</string>
<string name="vpn_text" msgid="3011306607126450322">"Şəbəkəni idarə etmək üçün toxunun."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Cihazı normal istifadə etmək üçün fabrik sıfırlaması"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Daha çox məlumat üçün toxunun."</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 7ed9a20..5c73faf 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -231,7 +231,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Zaključaj odmah"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Sadržaj je sakriven"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sadržaj je sakriven smernicama"</string>
<string name="safeMode" msgid="2788228061547930246">"Bezbedni režim"</string>
@@ -1060,13 +1059,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Dodirnite za još opcija."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Otklanjanje grešaka sa USB-a je uspostavljeno"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Dodirnite da biste onemogućili otklanjanje grešaka sa USB-a."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Izveštaj o grešci se generiše…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Želite li da podelite izveštaj o grešci?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Deli se izveštaj o grešci…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"IT administrator je zatražio izveštaj o grešci radi lakšeg rešavanja problema u vezi sa ovim uređajem. Aplikacije i podaci mogu da se dele, a uređaj će se privremeno usporiti."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"IT administrator je zatražio izveštaj o grešci radi lakšeg rešavanja problema u vezi sa ovim uređajem. Aplikacije i podaci mogu da se dele."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Ovo će privremeno usporiti uređaj"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"PRIHVATI"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ODBIJ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"IT administrator je zatražio izveštaj o grešci radi lakšeg rešavanja problema u vezi sa ovim uređajem. Aplikacije i podaci mogu da se dele."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"DELI"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODBIJ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Promenite tastaturu"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Izaberite tastature"</string>
<string name="show_ime" msgid="2506087537466597099">"Zadrži ga na ekranu dok je fizička tastatura aktivna"</string>
@@ -1145,8 +1143,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Pozadina"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Promena pozadine"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Monitor obaveštenja"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Obrađivač za virtuelnu realnost"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Dobavljač uslova"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Pomoćnik za obaveštenja"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Usluga rangiranja obaveštenja"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN je aktiviran"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikacija <xliff:g id="APP">%s</xliff:g> je aktivirala VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dodirnite da biste upravljali mrežom."</string>
@@ -1584,4 +1583,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Resetujte uređaj na fabrička podešavanja da biste ga normalno koristili"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Dodirnite da biste saznali više."</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index ac2a0ec..b2633a9 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Гласова помощ"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Заключване сега"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Скрито съдържание"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Съдържанието е скрито чрез правило"</string>
<string name="safeMode" msgid="2788228061547930246">"Безопасен режим"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Докоснете за още опции."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Отстраняване на грешки през USB"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Докоснете за деактивиране"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Сигналът за програмна грешка се извлича…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Да се сподели ли сигналът за програмна грешка?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Сигналът за програмна грешка се споделя…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Системният ви администратор поиска сигнал за програмна грешка с цел отстраняване на неизправностите на устройството. Възможно е да бъдат споделени приложения и данни и работата на устройството ви временно да се забави."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Системният ви администратор поиска сигнал за програмна грешка с цел отстраняване на неизправностите на това устройство. Възможно е да бъдат споделени приложения и данни."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Това може временно да забави работата на устройството ви"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ПРИЕМАМ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ОТХВЪРЛЯМ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Системният ви администратор поиска сигнал за програмна грешка с цел отстраняване на неизправностите на това устройство. Възможно е да бъдат споделени приложения и данни."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"СПОДЕЛЯНЕ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ОТХВЪРЛЯНЕ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Промяна на клавиатурата"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Избиране на клавиатури"</string>
<string name="show_ime" msgid="2506087537466597099">"Показване на екрана, докато физическата клавиатура е активна"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Тапет"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Промяна на тапета"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Слушател на известия"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Приемател за виртуална реалност"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Доставчик на условия"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Помощник за известия"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Услуга за класифициране на известията"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN е активирана"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN е активирана от <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Докоснете за управление на мрежата."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Възстановете фабричните настройки, за да използвате това устройство нормално"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Докоснете, за да научите повече."</string>
</resources>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 4e6cd4b..2042c4c 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"ভয়েস সহায়তা"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"এখনই লক করুন"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"৯৯৯+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>টি)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"লুকানো বিষয়বস্তু"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"নীতির কারণে সামগ্রী লুকানো আছে"</string>
<string name="safeMode" msgid="2788228061547930246">"নিরাপদ মোড"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"আরো বিকল্পের জন্য স্পর্শ করুন৷"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ডিবাগিং সংযুক্ত হয়েছে"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB ডিবাগিং অক্ষম করতে স্পর্শ করুন৷"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ত্রুটির প্রতিবেদন নেওয়া হচ্ছে..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ত্রুটির প্রতিবেদন শেয়ার করবেন?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"ত্রুটির প্রতিবেদন শেয়ার করা হচ্ছে..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"আপনার আইটি প্রশাসক এই ডিভাইসটির সমস্যা নিবারণে সহায়তা করতে একটি ত্রুটির প্রতিবেদন চেয়েছেন৷ অ্যাপ্লিকেশানগুলি এবং ডেটা শেয়ার করা হতে পারে এবং আপনার ডিভাইসটিকে অস্থায়ীভাবে ধীর করে দিতে পারে৷"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"আপনার আইটি প্রশাসক এই ডিভাইসটির সমস্যা নিবারণে সহায়তা করতে একটি ত্রুটির প্রতিবেদন চেয়েছেন৷ অ্যাপ্লিকেশান এবং ডেটা শেয়ার করা হতে পারে৷"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"এটি অস্থায়ীভাবে আপনার ডিভাইসটিকে ধীর করে দিতে পারে"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"স্বীকার করুন"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"অস্বীকার করুন"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"আপনার আইটি প্রশাসক এই ডিভাইসটির সমস্যা নিবারণে সহায়তা করতে একটি ত্রুটির প্রতিবেদন চেয়েছেন৷ অ্যাপ্লিকেশান এবং ডেটা শেয়ার করা হতে পারে৷"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"শেয়ার করুন"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"অস্বীকার করুন"</string>
<string name="select_input_method" msgid="8547250819326693584">"কীবোর্ড পরিবর্তন করুন"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"কীবোর্ড চয়ন করুন"</string>
<string name="show_ime" msgid="2506087537466597099">"ফিজিক্যাল কীবোর্ড সক্রিয় থাকার সময় এটিকে স্ক্রীনে রাখুন"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ওয়ালপেপার"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ওয়ালপেপার পরিবর্তন করুন"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"বিজ্ঞপ্তির শ্রোতা"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR শ্রোতা"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"শর্ত প্রদানকারী"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"বিজ্ঞপ্তি সহায়ক"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"বিজ্ঞপ্তি র্যাঙ্কার পরিষেবা"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN সক্রিয়"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> এর দ্বারা VPN সক্রিয় করা হয়েছে"</string>
<string name="vpn_text" msgid="3011306607126450322">"নেটওয়ার্ক পরিচালনা করতে স্পর্শ করুন৷"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"এই ডিভাইসটিকে স্বাভাবিকভাবে ব্যবহার করতে ফ্যাক্টরি পুনরায় সেট করুন"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"আরো জানতে স্পর্শ করুন৷"</string>
</resources>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 798f6f5..f352620 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -231,7 +231,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Zaključaj odmah"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Sadržaj je sakriven"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sadržaj skriven u skladu sa pravilima"</string>
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
@@ -1060,13 +1059,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"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="1016654627626476142">"Dodirnite da biste onemogućili USB otklanjanje grešaka."</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>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Vaš IT administrator je zatražio izvještaj o grešci kako bi pomogao riješiti problem na ovom uređaju. To može uzrokovati dijeljenje aplikacija i podataka te privremeno usporiti vaš uređaj."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Vaš IT administrator je zatražio izvještaj o grešci kako bi pomogao u rješavanju problema ovog uređaja. Aplikacije i podaci se mogu dijeliti."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"To može privremeno usporiti vaš uređaj"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"PRIHVATI"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ODBACI"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Vaš IT administrator je zatražio izvještaj o grešci kako bi pomogao u rješavanju problema ovog uređaja. Aplikacije i podaci se mogu dijeliti."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"PODIJELI"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODBACI"</string>
<string name="select_input_method" msgid="8547250819326693584">"Promijeni tastaturu"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Odaberite tastature"</string>
<string name="show_ime" msgid="2506087537466597099">"Prikaži na ekranu dok je fizička tastatura aktivna"</string>
@@ -1145,8 +1143,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Pozadinska slika"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Promijenite pozadinsku sliku"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Usluga za praćenje obavještenja"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR slušalac"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Pružalac uslova"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Pomoćnik za obavještenja"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Usluga rangiranja obavještenja"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviran"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikacija <xliff:g id="APP">%s</xliff:g> je aktivirala VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dodirnite za upravljanje mrežom."</string>
@@ -1558,7 +1557,7 @@
</plurals>
<string name="importance_from_user" msgid="7318955817386549931">"Vi određujete značaj ovih obavještenja."</string>
<string name="importance_from_person" msgid="9160133597262938296">"Ovo je značajno zbog osoba koje su uključene."</string>
- <string name="user_creation_account_exists" msgid="1942606193570143289">"Da li dozvoljate da <xliff:g id="APP">%1$s</xliff:g> kreira novog korisnika za nalog <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
+ <string name="user_creation_account_exists" msgid="1942606193570143289">"Da li dozvoljavate aplikaciji <xliff:g id="APP">%1$s</xliff:g> da kreira novog korisnika s računom <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
<string name="user_creation_adding" msgid="4482658054622099197">"Da li dozvoljavate da <xliff:g id="APP">%1$s</xliff:g> kreira novog korisnika za <xliff:g id="ACCOUNT">%2$s</xliff:g> (Korisnik sa ovim nalogom već postoji)?"</string>
<string name="language_selection_title" msgid="2680677278159281088">"Dodaj jezik"</string>
<string name="country_selection_title" msgid="2954859441620215513">"Izbor regije"</string>
@@ -1584,4 +1583,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Vratite na fabričke postavke kako biste mogli normalno koristiti ovaj uređaj"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Dodirnite da saznate više."</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 9bd8014..5482896 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. per veu"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Bloqueja ara"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"+999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contingut amagat"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contingut amagat de conformitat amb la política"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode segur"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Toca per veure més opcions."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuració USB activada"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toca per desactivar la depuració USB"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"S\'està creant l\'informe d\'errors…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vols compartir l\'informe d\'errors?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"S\'està compartint l\'informe d\'errors…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"L\'administrador de TI ha sol·licitat un informe d\'errors per resoldre els problemes d\'aquest dispositiu. És possible que es comparteixin aplicacions i dades, i que el dispositiu funcioni més a poc a poc durant una estona."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"El teu administrador de TI ha sol·licitat un informe d\'errors per resoldre els problemes d\'aquest dispositiu. És possible que es comparteixin aplicacions i dades."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"És possible que el dispositiu funcioni més a poc a poc durant una estona"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCEPTA"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"REBUTJA"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"L\'administrador de TI ha sol·licitat un informe d\'errors per resoldre els problemes d\'aquest dispositiu. És possible que es comparteixin aplicacions i dades."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTEIX"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REBUTJA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Canvia el teclat"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Tria els teclats"</string>
<string name="show_ime" msgid="2506087537466597099">"El deixa a la pantalla mentre el teclat físic està actiu"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fons de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Canvia el fons de pantalla"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Oient de notificacions"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Processador de realitat virtual"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Proveïdor de condicions"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Assistent de notificacions"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Servei de classificació de notificacions"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ha activat VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca per gestionar la xarxa."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Restableix les dades de fàbrica del dispositiu per utilitzar-lo amb normalitat"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toca per obtenir més informació."</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index facf368..390dfd0 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -233,7 +233,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Hlas. asistence"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Zamknout"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Skrytý obsah"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Obsah skrytý zásadami"</string>
<string name="safeMode" msgid="2788228061547930246">"Nouzový režim"</string>
@@ -1068,13 +1067,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Klepnutím zobrazíte další možnosti."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Ladění přes USB připojeno"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Dotykem zakážete ladění USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Vytváření zprávy o chybě…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Sdílet zprávu o chybě?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Sdílení zprávy o chybě…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Administrátor IT si vyžádal zprávu o chybě, aby mohl problém odstranit. Mohou být sdíleny aplikace a data a zařízení se může dočasně zpomalit."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Administrátor IT si vyžádal zprávu o chybě, aby mohl problém odstranit. Aplikace a data mohou být sdílena."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Zařízení se může dočasně zpomalit"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"PŘIJMOUT"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ODMÍTNOUT"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Administrátor IT si vyžádal zprávu o chybě, aby mohl problém odstranit. Aplikace a data mohou být sdílena."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"SDÍLET"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODMÍTNOUT"</string>
<string name="select_input_method" msgid="8547250819326693584">"Změna klávesnice"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Vybrat klávesnici"</string>
<string name="show_ime" msgid="2506087537466597099">"Ponechat na obrazovce, když je aktivní fyzická klávesnice"</string>
@@ -1153,8 +1151,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Změnit tapetu"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Aplikace poslouchající oznámení"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Přijímač virtuální reality"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Poskytovatel podmínky"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Asistent oznámení"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Služba na hodnocení důležitosti oznámení"</string>
<string name="vpn_title" msgid="19615213552042827">"Síť VPN je aktivována"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikace <xliff:g id="APP">%s</xliff:g> aktivovala síť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykem zobrazíte správu sítě."</string>
@@ -1603,4 +1602,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Chcete-li toto zařízení normálně používat, obnovte jej do továrního nastavení"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Klepnutím zobrazíte další informace."</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index a0a3f8f..583a6b7 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Taleassistent"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lås nu"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Indholdet er skjult"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Indholdet er skjult af politikken"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikker tilstand"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Tryk for at se flere muligheder."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-fejlretning er tilsluttet"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Tryk for at deaktivere USB-fejlretning."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Opretter fejlrapport…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vil du dele fejlrapporten?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Deler fejlrapport…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Din it-administrator har anmodet om en fejlrapport for bedre at kunne finde og rette fejlen på enheden. Apps og data deles muligvis, og din enhed kan midlertidigt blive langsommere."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Din it-administrator har anmodet om en fejlrapport for bedre at kunne finde og rette fejlen på enheden. Apps og data deles muligvis."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Dette kan midlertidigt gøre enheden langsommere"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCEPTÉR"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"AFVIS"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Din it-administrator har anmodet om en fejlrapport for bedre at kunne finde og rette fejlen på enheden. Apps og data deles muligvis."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"DEL"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"AFVIS"</string>
<string name="select_input_method" msgid="8547250819326693584">"Skift tastatur"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Vælg tastaturer"</string>
<string name="show_ime" msgid="2506087537466597099">"Behold den på skærmen, mens det fysiske tastatur er aktivt"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Baggrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Skift baggrund"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Underretningslytter"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR-lyttefunktion"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Tjeneste til formidling af betingelser"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Underretningsassistent"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Tjeneste til rangering af underretninger"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN er aktiveret."</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveres af <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tryk for at administrere netværket."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Nulstil enheden til fabriksindstillingerne for at bruge den på normal vis"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Tryk for at få flere oplysninger."</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 8406a70..b67402a 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Sprachassistent"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Jetzt sperren"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Inhalte ausgeblendet"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Inhalte aufgrund der Richtlinien ausgeblendet"</string>
<string name="safeMode" msgid="2788228061547930246">"Abgesicherter Modus"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Für weitere Optionen tippen"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-Debugging"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Zum Deaktivieren berühren"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Fehlerbericht wird abgerufen…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Fehlerbericht teilen?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Fehlerbericht wird geteilt…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Dein IT-Administrator hat einen Fehlerbericht zur Fehlerbehebung dieses Geräts angefordert. Apps und Daten werden unter Umständen geteilt und dein Gerät wird möglicherweise vorübergehend langsamer."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Dein IT-Administrator hat einen Fehlerbericht zur Fehlerbehebung dieses Geräts angefordert. Apps und Daten werden unter Umständen geteilt."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Dadurch wird dein Gerät möglicherweise vorübergehend langsamer"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"AKZEPTIEREN"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ABLEHNEN"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Dein IT-Administrator hat einen Fehlerbericht zur Fehlerbehebung für dieses Gerät angefordert. Apps und Daten werden unter Umständen geteilt."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"TEILEN"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ABLEHNEN"</string>
<string name="select_input_method" msgid="8547250819326693584">"Tastatur ändern"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Tastatur auswählen"</string>
<string name="show_ime" msgid="2506087537466597099">"Auf dem Display einblenden, wenn die physische Tastatur aktiv ist"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Hintergrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Hintergrund ändern"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Benachrichtigungs-Listener"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR-Listener"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Bedingungsprovider"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Benachrichtigungsassistent"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Service für Einstufung von Benachrichtigungen"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviert"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN wurde von <xliff:g id="APP">%s</xliff:g> aktiviert."</string>
<string name="vpn_text" msgid="3011306607126450322">"Zum Verwalten des Netzwerks berühren"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Gerät zur normalen Verwendung auf Werkseinstellungen zurücksetzen"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Für weitere Informationen tippen."</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index f5192b7..8450189 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Φων.υποβοηθ."</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Κλείδωμα τώρα"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Κρυφό περιεχόμενο"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Το περιεχόμενο είναι κρυφό βάσει πολιτικής"</string>
<string name="safeMode" msgid="2788228061547930246">"Ασφαλής λειτουργία"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Αγγίξτε για περισσότερες επιλογές."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Συνδέθηκε ο εντοπισμός σφαλμάτων USB"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Απεν. του εντοπ. σφαλμάτων USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Λήψη αναφοράς σφάλματος…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Κοινή χρήση αναφοράς σφάλματος;"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Κοινή χρήση αναφοράς σφάλματος…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Ο διαχειριστής σας IT ζήτησε μια αναφορά σφάλματος για να συμβάλει στην αντιμετώπιση του προβλήματος αυτής της συσκευής. Ενδέχεται να γίνει κοινή χρήση των εφαρμογών και να επιβραδυνθεί προσωρινά τη λειτουργία της συσκευής σας."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Ο διαχειριστής σας IT ζήτησε μια αναφορά σφάλματος για να συμβάλει στην αντιμετώπιση του προβλήματος αυτής της συσκευής. Ενδέχεται να γίνει κοινή χρήση των εφαρμογών και των δεδομένων."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Αυτή η διαδικασία ενδέχεται να επιβραδύνει προσωρινά τη λειτουργία της συσκευής σας"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ΑΠΟΔΟΧΗ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ΑΠΟΡΡΙΨΗ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Ο διαχειριστής IT σας ζήτησε μια αναφορά σφάλματος για να συμβάλει στην αντιμετώπιση του προβλήματος αυτής της συσκευής. Ενδέχεται να κοινοποιηθούν οι εφαρμογές και τα δεδομένα."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ΚΟΙΝΟΠΟΙΗΣΗ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ΑΠΟΡΡΙΨΗ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Αλλαγή πληκτρολογίου"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Επιλογή πληκτρολογίων"</string>
<string name="show_ime" msgid="2506087537466597099">"Να παραμένει στην οθόνη όταν είναι ενεργό το φυσικό πληκτρολόγιο"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Ταπετσαρία"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Αλλαγή ταπετσαρίας"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Υπηρεσία ακρόασης ειδοποίησης"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Λειτουργία ακρόασης Εικονικής Πραγματικότητας"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Πάροχος συνθηκών"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Βοηθός ειδοποιήσεων"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Υπηρεσία κατάταξης ειδοποιήσεων"</string>
<string name="vpn_title" msgid="19615213552042827">"Το VPN ενεργοποιήθηκε"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Το VPN ενεργοποιήθηκε από την εφαρμογή <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Αγγίξτε για τη διαχείριση του δικτύου."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Επαναφέρετε τις εργοστασιακές ρυθμίσεις για να χρησιμοποιήσετε αυτήν τη συσκευή κανονικά"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Αγγίξτε για να μάθετε περισσότερα."</string>
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 7fdf1c1..ac7ad91 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lock now"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contents hidden"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contents hidden by policy"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Touch for more options."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Touch to disable USB debugging."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Taking bug report…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Share bug report?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Sharing bug report…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared and your device may temporarily slow down."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"This may temporarily slow down your device"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCEPT"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"DECLINE"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHARE"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"DECLINE"</string>
<string name="select_input_method" msgid="8547250819326693584">"Change keyboard"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Choose keyboards"</string>
<string name="show_ime" msgid="2506087537466597099">"Keep it on screen while physical keyboard is active"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Change wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Notification listener"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR listener"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Condition provider"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Notification assistant"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Notification ranker service"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activated"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is activated by <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Touch to manage the network."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Restore this device to factory settings to use normally"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Touch to find out more."</string>
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7fdf1c1..ac7ad91 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lock now"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contents hidden"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contents hidden by policy"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Touch for more options."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Touch to disable USB debugging."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Taking bug report…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Share bug report?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Sharing bug report…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared and your device may temporarily slow down."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"This may temporarily slow down your device"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCEPT"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"DECLINE"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHARE"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"DECLINE"</string>
<string name="select_input_method" msgid="8547250819326693584">"Change keyboard"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Choose keyboards"</string>
<string name="show_ime" msgid="2506087537466597099">"Keep it on screen while physical keyboard is active"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Change wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Notification listener"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR listener"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Condition provider"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Notification assistant"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Notification ranker service"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activated"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is activated by <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Touch to manage the network."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Restore this device to factory settings to use normally"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Touch to find out more."</string>
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7fdf1c1..ac7ad91 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lock now"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contents hidden"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contents hidden by policy"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Touch for more options."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Touch to disable USB debugging."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Taking bug report…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Share bug report?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Sharing bug report…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared and your device may temporarily slow down."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"This may temporarily slow down your device"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCEPT"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"DECLINE"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHARE"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"DECLINE"</string>
<string name="select_input_method" msgid="8547250819326693584">"Change keyboard"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Choose keyboards"</string>
<string name="show_ime" msgid="2506087537466597099">"Keep it on screen while physical keyboard is active"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Change wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Notification listener"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR listener"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Condition provider"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Notification assistant"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Notification ranker service"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activated"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is activated by <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Touch to manage the network."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Restore this device to factory settings to use normally"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Touch to find out more."</string>
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index b101ded..4623d77 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear ahora"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenidos ocultos"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenido oculto debido a la política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
@@ -239,7 +238,7 @@
<string name="permgrouplab_contacts" msgid="3657758145679177612">"Contactos"</string>
<string name="permgroupdesc_contacts" msgid="6951499528303668046">"acceder a los contactos"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Ubicación"</string>
- <string name="permgroupdesc_location" msgid="1346617465127855033">"acceso a la ubicación de este dispositivo"</string>
+ <string name="permgroupdesc_location" msgid="1346617465127855033">"acceder a la ubicación de este dispositivo"</string>
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Calendario"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acceder al calendario"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Toca para ver más opciones."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración por USB conectada"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toca para desactivar la depuración por USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Realizando un informe de errores…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"¿Compartir informe de errores?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Compartiendo informe de errores…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"El administrador de TI solicitó un informe de errores para ayudar a solucionar los problemas de este dispositivo. Es posible que se compartan apps y datos, y que el dispositivo se ralentice temporalmente."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"El administrador de TI solicitó un informe de errores para ayudar a solucionar los problemas de este dispositivo. Es posible que se compartan apps y datos."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Es posible que tu dispositivo se ralentice temporalmente"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACEPTAR"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"RECHAZAR"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"El administrador de TI solicitó un informe de errores para solucionar los problemas de este dispositivo. Es posible que se compartan apps y datos."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTIR"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECHAZAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Cambiar el teclado"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Seleccionar teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Mantener en la pantalla cuando el teclado físico está activo"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Papel tapiz"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambiar fondo de pantalla"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Agente de escucha de notificaciones"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Procesador de realidad virtual"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Proveedor de condiciones"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Asistente de notificaciones"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Servicio de clasificación de notificaciones"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN está activado por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Restablece la configuración de fábrica para usar este dispositivo con normalidad"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toca para obtener más información."</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 6b2c0f8..65f5f24 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear ahora"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"> 999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenidos ocultos"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenidos ocultos por política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
@@ -276,7 +275,7 @@
<string name="permdesc_install_shortcut" msgid="8341295916286736996">"Permite que una aplicación añada accesos directos a la pantalla de inicio sin intervención del usuario."</string>
<string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"desinstalar accesos directos"</string>
<string name="permdesc_uninstall_shortcut" msgid="6745743474265057975">"Permite que la aplicación elimine accesos directos de la pantalla de inicio sin la intervención del usuario."</string>
- <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"redireccionar llamadas salientes"</string>
+ <string name="permlab_processOutgoingCalls" msgid="3906007831192990946">"redirigir llamadas salientes"</string>
<string name="permdesc_processOutgoingCalls" msgid="5156385005547315876">"Permite que la aplicación vea el número que se marca al realizar una llamada con la opción de redirigir la llamada a otro número o cancelar la llamada."</string>
<string name="permlab_receiveSms" msgid="8673471768947895082">"recibir mensajes de texto (SMS)"</string>
<string name="permdesc_receiveSms" msgid="6424387754228766939">"Permite que la aplicación reciba y procese mensajes MMS, lo que significa que podría utilizar este permiso para controlar o eliminar mensajes enviados al dispositivo sin mostrárselos al usuario."</string>
@@ -910,8 +909,8 @@
<string name="noApplications" msgid="2991814273936504689">"Ninguna aplicación puede realizar esta acción."</string>
<string name="aerr_application" msgid="250320989337856518">"La aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> ha dejado de funcionar"</string>
<string name="aerr_process" msgid="6201597323218674729">"El proceso <xliff:g id="PROCESS">%1$s</xliff:g> ha dejado de funcionar"</string>
- <string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> sigue dejando de funcionar"</string>
- <string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> sigue dejando de funcionar"</string>
+ <string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> sigue sin funcionar"</string>
+ <string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> sigue sin funcionar"</string>
<string name="aerr_restart" msgid="9001379185665886595">"Reiniciar aplicación"</string>
<string name="aerr_reset" msgid="7645427603514220451">"Restablecer y reiniciar aplicación"</string>
<string name="aerr_report" msgid="5371800241488400617">"Enviar sugerencias"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Toca para obtener más opciones"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración USB habilitada"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toca aquí para inhabilitarla"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Creando informe de errores…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"¿Compartir informe de errores?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Compartiendo informe de errores…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Tu administrador de TI ha solicitado un informe de errores para solucionar problemas de este dispositivo. Es posible que se compartan las aplicaciones y los datos. Puede que el dispositivo funcione más lento de forma temporal."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Tu administrador de TI ha solicitado un informe de errores para solucionar problemas de este dispositivo. Es posible que se compartan las aplicaciones y los datos."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Puede que el dispositivo funcione más lento de forma temporal"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACEPTAR"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"RECHAZAR"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Tu administrador de TI ha solicitado un informe de errores para solucionar problemas de este dispositivo. Es posible que se compartan las aplicaciones y los datos."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTIR"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECHAZAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Cambiar teclado"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Elegir teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Debe seguir en pantalla mientras el teclado físico esté activo"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fondo de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambiar fondo de pantalla"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Detector de notificaciones"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Procesador de RV"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Proveedor de condiciones"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Asistente de notificaciones"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Servicio de clasificación de notificaciones"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
@@ -1447,7 +1446,7 @@
<item quantity="one">Vuelve a intentarlo en 1 segundo</item>
</plurals>
<string name="restr_pin_try_later" msgid="973144472490532377">"Volver a intentar más tarde"</string>
- <string name="immersive_cling_title" msgid="8394201622932303336">"Mostrando pantalla completa"</string>
+ <string name="immersive_cling_title" msgid="8394201622932303336">"Modo de pantalla completa"</string>
<string name="immersive_cling_description" msgid="3482371193207536040">"Para salir, desliza hacia abajo desde arriba."</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"Entendido"</string>
<string name="done_label" msgid="2093726099505892398">"Listo"</string>
@@ -1569,4 +1568,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Restablece los datos de fábrica para utilizar este dispositivo con normalidad"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toca para obtener más información."</string>
</resources>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 0236d2c..c35d222 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Häälabi"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lukusta kohe"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Sisu on peidetud"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sisu on eeskirjadega peidetud"</string>
<string name="safeMode" msgid="2788228061547930246">"Turvarežiim"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Puudutage rohkemate valikute kuvamiseks."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-silumine ühendatud"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Puudutage USB-silumise keelamiseks."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Veaaruande võtmine …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Kas jagada veaaruannet?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Veaaruande jagamine …"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"IT-administraator taotles veaaruannet, mis aitaks seadmes vigu otsida. Rakendusi ja andmeid võidakse jagada ja see võib ajutiselt teie seadet aeglustada."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"IT-administraator taotles veaaruannet, mis aitaks seadmes vigu otsida. Rakendusi ja andmeid võidakse jagada."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"See võib ajutiselt teie seadet aeglustada"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"NÕUSTU"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"KEELDU"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"IT-administraator taotles veaaruannet, mis aitaks seadmes vigu otsida. Rakendusi ja andmeid võidakse jagada."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"JAGA"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"KEELDU"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klaviatuuri muutmine"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Vali klaviatuurid"</string>
<string name="show_ime" msgid="2506087537466597099">"Hoia seda ekraanil, kui füüsiline klaviatuur on aktiivne"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Taustapilt"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Muutke taustapilti"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Märguannete kuulamisteenus"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Virtuaalreaalses režiimis kuulaja"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Tingimuse pakkuja"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Märguannete abi"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Märguannete tähtsuse määramise teenus"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN on aktiveeritud"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-i aktiveeris <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Võrgu haldamiseks puudutage."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Seadme tavapäraseks kasutamiseks lähtestage see tehaseseadetele"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Lisateabe saamiseks puudutage."</string>
</resources>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 2ed5463..bb83a9e 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ahots-laguntza"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Blokeatu"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Edukiak ezkutatuta daude"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Gidalerro batzuk ezkutatu dira, gidalerroei jarraiki"</string>
<string name="safeMode" msgid="2788228061547930246">"Modu segurua"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Ukitu aukera gehiago ikusteko."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB arazketa konektatuta"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB arazketa desgaitzeko, ukitu hau."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Akatsen txostena sortzen…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Akatsen txostena partekatu nahi duzu?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Akatsen txostena partekatzen…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"IKT administratzaileak akatsen txostena eskatu du gailuko arazoa konpontzeko. Baliteke aplikazioak eta datuak partekatzea, eta agian motelago ibiliko da gailua aldi batez."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"IKT administratzaileak akatsen txostena eskatu du gailuko arazoa konpontzeko. Baliteke aplikazioak eta datuak partekatzea."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Agian motelago ibiliko da gailua aldi batez"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ONARTU"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"BAZTERTU"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"IKT administratzaileak akatsen txostena eskatu du gailuko arazoa konpontzeko. Baliteke aplikazioak eta datuak partekatzea."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"PARTEKATU"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"BAZTERTU"</string>
<string name="select_input_method" msgid="8547250819326693584">"Aldatu teklatua"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Aukeratu teklatuak"</string>
<string name="show_ime" msgid="2506087537466597099">"Erakutsi pantailan teklatu fisikoa aktibo dagoen bitartean"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Horma-papera"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Aldatu horma-papera"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Jakinarazpenak hautemateko zerbitzua"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Errealitate birtualeko hautemailea"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Baldintza-hornitzailea"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Jakinarazpenen laguntzailea"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Jakinarazpenen sailkapen-zerbitzua"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN eginbidea aktibatuta"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN konexioa aktibatu du"</string>
<string name="vpn_text" msgid="3011306607126450322">"Ukitu sarea kudeatzeko."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Berrezarri jatorrizko egoerara gailua ohi bezala erabiltzen jarraitu ahal izateko"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Ukitu informazio gehiago lortzeko."</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index feae1ca..eeab6d3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"دستیار صوتی"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"اکنون قفل شود"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"بیشتر از 999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"محتواها پنهان هستند"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"محتوا بر اساس خطمشی پنهان شده است"</string>
<string name="safeMode" msgid="2788228061547930246">"حالت ایمن"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"برای گزینههای بیشتر لمس کنید."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"اشکالزدایی USB متصل شد"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"غیرفعالکردن اشکالزداییUSB: با لمس آن."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"درحال گرفتن گزارش اشکال…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"گزارش اشکال به اشتراک گذاشته شود؟"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"درحال اشتراکگذاری گزارش اشکال…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"سرپرست فناوری اطلاعات شما برای کمک به عیبیابی این دستگاه، درخواست گزارش اشکال کرده است. ممکن است برنامهها و دادهها به اشتراک گذاشته شوند و سرعت دستگاهتان بهطور موقت کاهش یابد."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"سرپرست فناوری اطلاعات شما برای کمک به عیبیابی این دستگاه، گزارش اشکال درخواست کرده است. ممکن است برنامهها و دادهها به اشتراک گذاشته شوند."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"ممکن است سرعت دستگاهتان بهطور موقت کاهش یابد"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"پذیرفتن"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"نپذیرفتن"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"سرپرست فناوری اطلاعات شما برای کمک به عیبیابی این دستگاه، گزارش اشکال درخواست کرده است. ممکن است برنامهها و دادهها به اشتراک گذاشته شوند."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"اشتراکگذاری"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"نپذیرفتن"</string>
<string name="select_input_method" msgid="8547250819326693584">"تغییر صفحهکلید"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"انتخاب صفحهکلیدها"</string>
<string name="show_ime" msgid="2506087537466597099">"وقتی صفحهکلید فیزیکی فعال است این ویرایشگر را روی صفحه نگهمیدارد"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"کاغذدیواری"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"تغییر کاغذدیواری"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"شنونده اعلان"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"شنونده VR"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"ارائهدهنده وضعیت"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"دستیار اعلان"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"سرویس رتبهبندی اعلان"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN فعال شد"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN توسط <xliff:g id="APP">%s</xliff:g> فعال شده است"</string>
<string name="vpn_text" msgid="3011306607126450322">"برای مدیریت شبکه لمس کنید."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"بازنشانی کارخانهای برای استفاده عادی از این دستگاه"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"برای یادگیری بیشتر لمس کنید."</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 2f0a361..d45e0bd 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ääniapuri"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lukitse nyt"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Sisältö piilotettu"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sisältö on piilotettu käytännön perusteella."</string>
<string name="safeMode" msgid="2788228061547930246">"Suojattu tila"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Lisää vaihtoehtoja koskettamalla"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-vianetsintä yhdistetty"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Sulje USB-vianetsintä koskettamalla."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Luodaan virheraporttia…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Jaetaanko virheraportti?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Jaetaan virheraporttia…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Järjestelmänvalvoja pyysi virheraporttia voidakseen auttaa laitteen vianetsinnässä. Sovelluksia ja tietoja voidaan jakaa, ja laitteen toiminta voi hidastua väliaikaisesti."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Järjestelmänvalvoja pyysi virheraporttia voidakseen auttaa laitteen vianetsinnässä. Sovelluksia ja tietoja voidaan jakaa."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Tämä voi hidastaa laitteen toimintaa väliaikaisesti."</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"HYVÄKSY"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"HYLKÄÄ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Järjestelmänvalvoja pyysi virheraporttia voidakseen auttaa laitteen vianetsinnässä. Sovelluksia ja tietoja voidaan jakaa."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"JAA"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"HYLKÄÄ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Vaihda näppäimistö"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Valitse näppäimistöt"</string>
<string name="show_ime" msgid="2506087537466597099">"Pidä näytöllä, kun fyysinen näppäimistö on aktiivinen."</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Taustakuva"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Vaihda taustakuvaa"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Ilmoituskuuntelija"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Virtuaalitodellisuuden kuuntelija"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Ehtojen toimituspalvelu"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Ilmoitusapuri"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Ilmoitusten sijoituspalvelu"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN on aktivoitu"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> on aktivoinut VPN-yhteyden"</string>
<string name="vpn_text" msgid="3011306607126450322">"Voit hallinnoida verkkoa koskettamalla."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Palauta tehdasasetukset, jotta voit käyttää laitetta tavallisesti"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Lue lisätietoja koskettamalla."</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 4dce7e6..6fa6f49 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. vocale"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Verrouiller"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenus masqués"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenu masqué conformément aux politiques"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Touchez pour afficher plus d\'options."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Débogage USB connecté"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Appuyez pour désactiver le débogage USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Création d\'un rapport de bogue en cours..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Partager le rapport de bogue?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Partage du rapport de bogue en cours..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Votre administrateur informatique a demandé un rapport de bogue pour l\'aider à dépanner cet appareil. Les applications et les données peuvent être partagées. Cela pourrait temporairement ralentir votre appareil."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Votre administrateur informatique a demandé un rapport de bogue pour l\'aider à dépanner cet appareil. Les applications et les données peuvent être partagées."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Cela pourrait temporairement ralentir votre appareil."</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCEPTER"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"REFUSER"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Votre administrateur informatique a demandé un rapport de bogue pour l\'aider à dépanner cet appareil. Les applications et les données peuvent être partagées."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"PARTAGER"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REFUSER"</string>
<string name="select_input_method" msgid="8547250819326693584">"Changer de clavier"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Choisir les claviers"</string>
<string name="show_ime" msgid="2506087537466597099">"Afficher lorsque le clavier physique est activé"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fond d\'écran"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Changer de fond d\'écran"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Outil d\'écoute des notifications"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Écouteur de réalité virtuelle"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Fournisseur de conditions"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Assistant des notifications"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Service de classement des notifications"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activé"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activé par <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Appuyez ici pour gérer le réseau."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Rétablissez la configuration d\'usine pour utiliser cet appareil normalement"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Touchez ici pour en savoir plus."</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c8ef7d0..02ad9e6 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Assistance vocale"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Verrouiller"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenus masqués"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenu masqué conformément aux règles"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Appuyez pour afficher plus d\'options"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Débogage USB activé"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Appuyez pour désact. débogage USB"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Création du rapport de bug…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Partager le rapport de bug ?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Partage du rapport de bug…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Votre administrateur informatique a demandé un rapport de bug pour l\'aider à résoudre le problème lié à cet appareil. Il est possible que des applications et des données soient partagées et que votre appareil ralentisse temporairement."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Votre administrateur informatique a demandé un rapport de bug pour l\'aider à résoudre le problème lié à cet appareil. Il est possible que des applications et des données soient partagées."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Ceci risque de ralentir temporairement votre appareil."</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCEPTER"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"REFUSER"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Votre administrateur informatique a demandé un rapport de bug pour l\'aider à résoudre le problème lié à cet appareil. Il est possible que des applications et des données soient partagées."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"PARTAGER"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REFUSER"</string>
<string name="select_input_method" msgid="8547250819326693584">"Changer de clavier"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Sélectionner des claviers"</string>
<string name="show_ime" msgid="2506087537466597099">"Afficher lorsque le clavier physique est activé"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fond d\'écran"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Changer de fond d\'écran"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Outil d\'écoute des notifications"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Écouteur de réalité virtuelle"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Fournisseur de conditions"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Assistant de notifications"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Service de classement des notifications"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activé"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activé par <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Appuyez ici pour gérer le réseau."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Rétablir la configuration d\'usine pour utiliser cet appareil normalement"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Appuyez ici pour en savoir plus."</string>
</resources>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 41c600d..cb0ac76 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asistente voz"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear agora"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contido oculto"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Ocultouse contido por causa da política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Toca para ver máis opcións."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración USB conectada"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toca aquí para desactivala"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Creando informe de erros…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Queres compartir o informe de erros?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Compartindo informe de erros..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"O teu administrador de TI solicitou un informe de erros para axudar a solucionar os problemas deste dispositivo. É posible que se compartan aplicacións e datos e que se reduza a velocidade do teu dispositivo temporalmente."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"O teu administrador de TI solicitou un informe de erros para axudar a solucionar os problemas deste dispositivo. É posible que se compartan aplicacións e datos."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Esta acción pode reducir a velocidade do teu dispositivo temporalmente"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACEPTAR"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ANULAR"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"O teu administrador de TI solicitou un informe de erros para axudar a solucionar os problemas deste dispositivo. É posible que se compartan aplicacións e datos."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTIR"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ANULAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Cambiar teclado"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Seleccionar teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Manteno na pantalla mentres o teclado físico estea activo"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fondo de pantalla"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambiar fondo de pantalla"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Axente de escoita de notificacións"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Axente de escoita de RV"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Provedor de condicións"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Asistente de notificacións"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Servizo de clasificación de notificacións"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> activou a VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca aquí para xestionar a rede."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Restablecemento da configuración de fábrica para usar este dispositivo con normalidade"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toca para acceder a máis información"</string>
</resources>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 0bda139..6270867 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"વૉઇસ સહાય"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"હવે લૉક કરો"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"સામગ્રીઓ છુપાવેલ છે"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"નીતિ દ્વારા સામગ્રી છુપાવાઈ"</string>
<string name="safeMode" msgid="2788228061547930246">"સુરક્ષિત મોડ"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"વધુ વિકલ્પો માટે ટચ કરો."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ડીબગિંગ કનેક્ટ થયું."</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB ડીબગિંગ અક્ષમ કરવા માટે ટચ કરો."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"બગ રિપોર્ટ લઈ રહ્યાં છે…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"બગ રિપોર્ટ શેર કરીએ?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"બગ રિપોર્ટ શેર કરી રહ્યાં છે…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"તમારા IT વ્યવસ્થાપક એ આ ઉપકરણની સમસ્યા નિવારણમાં સહાય માટે બગ રિપોર્ટની વિનંતી કરી છે. ઍપ્લિકેશનો અને ડેટા શેર કરવામાં આવી શકે છે અને તમારા ઉપકરણને અસ્થાયી રૂપે ધીમું કરી શકે છે."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"તમારા IT વ્યવસ્થાપક એ આ ઉપકરણની સમસ્યા નિવારણમાં સહાય માટે બગ રિપોર્ટની વિનંતી કરી છે. ઍપ્લિકેશનો અને ડેટા શેર કરવામાં આવી શકે છે."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"આ અસ્થાયી રૂપે તમારા ઉપકરણને ધીમું કરી શકે છે"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"સ્વીકારો"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"નકારો"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"તમારા IT વ્યવસ્થાપક એ આ ઉપકરણની સમસ્યા નિવારણમાં સહાય માટે બગ રિપોર્ટની વિનંતી કરી છે. ઍપ્લિકેશનો અને ડેટા શેર કરવામાં આવી શકે છે."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"શેર કરો"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"નકારો"</string>
<string name="select_input_method" msgid="8547250819326693584">"કીબોર્ડ બદલો"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"કીબોર્ડ્સ પસંદ કરો"</string>
<string name="show_ime" msgid="2506087537466597099">"જ્યારે ભૌતિક કીબોર્ડ સક્રિય હોય ત્યારે તેને સ્ક્રીન પર રાખો"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"વૉલપેપર"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"વૉલપેપર બદલો"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"સૂચના સાંભળનાર"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR સાંભળનાર"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"શરત પ્રદાતા"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"સૂચના સહાયક"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"સૂચના રેંકર સેવા"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN સક્રિય કર્યું"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> દ્વારા VPN સક્રિય થયું"</string>
<string name="vpn_text" msgid="3011306607126450322">"નેટવર્કને સંચાલિત કરવા માટે ટચ કરો."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"આ ઉપકરણને સામાન્ય રીતે ઉપયોગ કરવા માટે ફેક્ટરી રીસેટ કરો"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"વધુ જાણવા માટે ટચ કરો."</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 995c9c7..82e1d83 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"वॉइस सहायक"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"अभी लॉक करें"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"छिपी हुई सामग्री"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"सामग्री पॉलिसी के द्वारा छिपी हुई है"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"और विकल्पों के लिए स्पर्श करें."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB डीबग कनेक्ट किया गया"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB डीबग करना अक्षम करने के लिए स्पर्श करें."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"बग रिपोर्ट प्राप्त की जा रही है…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"बग रिपोर्ट साझा करें?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"बग रिपोर्ट साझा की जा रही है…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"इस डिवाइस के समस्या निवारण में सहायता हेतु आपके आईटी व्यवस्थापक ने बग रिपोर्ट के लिए अनुरोध किया है. ऐप्स और डेटा को साझा किया जा सकता है और आपके डिवाइस की गति अस्थायी रूप से धीमी हो सकती है."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"आपके आईटी व्यवस्थापक ने इस डिवाइस के समस्या निवारण में सहायता के लिए एक बग रिपोर्ट का अनुरोध किया है. ऐप्स और डेटा साझा किए जा सकते हैं."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"इससे आपका डिवाइस अस्थायी रूप से धीमा हो सकता है"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"स्वीकार करें"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"अस्वीकार करें"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"आपके आईटी व्यवस्थापक ने इस डिवाइस के समस्या निवारण में सहायता के लिए एक बग रिपोर्ट का अनुरोध किया है. ऐप्स और डेटा को साझा किया जा सकता है."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"साझा करें"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"अस्वीकार करें"</string>
<string name="select_input_method" msgid="8547250819326693584">"कीबोर्ड बदलें"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"कीबोर्ड चुनें"</string>
<string name="show_ime" msgid="2506087537466597099">"भौतिक कीबोर्ड के सक्रिय होने के दौरान इसे स्क्रीन पर बनाए रखें"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"वॉलपेपर"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"वॉलपेपर बदलें"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"नोटिफिकेशन श्रवणकर्ता"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR श्रोता"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"स्थिति प्रदाता"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"नोटिफिकेशन सहायक"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"नोटिफ़िकेशन रैंकर सेवा"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN सक्रिय"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN को <xliff:g id="APP">%s</xliff:g> द्वारा सक्रिय किया गया है"</string>
<string name="vpn_text" msgid="3011306607126450322">"नेटवर्क प्रबंधित करने के लिए स्पर्श करें."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"इस डिवाइस का सामान्य रूप से उपयोग करने के लिए फ़ैक्टरी रीसेट करें"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"अधिक जानने के लिए स्पर्श करें."</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 823181d..cd23a1a 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -231,7 +231,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Glasovna pomoć"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Zaključaj sada"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Sadržaj je skriven"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Sadržaj je skriven prema pravilima"</string>
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
@@ -1060,13 +1059,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Dodirnite za više opcija."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Priključen je alat za uklanjanje pogrešaka USB-om"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Dodirnite da se onemogući otklanjanje pogrešaka USB-om."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Izrada izvješća o programskoj pogrešci…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Želite li podijeliti izvješće o programskoj pogrešci?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Dijeljenje izvješća o programskoj pogrešci…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"IT administrator zatražio je izvješće o programskoj pogrešci radi lakšeg rješavanja problema na uređaju. Moguće je da će se aplikacije i podaci dijeliti, što bi moglo privremeno usporiti uređaj."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"IT administrator zatražio je izvješće o programskoj pogrešci radi lakšeg rješavanja problema na uređaju. Moguće je da će se aplikacije i podaci dijeliti."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"To bi moglo privremeno usporiti uređaj"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"PRIHVATI"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ODBIJ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"IT administrator zatražio je izvješće o programskoj pogrešci radi lakšeg rješavanja problema na uređaju. Moguće je da će se aplikacije i podaci dijeliti."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"DIJELI"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODBIJ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Promjena tipkovnice"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Odaberi tipkovnice"</string>
<string name="show_ime" msgid="2506087537466597099">"Zadržava se na zaslonu dok je fizička tipkovnica aktivna"</string>
@@ -1145,8 +1143,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Pozadinska slika"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Promjena pozadinske slike"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Slušatelj obavijesti"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Slušatelj virtualne stvarnosti"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Davatalj uvjeta"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Pomoćnik za obavijesti"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Usluga rangiranja obavijesti"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviran"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikacija <xliff:g id="APP">%s</xliff:g> aktivirala je VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dodirnite za upravljanje mrežom."</string>
@@ -1584,4 +1583,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Uređaj je vraćen na tvorničke postavke da biste ga mogli upotrebljavati na pravilan način"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Dodirnite da biste saznali više."</string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 015e13d..ffa8d21 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Hangsegéd"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Zárolás most"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Tartalom elrejtve"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"A tartalom irányelv miatt elrejtve"</string>
<string name="safeMode" msgid="2788228061547930246">"Biztonsági üzemmód"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Érintse meg a további lehetőségekhez."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB hibakereső csatlakoztatva"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Érintse meg az USB hibakeresés kikapcsolásához."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Hibajelentés készítése…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Megosztja a hibajelentést?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Hibajelentés megosztása…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"A rendszergazda hibajelentést kért, hogy segíthessen az eszközzel kapcsolatos probléma megoldásában. Előfordulhat, hogy a rendszer megosztja az alkalmazásokat és adatokat, továbbá eszköze átmenetileg lelassulhat."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"A rendszergazda hibajelentést kért, hogy segíthessen az eszközzel kapcsolatos probléma megoldásában. Előfordulhat, hogy a rendszer megosztja az alkalmazásokat és adatokat."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Előfordulhat, hogy eszköze emiatt átmenetileg lelassul"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ELFOGADOM"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ELUTASÍTOM"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"A rendszergazda hibajelentést kért, hogy segíthessen az eszközzel kapcsolatos probléma megoldásában. Előfordulhat, hogy a rendszer megosztja az alkalmazásokat és adatokat."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"MEGOSZTÁS"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ELUTASÍTÁS"</string>
<string name="select_input_method" msgid="8547250819326693584">"Billentyűzet megváltoztatása"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Billentyűzetek kiválasztása"</string>
<string name="show_ime" msgid="2506087537466597099">"Maradjon a képernyőn, amíg a billentyűzet aktív"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Háttérkép"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Háttérkép megváltoztatása"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Értesítésfigyelő"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Virtuálisvalóság-figyelő"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Feltételbiztosító"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Értesítési segéd"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Értesítésrangsoroló szolgáltatás"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiválva"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A(z) <xliff:g id="APP">%s</xliff:g> aktiválta a VPN-t"</string>
<string name="vpn_text" msgid="3011306607126450322">"Érintse meg a hálózat kezeléséhez."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Gyári beállítások visszaállítása az eszköz normál módú használatához"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Érintse meg a további információkért."</string>
</resources>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 3a56491..909a5c9 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ձայնային օգնութ"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Կողպել հիմա"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Բովանդակությունը թաքցված է"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Բովանդակությունը թաքցվել է ըստ քաղաքականության"</string>
<string name="safeMode" msgid="2788228061547930246">"Անվտանգ ռեժիմ"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Հպեք՝ լրացուցիչ ընտրանքների համար:"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB վրիպազերծումը միացված է"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Հպեք` USB կարգաբերումը կասեցնելու համար:"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Վրիպակի զեկույցի ստեղծում…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Տրամադրե՞լ վրիպակի զեկույցը:"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Վրիպակի զեկույցի տրամադրում…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Այս սարքի անսարքությունների վերացման նպատակով ձեր ՏՏ ադմինիստրատորին անհրաժեշտ է վրիպակի զեկույց: Կարող են տրամադրվել տեղեկություններ ձեր հավելվածների մասին և այլ տվյալներ, իսկ սարքի աշխատանքը կարող է ժամանակավորապես դանդաղել:"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Այս սարքի անսարքությունների վերացման նպատակով ձեր ՏՏ ադմինիստրատորին անհրաժեշտ է վրիպակի զեկույց: Կարող են տրամադրվել տեղեկություններ ձեր հավելվածներնի մասին և այլ տվյալներ:"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Այդ ընթացքում ձեր սարքի աշխատանքը կարող է դանդաղել"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ԸՆԴՈՒՆԵԼ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ՄԵՐԺԵԼ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Այս սարքի անսարքությունների վերացման նպատակով ձեր ՏՏ ադմինիստրատորին անհրաժեշտ է վրիպակի զեկույց: Կարող են տրամադրվել տեղեկություններ ձեր հավելվածների մասին և այլ տվյալներ:"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ՏՐԱՄԱԴՐԵԼ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ՄԵՐԺԵԼ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Փոխել ստեղնաշարը"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Ընտրել ստեղնաշար"</string>
<string name="show_ime" msgid="2506087537466597099">"Պահել էկրանին մինչդեռ ֆիզիկական ստեղնաշարն ակտիվ է"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Պաստառ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Փոխել պաստառը"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Ծանուցման ունկնդիր"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR ունկնդրիչ"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Պայմանների մատակարար"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Ծանուցումների օգնական"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Ըստ կարևորության ծանուցումների դասակարգման ծառայություն"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN-ը ակտիվացված է"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-ն ակտիվացված է <xliff:g id="APP">%s</xliff:g>-ի կողմից"</string>
<string name="vpn_text" msgid="3011306607126450322">"Հպեք` ցանցի կառավարման համար:"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Սարքը սովորական ռեժիմում օգտագործելու համար կատարեք գործարանային վերակայում"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Հպեք՝ ավելին իմանալու համար:"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 2391ef7..14b7526 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Bantuan Suara"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Kunci sekarang"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Konten tersembunyi"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Konten disembunyikan menurut kebijakan"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode aman"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Sentuh untuk opsi lainnya."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Debugging USB terhubung"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Sentuh untuk menonaktifkan debugging USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Mengambil laporan bug…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Bagikan laporan bug?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Membagikan laporan bug..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Admin IT meminta laporan bug untuk membantu memecahkan masalah perangkat ini. Aplikasi dan data mungkin dibagikan dan untuk sementara perangkat mungkin menjadi lambat."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Admin IT meminta laporan bug untuk membantu memecahkan masalah perangkat ini. Aplikasi dan data mungkin dibagikan."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Tindakan ini mungkin memperlambat perangkat untuk sementara"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"SETUJU"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"TOLAK"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Admin IT meminta laporan bug untuk membantu memecahkan masalah perangkat ini. Aplikasi dan data mungkin dibagikan."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"BAGIKAN"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"TOLAK"</string>
<string name="select_input_method" msgid="8547250819326693584">"Ubah keyboard"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Pilih keyboard"</string>
<string name="show_ime" msgid="2506087537466597099">"Pertahankan di layar jika keyboard fisik masih aktif"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ubah wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Pendengar pemberitahuan"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Pemroses Realitas Maya"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Penyedia ketentuan"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Asisten notifikasi"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Layanan penentu peringkat notifikasi"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN diaktifkan"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengelola jaringan."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Dikembalikan ke setelan pabrik untuk menggunakan perangkat ini secara normal"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Sentuh untuk mempelajari lebih lanjut."</string>
</resources>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 8246584..e3278f3 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Raddaðstoð"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Læsa núna"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Innihald falið"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Efni falið með reglu"</string>
<string name="safeMode" msgid="2788228061547930246">"Örugg stilling"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Snertu til að fá fleiri valkosti."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-villuleit tengd"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Snertu til að slökkva á USB-villuleit."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Tekur við villutilkynningu…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Deila villutilkynningu?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Deilir villutilkynningu..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Kerfisstjórinn þinn óskaði eftir villutilkynningu til að auðvelda úrræðaleit á þessu tæki. Forritum og gögnum verður hugsanlega deilt og mögulegt er að þetta hægi tímabundið á tækinu."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Kerfisstjórinn þinn óskaði eftir villutilkynningu til að auðvelda úrræðaleit á þessu tæki. Forritum og gögnum verður hugsanlega deilt."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Hugsanlega hægir þetta tímabundið á tækinu þínu"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"SAMÞYKKJA"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"HAFNA"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Kerfisstjórinn þinn óskaði eftir villutilkynningu til að auðvelda úrræðaleit á þessu tæki. Forritum og gögnum verður hugsanlega deilt."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"DEILA"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"HAFNA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Skipta um lyklaborð"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Velja lyklaborð"</string>
<string name="show_ime" msgid="2506087537466597099">"Haltu því á skjánum meðan vélbúnaðarlyklaborðið er virkt"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Veggfóður"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Skipta um veggfóður"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Tilkynningahlustun"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Sýndarveruleikavöktun"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Skilyrðaveita"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Tilkynningaaðstoð"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Tilkynningaröðun"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN virkjað"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN er virkjað með <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Snertu til að hafa umsjón með netinu."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Núllstilltu til að nota þetta tæki í venjulegri stillingu"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Snertu til að fá frekari upplýsingar."</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 912f154..6f1c1ec 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Blocca ora"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Contenuti nascosti"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Contenuti nascosti in base alle norme"</string>
<string name="safeMode" msgid="2788228061547930246">"Modalità provvisoria"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Tocca per visualizzare più opzioni."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Debug USB collegato"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Tocca per disattivare il debug USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Recupero della segnalazione di bug…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Condividere la segnalazione di bug?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Condivisione della segnalazione di bug…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"L\'amministratore IT ha richiesto una segnalazione di bug per poter risolvere più facilmente i problemi di questo dispositivo. Potrebbero essere condivisi dati e app e il dispositivo potrebbe rallentare temporaneamente."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"L\'amministratore IT ha richiesto una segnalazione di bug per poter risolvere più facilmente i problemi di questo dispositivo. Potrebbero essere condivisi dati e app."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Il dispositivo potrebbe rallentare temporaneamente"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCETTO"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"RIFIUTO"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"L\'amministratore IT ha richiesto una segnalazione di bug per poter risolvere più facilmente i problemi di questo dispositivo. Potrebbero essere condivisi dati e app."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"CONDIVIDI"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RIFIUTO"</string>
<string name="select_input_method" msgid="8547250819326693584">"Cambia tastiera"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Scegli tastiera"</string>
<string name="show_ime" msgid="2506087537466597099">"Tieni sullo schermo quando è attiva la tastiera fisica"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Sfondo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Cambia sfondo"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Listener di notifica"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Listener realtà virtuale"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Provider condizioni"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Assistente notifica"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Servizio di classificazione delle notifiche"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN attiva"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN attivata da <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tocca per gestire la rete."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Esegui il ripristino dei dati di fabbrica per utilizzare normalmente il dispositivo"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Tocca per ulteriori informazioni."</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index c334a3c..c79befa 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -233,7 +233,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"נעל עכשיו"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"התוכן מוסתר"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"התוכן מוסתר על ידי המדיניות"</string>
<string name="safeMode" msgid="2788228061547930246">"מצב בטוח"</string>
@@ -1068,13 +1067,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"גע להצגת עוד אפשרויות."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"ניפוי באגים של USB מחובר"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"גע כדי להשבית ניפוי באגים בהתקן ה-USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"עיבוד דוח על באג..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"האם לשתף דוח על באג?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"שיתוף דוח על באג…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"מנהל ה-IT ביקש דוח על באג כדי לסייע בפתרון בעיות במכשיר זה. ייתכן שאפליקציות ונתונים ישותפו. פעילות המכשיר עשויה להאט באופן זמני."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"מנהל ה-IT שלך ביקש דוח על באג כדי לסייע בפתרון בעיות במכשיר זה. ייתכן שאפליקציות ונתונים ישותפו."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"קצב הפעולה של המכשיר עשוי להיות איטי יותר באופן זמני כתוצאה מכך"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"אישור"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"דחייה"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"מנהל ה-IT שלך ביקש דוח על באג כדי לסייע בפתרון בעיות במכשיר זה. ייתכן שאפליקציות ונתונים ישותפו."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"שתף"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"דחה"</string>
<string name="select_input_method" msgid="8547250819326693584">"שינוי מקלדת"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"בחר מקלדות"</string>
<string name="show_ime" msgid="2506087537466597099">"השאר אותו במסך בזמן שהמקלדת הפיזית פעילה"</string>
@@ -1153,8 +1151,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"טפט"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"שנה טפט"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"מאזין להתראות"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR ליסנר"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"ספק תנאי"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"אסיסטנט ההודעות"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"שירות של דירוג הודעות"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN מופעל"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN מופעל על ידי <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"גע כדי לנהל את הרשת."</string>
@@ -1603,4 +1602,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"איפוס להגדרות היצרן לצורך שימוש במכשיר זה באופן רגיל"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"גע לקבלת מידע נוסף."</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 9ee8e0e..5c97295 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -213,8 +213,7 @@
<string name="bugreport_option_interactive_title" msgid="8635056131768862479">"対話型レポート"</string>
<string name="bugreport_option_interactive_summary" msgid="8180152634022797629">"ほとんどの場合はこのオプションを使用します。レポートの進行状況を追跡し、問題についての詳細情報を確認することができます。レポート作成に時間がかかってもあまり使用されないセクションは省略されることがあります。"</string>
<string name="bugreport_option_full_title" msgid="6354382025840076439">"完全レポート"</string>
- <!-- no translation found for bugreport_option_full_summary (6687306111256813257) -->
- <skip />
+ <string name="bugreport_option_full_summary" msgid="6687306111256813257">"端末の反応がないとき、または速度が遅すぎるときにシステムへの影響を最小限に抑えたい場合は、このオプションを使用します。またすべてのレポート セクションを表示したい場合にもこのオプションを使用します。スクリーンショットは作成されず、詳細情報も表示できません。"</string>
<plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368">
<item quantity="other"><xliff:g id="NUMBER_1">%d</xliff:g> 秒後にバグレポートのスクリーンショットが作成されます。</item>
<item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> 秒後にバグレポートのスクリーンショットが作成されます。</item>
@@ -230,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"音声アシスト"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"今すぐロック"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g> 件)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"コンテンツが非表示"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ポリシーによって非表示になっているコンテンツ"</string>
<string name="safeMode" msgid="2788228061547930246">"セーフモード"</string>
@@ -1053,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"タップしてその他のオプションを表示"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USBデバッグが接続されました"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"タップしてUSBデバッグを無効化"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"バグレポートを取得しています…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"バグレポートを共有しますか?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"バグレポートの共有中…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"IT 管理者からこの端末のトラブルシューティングに役立てるためバグレポートを共有するようリクエストがありました。アプリやデータが共有され、端末の動作が一時的に遅くなる場合があります。"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"IT 管理者からこの端末のトラブルシューティングに役立てるためバグレポートを共有するようリクエストがありました。アプリやデータが共有されることがあります。"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"共有によって端末の動作が一時的に遅くなる場合があります"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"同意する"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"同意しない"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"IT 管理者からこの端末のトラブルシューティングに役立てるためバグレポートを共有するようリクエストがありました。アプリやデータが共有されることがあります。"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"共有する"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"共有しない"</string>
<string name="select_input_method" msgid="8547250819326693584">"キーボードの変更"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"キーボードの選択"</string>
<string name="show_ime" msgid="2506087537466597099">"物理キーボードが有効になっている間は、画面に表示されます"</string>
@@ -1138,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"壁紙"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"壁紙を変更"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"通知リスナー"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR リスナー"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"コンディションプロバイダ"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"通知アシスタント"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"通知ランカー サービス"</string>
<string name="vpn_title" msgid="19615213552042827">"VPNが有効になりました"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPNが<xliff:g id="APP">%s</xliff:g>により有効化されました"</string>
<string name="vpn_text" msgid="3011306607126450322">"タップしてネットワークを管理します。"</string>
@@ -1548,16 +1546,11 @@
<string name="language_picker_section_suggested" msgid="8414489646861640885">"言語の候補"</string>
<string name="language_picker_section_all" msgid="3097279199511617537">"すべての言語"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"検索"</string>
- <!-- no translation found for work_mode_off_title (8954725060677558855) -->
- <skip />
- <!-- no translation found for work_mode_off_message (3286169091278094476) -->
- <skip />
- <!-- no translation found for work_mode_turn_on (2062544985670564875) -->
- <skip />
- <!-- no translation found for suspended_package_title (3408150347778524435) -->
- <skip />
- <!-- no translation found for suspended_package_message (6341091587106868601) -->
- <skip />
+ <string name="work_mode_off_title" msgid="8954725060677558855">"Work モード OFF"</string>
+ <string name="work_mode_off_message" msgid="3286169091278094476">"仕事用プロファイルで、アプリ、バックグラウンド同期などの関連機能の使用を許可します。"</string>
+ <string name="work_mode_turn_on" msgid="2062544985670564875">"ON にする"</string>
+ <string name="suspended_package_title" msgid="3408150347778524435">"%1$sが無効です"</string>
+ <string name="suspended_package_message" msgid="6341091587106868601">"%1$s管理者によって無効になっています。詳しくは管理者までお問い合わせください。"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"新着メッセージがあります"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"表示するには SMS アプリを開きます"</string>
<string name="user_encrypted_title" msgid="9054897468831672082">"一部機能が制限されている可能性"</string>
@@ -1571,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"この端末は正常に使用するために出荷時設定にリセットされました"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"タップして詳細をご確認ください。"</string>
</resources>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 8e4e378..15167f7 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"ხმოვანი ასისტ."</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ახლა ჩაკეტვა"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"შიგთავსი დამალულია"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"შიგთავსი დამალულია წესების შესაბამისად"</string>
<string name="safeMode" msgid="2788228061547930246">"უსაფრთხო რეჟიმი"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"შეეხეთ დამატებითი პარამეტრებისთვის."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB გამართვა შეერთებულია"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"შეეხეთ, რათა შეწყვიტოთ USB-ის გამართვა."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"მიმდინარეობს ხარვეზის შესახებ ანგარიშის შექმნა…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"გსურთ ხარვეზის შესახებ ანგარიშის გაზიარება?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"მიმდინარეობს ხარვეზის შესახებ ანგარიშის გაზიარება…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"ამ მოწყობილობის პრობლემების აღმოფხვრაში დასახმარებლად, თქვენი IT ადმინისტრატორი ხარვეზის შესახებ ანგარიშს ითხოვს, რა დროსაც შეიძლება გაზიარდეს აპები და მონაცემები, ხოლო თქვენი მოწყობილობა დროებით შენელდეს."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"ამ მოწყობილობის პრობლემების აღმოფხვრაში დასახმარებლად, თქვენი IT ადმინისტრატორი ხარვეზის შესახებ ანგარიშს ითხოვს, რა დროსაც შეიძლება გაზიარდეს აპები და მონაცემები."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"ამან შეიძლება დროებით შეანელოს თქვენი მოწყობილობა"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"მიღება"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"უარყოფა"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"ამ მოწყობილობის პრობლემების აღმოფხვრაში დასახმარებლად, თქვენი IT ადმინისტრატორი ხარვეზის შესახებ ანგარიშს ითხოვს, რა დროსაც შეიძლება გაზიარდეს აპები და მონაცემები."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"გაზიარება"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"უარყოფა"</string>
<string name="select_input_method" msgid="8547250819326693584">"კლავიატურის შეცვლა"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"კლავიატურების არჩევა"</string>
<string name="show_ime" msgid="2506087537466597099">"აქტიური ფიზიკური კლავიატურისას ეკრანზე შენარჩუნება"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ფონი"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ფონის შეცვლა"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"შეტყობინებების მსმენელი"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"ვირტუალური რეალობის მსმენელი"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"მდგომარეობის პროვაიდერი"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"შეტყობინებათა ასისტენტი"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"შეტყობინებების მნიშვნელობის დონის შეფასების სერვისი"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN გააქტიურებულია"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN გააქტიურებულია <xliff:g id="APP">%s</xliff:g>-ის მიერ"</string>
<string name="vpn_text" msgid="3011306607126450322">"შეეხეთ ქსელის სამართავად."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"ამ მოწყობილობის ჩვეულებრივად გამოსაყენებლად, დააბრუნეთ ქარხნული პარამეტრები"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"შეეხეთ მეტის გასაგებად."</string>
</resources>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index c14bc68..6d31dd1 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Дауыс көмекшісі"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Қазір бекіту"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Мазмұн жасырылған"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Мазмұн саясатқа сай жасырылған"</string>
<string name="safeMode" msgid="2788228061547930246">"Қауіпсіз режим"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Қосымша параметрлер үшін түртіңіз."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB жөндеу қосылған"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB жөндеуді өшіру үшін түртіңіз."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Қате туралы есеп алынуда…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Қате туралы есепті бөлісу керек пе?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Қате туралы есеп бөлісілуде…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"АТ әкімшісі осы құрылғы ақауларын жоюға көмектесу үшін қате туралы есепті сұрады. Қолданбалар және деректер бөлісілуі әрі құрылғының жұмысы уақытша баяулауы мүмкін."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"АТ әкімшісі осы құрылғы ақауларын жоюға көмектесу үшін қате туралы есепті сұрады. Қолданбалар және деректер бөлісілуі мүмкін."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Бұл құрылғы жұмысын уақытша баяулатуы мүмкін"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ҚАБЫЛДАУ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ҚАБЫЛДАМАУ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"АТ әкімшісі осы құрылғы ақауларын жоюға көмектесу үшін қате туралы есепті сұрады. Қолданбалар мен деректерді бөлісуі мүмкін."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"БӨЛІСУ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ҚАБЫЛДАМАУ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Пернетақтаны өзгерту"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Пернетақталарды таңдау"</string>
<string name="show_ime" msgid="2506087537466597099">"Физикалық пернетақта белсенді кезде оны экранда ұстау"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Артқы фоны"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Артқы фонын өзгерту"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Хабар бақылағыш"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Виртуалды шынайылық тыңдаушысы"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Шарт провайдері"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Хабарландыру көмекшісі"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Хабарландыруларды жіктеу қызметі"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN белсенді"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"ВЖЭ <xliff:g id="APP">%s</xliff:g> арқылы қосылған"</string>
<string name="vpn_text" msgid="3011306607126450322">"Желіні басқару үшін түрту."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Осы құрылғыны әдеттегідей пайдалану үшін зауыттық параметрлерді қалпына келтіріңіз"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Қосымша мәліметтер алу үшін түртіңіз."</string>
</resources>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 0becd32..e3e46b0 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"ជំនួយសម្លេង"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ចាក់សោឥឡូវនេះ"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"បានលាក់មាតិកា"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"មាតិកាត្រូវបានលាក់ដោយផ្អែកលើគោលការណ៍"</string>
<string name="safeMode" msgid="2788228061547930246">"របៀបសុវត្ថិភាព"</string>
@@ -1054,13 +1053,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"ប៉ះដើម្បីបានជម្រើសថែមទៀត។"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"បានភ្ជាប់ការកែកំហុសយូអេសប៊ី"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"ប៉ះ ដើម្បីបិទការកែកំហុសយូអេសប៊ី។"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"កំពុងទទួលយករបាយការណ៍កំហុស…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ចែករំលែករបាយការណ៍កំហុសឬ?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"កំពុងចែករំលែករបាយកំហុស…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"អ្នកគ្រប់គ្រងផ្នែកព័ត៌មានវិទ្យារបស់អ្នកបានស្នើរបាយការណ៍កំហុសដើម្បីដោះស្រាយកំហុសឧបករណ៍នេះ។ កម្មវិធី និងទិន្នន័យអាចនឹងត្រូវបានចែករំលែក ហើយឧបករណ៍របស់អ្នកអាចនឹងយឺតជាបណ្តោះអាសន្ន។"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"អ្នកគ្រប់គ្រងផ្នែកព័ត៌មានវិទ្យារបស់អ្នកបានស្នើរបាយការណ៍កំហុសដើម្បីដោះស្រាយកំហុសឧបករណ៍នេះ។ កម្មវិធី និងទិន្នន័យអាចនឹងត្រូវបានចែករំលែក។"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"វាអាចនឹងធ្វើឲ្យឧបករណ៍របស់អ្នកយឺតជាបណ្តោះអាសន្ន។"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ព្រមទទួល"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"បដិសេធ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"អ្នកគ្រប់គ្រងផ្នែកព័ត៌មានវិទ្យារបស់អ្នកបានស្នើរបាយការណ៍កំហុសដើម្បីដោះស្រាយកំហុសឧបករណ៍នេះ។ កម្មវិធី និងទិន្នន័យអាចនឹងត្រូវបានចែករំលែក។"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ចែករំលែក"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"បដិសេធ"</string>
<string name="select_input_method" msgid="8547250819326693584">"ប្ដូរក្ដារចុច"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"ជ្រើសក្ដារចុច"</string>
<string name="show_ime" msgid="2506087537466597099">"ទុកវានៅលើអេក្រង់ខណៈពេលក្តារចុចពិតប្រាកដកំពុងសកម្ម"</string>
@@ -1139,8 +1137,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ផ្ទាំងរូបភាព"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ប្ដូរផ្ទាំងរូបភាព"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"កម្មវិធីស្ដាប់ការជូនដំណឹង"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"កម្មវិធីស្តាប់ VR"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"ក្រុមហ៊ុនផ្ដល់លក្ខខណ្ឌ"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"ជំនួយការជូនដំណឹង"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"សេវាកម្មវាយតម្លៃការជូនដំណឹង"</string>
<string name="vpn_title" msgid="19615213552042827">"បានធ្វើឲ្យ VPN សកម្ម"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"បានធ្វើឲ្យ VPN សកម្មដោយ <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"ប៉ះ ដើម្បីគ្រប់គ្រងបណ្ដាញ។"</string>
@@ -1567,4 +1566,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"កំណត់ដូចចេញពីរោងចក្រឡើងវិញដើម្បីប្រើឧបករណ៍នេះតាមធម្មតា"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"ប៉ះ ដើម្បីស្វែងយល់បន្ថែម។"</string>
</resources>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index eeb156c..7a41d4b 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ಈಗ ಲಾಕ್ ಮಾಡಿ"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"ವಿಷಯಗಳನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ನೀತಿಯಿಂದ ಮರೆಮಾಡಲಾಗಿರುವ ವಿಷಯಗಳು"</string>
<string name="safeMode" msgid="2788228061547930246">"ಸುರಕ್ಷಿತ ಮೋಡ್"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗೆ ಸ್ಪರ್ಶಿಸಿ."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ಡೀಬಗಿಂಗ್ ಸಂಪರ್ಕ"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB ಡೀಬಗಿಂಗ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಸ್ಪರ್ಶಿಸಿ."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ದೋಷದ ವರದಿಯನ್ನು ತೆಗೆದುಕೊಳ್ಳಲಾಗುತ್ತಿದೆ…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ಬಗ್ ವರದಿಯನ್ನು ಹಂಚುವುದೇ?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"ಬಗ್ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"ಈ ಸಾಧನದ ಸಮಸ್ಯೆ ನಿವಾರಿಸುವುದಕ್ಕೆ ಸಹಾಯ ಮಾಡಲು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರು ಬಗ್ ವರದಿಯನ್ನು ವಿನಂತಿಸಿದ್ದಾರೆ. ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು ಹಾಗು ನಿಮ್ಮ ಸಾಧನವು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಧಾನಗೊಳ್ಳಬಹುದು."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"ಈ ಸಾಧನದ ಸಮಸ್ಯೆ ನಿವಾರಿಸಲು ಸಹಾಯ ಮಾಡಲು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರು ಬಗ್ ವರದಿಯನ್ನು ವಿನಂತಿಸಿದ್ದಾರೆ. ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"ಇದು ನಿಮ್ಮ ಸಾಧನವನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಧಾನಗೊಳಿಸಬಹುದು"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ಸಮ್ಮತಿಸು"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ತಿರಸ್ಕರಿಸು"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"ಈ ಸಾಧನದ ಸಮಸ್ಯೆ ನಿವಾರಿಸಲು ಸಹಾಯ ಮಾಡಲು ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರು ಬಗ್ ವರದಿಯನ್ನು ವಿನಂತಿಸಿದ್ದಾರೆ. ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ನಿರಾಕರಿಸು"</string>
<string name="select_input_method" msgid="8547250819326693584">"ಕೀಬೋರ್ಡ್ ಬದಲಿಸಿ"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"ಕೀಬೋರ್ಡ್ಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="show_ime" msgid="2506087537466597099">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್ ಸಕ್ರಿಯವಾಗಿರುವಾಗ ಅದನ್ನು ಪರದೆಯ ಮೇಲೆ ಇರಿಸಿಕೊಳ್ಳಿ"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ವಾಲ್ಪೇಪರ್"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ವಾಲ್ಪೇಪರ್ ಬದಲಿಸಿ"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"ಅಧಿಸೂಚನೆ ಕೇಳುಗ"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR ಕೇಳುವಿಕೆ"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"ಕಂಡೀಶನ್ ಪೂರೈಕೆದಾರರು"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"ಅಧಿಸೂಚನೆ ಸಹಾಯಕ"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"ಅಧಿಸೂಚನೆ ಶ್ರೇಣಿಯ ಸೇವೆ"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ಮೂಲಕ VPN ಸಕ್ರಿಯಗೊಂಡಿದೆ"</string>
<string name="vpn_text" msgid="3011306607126450322">"ನೆಟ್ವರ್ಕ್ ನಿರ್ವಹಿಸಲು ಸ್ಪರ್ಶಿಸಿ"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"ಈ ಸಾಧನವನ್ನು ಸಾಮಾನ್ಯ ರೀತಿಯಲ್ಲಿ ಬಳಸಲು ಫ್ಯಾಕ್ಟರಿ ರಿಸೆಟ್ ಮಾಡಿ"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಸ್ಪರ್ಶಿಸಿ."</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 3496962..b236d39 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"음성 지원"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"지금 잠그기"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>개)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"숨겨진 콘텐츠"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"콘텐츠가 정책에 의해 숨겨졌습니다."</string>
<string name="safeMode" msgid="2788228061547930246">"안전 모드"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"더 많은 옵션을 확인하려면 터치하세요."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB 디버깅 연결됨"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB 디버깅을 사용하지 않으려면 터치하세요."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"버그 보고서 가져오는 중..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"버그 보고서를 공유하시겠습니까?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"버그 신고서 공유 중..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"IT 관리자가 이 기기의 문제해결을 위해 버그 신고서를 요청했습니다. 앱과 데이터가 공유될 수 있으며 기기 속도가 일시적으로 느려질 수 있습니다."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"IT 관리자가 이 기기의 문제해결을 위해 버그 보고서를 요청했습니다. 앱과 데이터가 공유될 수 있습니다."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"이로 인해 일시적으로 기기 속도가 느려질 수 있습니다."</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"수락"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"거절"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"IT 관리자가 이 기기의 문제해결을 위해 버그 보고서를 요청했습니다. 앱과 데이터가 공유될 수 있습니다."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"공유"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"거부"</string>
<string name="select_input_method" msgid="8547250819326693584">"키보드 변경"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"키보드 선택"</string>
<string name="show_ime" msgid="2506087537466597099">"물리적 키보드가 활성 상태인 경우 화면에 켜 둠"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"배경화면"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"배경화면 변경"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"알림 수신기"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"가상 현실 리스너"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"조건 제공자"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"알림 어시스턴트"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"알림 순위 지정 서비스"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN이 활성화됨"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN이 <xliff:g id="APP">%s</xliff:g>에 의해 활성화됨"</string>
<string name="vpn_text" msgid="3011306607126450322">"네트워크를 관리하려면 터치하세요."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"정상적인 기기 사용을 위한 초기화"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"자세한 내용을 보려면 터치하세요."</string>
</resources>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index a6ee770..3a30b55 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Үн жардамчысы"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Азыр кулпулоо"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Мазмундар жашырылган"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Тийиштүү саясат боюнча жашырылган мазмундар"</string>
<string name="safeMode" msgid="2788228061547930246">"Коопсуз режим"</string>
@@ -1053,13 +1052,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Көбүрөөк параметр үчүн тийип коюңуз."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB мүчүлүштүктөрдү оңдоо туташтырылган"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB мүчүлүштүктөрдү жоюу мүмкүнчүлүгүн өчүрүү үчүн тийип коюңуз."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Мүчүлүштүк тууралуу кабар алынууда…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Мүчүлүштүк тууралуу баяндама бөлүшүлсүнбү?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Мүчүлүштүк тууралуу баяндама бөлүшүлүүдө…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Бул түзмөктүн бузулууларын аныктап оңдоо үчүн IT администраторуңуз мүчүлүштүктөр тууралуу маалыматты сурап жатат. Колдонмолор менен дайындар бөлүшүлүп, түзмөгүңүз жайыраак иштеп калышы мүмкүн."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Бул түзмөктүн бузулууларын аныктап оңдоо үчүн IT администраторуңуз мүчүлүштүктөр тууралуу маалыматты сурап жатат. Колдонмолор менен дайындар бөлүшүлүшү мүмкүн."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Ушуну менен түзмөгүңүздүн ылдамдыгы убактылуу төмөндөйт"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"КАБЫЛ АЛУУ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ЧЕТКЕ КАГУУ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Бул түзмөктүн бузулууларын аныктап оңдоо үчүн IT администраторуңуз мүчүлүштүктөр тууралуу маалыматты сурап жатат. Колдонмолор менен дайындар бөлүшүлүшү мүмкүн."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"БӨЛҮШҮҮ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ЧЕТКЕ КАГУУ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Баскычтопту өзгөртүү"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Баскычтопторду тандаңыз"</string>
<string name="show_ime" msgid="2506087537466597099">"Баскычтоп иштетилгенде экранда көрүнүп турсун"</string>
@@ -1138,8 +1136,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Тушкагаз"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Тушкагазды өзгөртүү"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Эскертүү тыңшагычы"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR режими"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Шарт түзүүчү"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Эскертме жардамчысы"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Эскертмени баалоо кызматы"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN иштетилди"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN <xliff:g id="APP">%s</xliff:g> аркылуу жандырылды"</string>
<string name="vpn_text" msgid="3011306607126450322">"желени башкаруу үчүн басыңыз."</string>
@@ -1566,4 +1565,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Бул түзмөктү кадимкидей колдонуу үчүн заводдук баштапкы абалына келтириңиз"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Көбүрөөк билүү үчүн тийип коюңуз."</string>
</resources>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index abe9b3f..5ad1786 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"ຊ່ວຍເຫຼືອທາງສຽງ"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ລັອກດຽວນີ້"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"ເນື້ອຫາຖືກເຊື່ອງໄວ້"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ເນື້ອຫາຖືກເຊື່ອງຕາມນະໂຍບາຍ"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"ສຳພັດສຳລັບທາງເລືອກເພີ່ມເຕີມ."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"ເຊື່ອມຕໍ່ການດີບັ໊ກຜ່ານ USB ແລ້ວ"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"ແຕະເພື່ອປິດການດີບັ໊ກຜ່ານ USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ກຳລັງຂໍລາຍງານຂໍ້ຜິດພາດ…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ແບ່ງປັນລາຍງານບັນຫາບໍ?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"ກຳລັງແບ່ງປັນລາຍງານບັນຫາ…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"ຜູ້ເບິ່ງແຍງລະບົບໄອທີຂອງທ່ານໄດ້ຮ້ອງຂໍລາຍງານຂໍ້ຜິດພາດເພື່ອຊ່ວຍແກ້ໄຂບັນຫາອຸປະກອນນີ້. ແອັບ ແລະ ຂໍ້ມູນອາດຖືກແບ່ງປັນ ແລະ ອຸປະກອນຂອງທ່ານອາດເຮັດວຽກຊ້າລົງຊົ່ວຄາວ."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"ຜູ້ເບິ່ງແຍງລະບົບໄອທີຂອງທ່ານໄດ້ຮ້ອງຂໍເອົາລາຍງານບັນຫາ ເພື່ອຊ່ວຍແກ້ໄຂບັນຫາໃຫ້ອຸປະກອນນີ້. ອາດຈະມີການແບ່ງປັນແອັບ ແລະ ຂໍ້ມູນນຳ."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"ນີ້ອາດຈະເຮັດໃຫ້ອຸປະກອນຂອງທ່ານຊ້າລົງຊົ່ວຄາວ"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ຍອມຮັບ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ປະຕິເສດ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"ຜູ້ເບິ່ງແຍງລະບົບໄອທີຂອງທ່ານໄດ້ຮ້ອງຂໍເອົາລາຍງານບັນຫາ ເພື່ອຊ່ວຍແກ້ໄຂບັນຫາໃຫ້ອຸປະກອນນີ້. ອາດຈະມີການແບ່ງປັນແອັບ ແລະ ຂໍ້ມູນນຳ."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ແບ່ງປັນ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ປະຕິເສດ"</string>
<string name="select_input_method" msgid="8547250819326693584">"ປ່ຽນແປ້ນພິມ"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"ເລືອກແປ້ນພິມ"</string>
<string name="show_ime" msgid="2506087537466597099">"ເປີດໃຊ້ໃຫ້ມັນຢູ່ໃນໜ້າຈໍໃນຂະນະທີ່ໃຊ້ແປ້ນພິມພາຍນອກຢູ່"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ພາບພື້ນຫຼັງ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ປ່ຽນພາບພື້ນຫຼັງ"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"ໂຕຟັງການແຈ້ງເຕືອນ"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"ຕົວຟັງ VR"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"ຜູ່ສະໜອງເງື່ອນໄຂ"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"ຕົວຊ່ວຍການແຈ້ງເຕືອນ"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"ບໍລິການຈັດອັນດັບການແຈ້ງເຕືອນ"</string>
<string name="vpn_title" msgid="19615213552042827">"ເປີດນຳໃຊ້ VPN ແລ້ວ"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"ເປີດໃຊ້ VPN ໂດຍ <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"ແຕະເພື່ອຈັດການເຄືອຂ່າຍ."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"ຣີເຊັດເປັນຄ່າໂຮງງານເພື່ອໃຊ້ອຸປະກອນນີ້ປົກກະຕິ"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"ແຕະເພື່ອສຶກສາເພີ່ມເຕີມ."</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ecf45b7..5496ada 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -233,7 +233,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Užrakinti dabar"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Turinys paslėptas"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Turinys paslėptas vadovaujantis politika"</string>
<string name="safeMode" msgid="2788228061547930246">"Saugos režimas"</string>
@@ -1068,13 +1067,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Palieskite, kad būtų rodoma daugiau parinkčių."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB derinimas prijungtas"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Neleisti USB derinimo."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Pateikiamas pranešimas apie riktą…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Bendrinti pranešimą apie riktą?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Bendrinamas pranešimas apie riktą..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Jūsų IT administratorius pateikė pranešimo apie riktą užklausą, kad galėtų padėti pašalinti triktis šiame įrenginyje. Programos bei duomenys gali būti bendrinami ir įrenginys gali laikinai lėčiau veikti."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Jūsų IT administratorius pateikė pranešimo apie riktą užklausą, kad galėtų padėti pašalinti triktis šiame įrenginyje. Programos ir duomenys gali būti bendrinami."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Dėl to įrenginys gali laikinai lėčiau veikti"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"SUTIKTI"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ATMESTI"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Jūsų IT administratorius pateikė pranešimo apie riktą užklausą, kad galėtų padėti pašalinti triktis šiame įrenginyje. Programos ir duomenys gali būti bendrinami."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"BENDRINTI"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ATMESTI"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klaviatūros keitimas"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Pasirinkti klaviatūras"</string>
<string name="show_ime" msgid="2506087537466597099">"Palikti ekrane, kol fizinė klaviatūra aktyvi"</string>
@@ -1153,8 +1151,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Darbalaukio fonas"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Keisti darbalaukio foną"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Pranešimų skaitymo priemonė"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Virtualiosios realybės apdorojimo priemonė"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Sąlygos teikėjas"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Pranešimų pagelbiklis"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Pranešimų reitingavimo paslauga"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN suaktyvintas"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN suaktyvino „<xliff:g id="APP">%s</xliff:g>“"</string>
<string name="vpn_text" msgid="3011306607126450322">"Palieskite, kad valdytumėte tinklą."</string>
@@ -1603,4 +1602,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Atkurkite gamyklinius nustatymus, kad galėtumėte įprastai naudoti šį įrenginį"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Palieskite, kad sužinotumėte daugiau."</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a572093..59ffdc3 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -231,7 +231,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Balss palīgs"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Bloķēt tūlīt"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"Pārsniedz"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Saturs paslēpts"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Saskaņā ar politiku saturs ir paslēpts."</string>
<string name="safeMode" msgid="2788228061547930246">"Drošais režīms"</string>
@@ -1060,13 +1059,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Citas opcijas"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB atkļūdošana ir pievienota."</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Iespējot USB atkļūdošanu."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Notiek kļūdas pārskata izveide…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vai kopīgot kļūdas pārskatu?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Notiek kļūdas pārskata kopīgošana…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Jūsu IT administrators pieprasīja kļūdas pārskatu, lai palīdzētu novērst problēmu šajā ierīcē. Var tikt kopīgotas lietotnes un dati, un jūsu ierīces darbība var īslaicīgi palēnināties."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Jūsu IT administrators pieprasīja kļūdas pārskatu, lai palīdzētu novērst problēmu šajā ierīcē. Var tikt kopīgotas lietotnes un dati."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Jūsu ierīces darbība var īslaicīgi palēnināties."</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"APSTIPRINĀT"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"NORAIDĪT"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Jūsu IT administrators pieprasīja kļūdas pārskatu, lai palīdzētu novērst problēmu šajā ierīcē. Var tikt kopīgotas lietotnes un dati."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"KOPĪGOT"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"NORAIDĪT"</string>
<string name="select_input_method" msgid="8547250819326693584">"Tastatūras maiņa"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Izvēlēties tastatūru"</string>
<string name="show_ime" msgid="2506087537466597099">"Paturēt ekrānā, kamēr ir aktīva fiziskā tastatūra"</string>
@@ -1145,8 +1143,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fona tapete"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Tapetes maiņa"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Paziņojumu uztvērējs"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR klausītājs"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Nosacījumu sniedzējs"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Paziņojumu palīgs"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Paziņojumu ranžēšanas pakalpojums"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ir aktivizēts."</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Lietojumprogramma <xliff:g id="APP">%s</xliff:g> aktivizēja VPN."</string>
<string name="vpn_text" msgid="3011306607126450322">"Pieskarieties, lai pārvaldītu tīklu."</string>
@@ -1584,4 +1583,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Rūpnīcas datu atiestatīšana ierīces normālai darbībai"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Pieskarieties, lai uzzinātu vairāk."</string>
</resources>
diff --git a/core/res/res/values-mcc232-mnc11/config.xml b/core/res/res/values-mcc232-mnc11/config.xml
new file mode 100644
index 0000000..91e37b4
--- /dev/null
+++ b/core/res/res/values-mcc232-mnc11/config.xml
@@ -0,0 +1,25 @@
+<?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.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23201</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc232-mnc12/config.xml b/core/res/res/values-mcc232-mnc12/config.xml
new file mode 100644
index 0000000..91e37b4
--- /dev/null
+++ b/core/res/res/values-mcc232-mnc12/config.xml
@@ -0,0 +1,25 @@
+<?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.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23201</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index a5a4ba7..d0a7b20 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Гласовна помош"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Заклучи сега"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Содржините се скриени"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Содржините се скриени поради политиката"</string>
<string name="safeMode" msgid="2788228061547930246">"Безбеден режим"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Допри за повеќе опции."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Поврзано е отстранување грешки преку УСБ"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Допрете за да се оневозможи отстранувањето грешки преку USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Се зема извештајот за грешки…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Да се сподели извештајот за грешки?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Се споделува извештај за грешки…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Администраторот за информатичка технологија побара извештај за грешки за да ви помогне во отстранувањето на грешките од уредот. Апликациите и податоците можеби ќе се споделат, а уредот можеби ќе биде привремено побавен при работата."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Вашиот администратор за информатичка технологија побара извештај за грешки за да помогне со отстранување на грешките на овој уред. Апликациите и податоците може да бидат споделени."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Ова може привремено да го забави уредот"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ПРИФАТИ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ОДБИЈ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Вашиот администратор за информатичка технологија побара извештај за грешки за да ви помогне во отстранувањето на грешките на овој уред. Апликациите и податоците може да бидат споделени."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"СПОДЕЛИ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ОДБИЈ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Измени тастатура"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Избери тастатури"</string>
<string name="show_ime" msgid="2506087537466597099">"Прикажувај го на екранот додека е активна физичката тастатура"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Тапет"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Промени тапет"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Слушател на известувања"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR слушател"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Давател на услов"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Помошник за известувања"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Услуга за рангирање известувања"</string>
<string name="vpn_title" msgid="19615213552042827">"Активирана VPN"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN е активирана со <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Допри за да управуваш со мрежата."</string>
@@ -1567,4 +1566,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Ресетирање на фабрички вредности за уредот да се користи нормално"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Допрете за да дознаете повеќе."</string>
</resources>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 8a97930..829e06a 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"വോയ്സ് സഹായം"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ഇപ്പോൾ ലോക്കുചെയ്യുക"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"കോൺടാക്റ്റുകൾ മറച്ചു"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"നയം അനുസരിച്ച് ഉള്ളടക്കം മറച്ചിരിക്കുന്നു"</string>
<string name="safeMode" msgid="2788228061547930246">"സുരക്ഷിത മോഡ്"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"കൂടുതൽ ഓപ്ഷനുകൾക്ക് സ്പർശിക്കൂ."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ഡീബഗ്ഗിംഗ് കണക്റ്റുചെയ്തു"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB ഡീബഗ്ഗിംഗ് ഓഫാക്കാൻ സ്പർശിക്കൂ."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ബഗ് റിപ്പോർട്ട് എടുക്കുന്നു…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ബഗ് റിപ്പോർട്ട് പങ്കിടണോ?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"ബഗ് റിപ്പോർട്ട് പങ്കിടുന്നു…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"ഈ ഉപകരണവുമായി ബന്ധപ്പെട്ട പ്രശ്നം പരിഹരിക്കുന്നതിന് നിങ്ങളുടെ ഐടി അഡ്മിൻ ഒരു ബഗ് റിപ്പോർട്ട് അഭ്യർത്ഥിച്ചു. ആപ്സും ഡാറ്റയും പങ്കിടപ്പെട്ടേക്കും, നിങ്ങളുടെ ഉപകരണത്തെ ഇത് താൽക്കാലികമായി മന്ദഗതിയിലാക്കിയേക്കാം."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"ഈ ഉപകരണത്തിലെ പ്രശ്നം പരിഹരിക്കുന്നതിന് നിങ്ങളുടെ ഐടി അഡ്മിൻ ഒരു ബഗ് റിപ്പോർട്ട് അഭ്യർത്ഥിച്ചു. ആപ്സും ഡാറ്റയും പങ്കിടപ്പെട്ടേക്കും."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"നിങ്ങളുടെ ഉപകരണത്തെ ഇത് താൽക്കാലികമായി മന്ദഗതിയിലാക്കിയേക്കാം"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"അംഗീകരിക്കുക"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"നിരസിക്കുക"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"ഈ ഉപകരണത്തിലെ പ്രശ്നം പരിഹരിക്കുന്നതിന് നിങ്ങളുടെ ഐടി അഡ്മിൻ ഒരു ബഗ് റിപ്പോർട്ട് അഭ്യർത്ഥിച്ചു. ആപ്സും ഡാറ്റയും പങ്കിടപ്പെട്ടേക്കും."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"പങ്കിടുക"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"നിരസിക്കുക"</string>
<string name="select_input_method" msgid="8547250819326693584">"കീബോഡ് മാറ്റുക"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"കീബോർഡുകൾ തിരഞ്ഞെടുക്കുക"</string>
<string name="show_ime" msgid="2506087537466597099">"ഫിസിക്കൽ കീബോർഡ് സജീവമായിരിക്കുമ്പോൾ സ്ക്രീനിൽ നിലനിർത്തുക"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"വാൾപേപ്പർ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"വാൾപേപ്പർ മാറ്റുക"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"അറിയിപ്പ് ലിസണർ"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR ലിസണർ"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"കണ്ടീഷൻ ദാതാവ്"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"അറിയിപ്പ് സഹായി"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"അറിയിപ്പ് റാങ്കർ സേവനം"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN സജീവമാക്കി"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ഉപയോഗിച്ച് VPN പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="vpn_text" msgid="3011306607126450322">"നെറ്റ്വർക്ക് നിയന്ത്രിക്കാൻ സ്പർശിക്കുക."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"ഈ ഉപകരണം സാധാരണ നിലയിൽ ഉപയോഗിക്കാൻ ഫാക്ടറി പുനഃസജ്ജീകരണം നടത്തുക"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"കൂടുതലറിയുന്നതിന് സ്പർശിക്കുക."</string>
</resources>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 9950003..9414f4e 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Дуут туслах"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Одоо түгжих"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Контентыг нуусан"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Удирдамжийн дагуу нуусан агуулга"</string>
<string name="safeMode" msgid="2788228061547930246">"Аюулгүй горим"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Нэмэлт сонголтыг харахын тулд дарна."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB дебаг холбогдсон"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB дебагийг идэвхгүй болгох бол хүрнэ үү."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Алдааны тайланг авч байна..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Алдааны тайланг хуваалцах уу?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Алдааны тайланг хуваалцаж байна..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Энэ төхөөрөмжийн асуудлыг шийдвэрлэхийнт тулд таны IT админ алдааны тайланг хүслээ. Апп болон өгөгдлийг хуваалцаж, таны төхөөрөмж бага зэрэг удаан ажиллаж болзошгүй."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Энэ төхөөрөмжийн асуудлыг шийдвэрлэхийн тулд таны IT админ алдааны тайланг хүслээ. Апп болон өгөгдлийг хуваалцсан байж болзошгүй."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Энэ нь таны төхөөрөмжийг түр хугацаанд удаашруулж болзошгүй"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ЗӨВШӨӨРӨХ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ТАТГАЛЗАХ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Энэ төхөөрөмжийн асуудлыг шийдвэрлэхийн тулд таны IT админ алдааны тайланг хүслээ. Апп болон өгөгдлийг хуваалцсан байж болзошгүй."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ХУВААЛЦАХ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ТАТГАЛЗАХ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Гарыг өөрчлөх"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Гар сонгох"</string>
<string name="show_ime" msgid="2506087537466597099">"Бодит гар идэвхтэй үед үүнийг дэлгэцэнд харуулна уу"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Ханын зураг"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ханын зураг солих"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Мэдэгдэл сонсогч"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR сонсогч"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Нөхцөл нийлүүлэгч"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Мэдэгдлийн туслагч"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Мэдэгдлийг ангилах үйлчилгээ"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN идэвхтэй болов"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-г <xliff:g id="APP">%s</xliff:g> идэвхтэй болгов"</string>
<string name="vpn_text" msgid="3011306607126450322">"Сүлжээг удирдах бол хүрнэ үү."</string>
@@ -1563,4 +1562,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Энэ төхөөрөмжийг хэвийн ашиглахын тулд үйлдвэрийн тохиргоонд дахин тохируулна уу"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Дэлгэрэнгүй үзэх бол дарна уу."</string>
</resources>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 968331a..67351ea 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"व्हॉइस सहाय्य"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"आता लॉक करा"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"लपविलेली सामग्री"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"धोरणाद्वारे सामग्री लपविली"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"अधिक पर्यायांसाठी स्पर्श करा."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB डीबग करणे कनेक्ट केले"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB डीबग करणे अक्षम करण्यासाठी स्पर्श करा."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"दोष अहवाल घेत आहे..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"बग अहवाल सामायिक करायचा?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"दोष अहवाल सामायिक करीत आहे..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"आपल्या IT प्रशासकाने या डिव्हाइसची समस्या निवारण करण्यात मदत करण्यासाठी एका दोष अहवालाची विनंती केली. अॅप्स आणि डेटा सामायिक केले जाऊ शकतात आणि आपले डिव्हाइस तात्पुरते धीमे होऊ शकते."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"आपल्या IT प्रशासकाने या डिव्हाइसच्या समस्येचे निवारण करण्यात मदत करण्यासाठी दोष अहवालाची विनंती केली. अॅप्स आणि डेटा सामायिक केला जाऊ शकतो."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"हे तात्पुरता आपले डिव्हाइस धिमे करू शकते."</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"स्वीकार करा"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"नकार द्या"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"आपल्या IT प्रशासकाने या डिव्हाइसच्या समस्येचे निवारण करण्यात मदत करण्यासाठी दोष अहवालाची विनंती केली. अॅप्स आणि डेटा सामायिक केले जाऊ शकतात."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"सामायिक करा"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"नकार द्या"</string>
<string name="select_input_method" msgid="8547250819326693584">"कीबोर्ड बदला"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"कीबोर्ड निवडा"</string>
<string name="show_ime" msgid="2506087537466597099">"भौतिक कीबोर्ड सक्रिय असताना त्यास स्क्रीनवर ठेवा"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"वॉलपेपर"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"वॉलपेपर बदला"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"सूचना ऐकणारा"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR श्रोता"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"अट प्रदाता"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"सूचना सहाय्यक"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"सूचना रॅंकर सेवा"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN सक्रिय"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> द्वारे VPN सक्रिय केले आहे"</string>
<string name="vpn_text" msgid="3011306607126450322">"नेटवर्क व्यवस्थापित करण्यासाठी स्पर्श करा."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"हे डिव्हाइस सामान्यपणे वापरण्यासाठी फॅक्टरी रीसेट करा"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"अधिक जाणून घेण्यासाठी स्पर्श करा."</string>
</resources>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index cd31b2b..5c342f0 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Bantuan Suara"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Kunci sekarang"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Kandungan tersembunyi"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Kandungan disembunyikan oleh dasar"</string>
<string name="safeMode" msgid="2788228061547930246">"Mod selamat"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Sentuh untuk mendapatkan lagi pilihan."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Penyahpepijatan USB disambungkan"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Sentuh untuk melumpuhkan penyahpepijatan USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Mengambil laporan pepijat…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Kongsi laporan pepijat?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Berkongsi laporan pepijat…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Pentadbir IT anda meminta laporan pepijat untuk membantu menyelesaikan masalah peranti ini. Apl dan data mungkin dikongsi dan peranti anda mungkin menjadi perlahan untuk sementara waktu."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Pentadbir IT anda meminta laporan pepijat untuk membantu menyelesaikan masalah peranti ini. Apl dan data mungkin dikongsi."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Proses ini mungkin memperlahankan peranti anda untuk sementara waktu"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"TERIMA"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"TOLAK"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Pentadbir IT anda meminta laporan pepijat untuk membantu menyelesaikan masalah peranti ini. Apl dan data mungkin dikongsi."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"KONGSI"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"TOLAK"</string>
<string name="select_input_method" msgid="8547250819326693584">"Tukar papan kekunci"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Pilih papan kekunci"</string>
<string name="show_ime" msgid="2506087537466597099">"Pastikannya pada skrin, semasa papan kekunci fizikal aktif"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Kertas dinding"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Tukar kertas dinding"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Pendengar pemberitahuan"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Pendengar VR"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Pembekal keadaan"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Pembantu pemberitahuan"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Perkhidmatan penentu kedudukan pemberitahuan"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN diaktifkan"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengurus rangkaian."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Lakukan tetapan semula kilang untuk menggunakan peranti ini seperti biasa"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Sentuh untuk mengetahui lebih lanjut."</string>
</resources>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 5cf2bd3..e66cf66 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"အသံ အကူအညီ"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ယခု သော့ပိတ်ရန်"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"၉၉၉+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"အကြောင်းအရာများ ဝှက်ထား"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"မူဝါဒမှ အကြောင်းအရာများကို ဝှက်ထားသည်"</string>
<string name="safeMode" msgid="2788228061547930246">"အန္တရာယ်ကင်းမှု စနစ်(Safe mode)"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"ထပ်မံရွေးချယ်စရာများအတွက် ထိပါ"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB အမှားစစ်ခြင်းအား ချိတ်ဆက်ထားသည်"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB ဒီဘာဂင် ပိတ်ရန် ထိပါ။"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ချွတ်ယွင်းချက် အစီရင်ခံစာပြုစုနေသည်..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ချွတ်ယွင်းချက် အစီရင်ခံစာကို မျှဝေမလား။"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"ချွတ်ယွင်းမှုအစီရင်ခံစာ မျှဝေနေသည်…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"ဤစက်ပစ္စည်းကို ပြဿနာအဖြေရှာရာတွင် ကူညီရန် သင့်အိုင်တီစီမံခန့်ခွဲသူသည် ချွတ်ယွင်းချက်အစီရင်ခံစာကို တောင်းဆိုထားသည်။ အက်ပ်များနှင့် ဒေတာကိုမျှဝေထားနိုင်ပြီး သင့်စက်ပစ္စည်းကို ခေတ္တနှေးကွေးသွားစေနိုင်သည်။"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"ဤစက်ပစ္စည်းကို ပြဿနာအဖြေရှာရာတွင် ကူညီရန် သင့်အိုင်တီစီမံခန့်ခွဲသူသည် ချွတ်ယွင်းချက်အစီရင်ခံစာကို တောင်းဆိုထားသည်။ အက်ပ်များနှင့် ဒေတာကိုမျှဝေထားနိုင်ပါသည်။"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"၎င်းသည်သင့်စက်ပစ္စည်းကို ခေတ္တနှေးကွေးသွားစေနိုင်သည်"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"လက်ခံပါ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ငြင်းပယ်ပါ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"ဤစက်ပစ္စည်းကို ပြဿနာအဖြေရှာရာတွင် ကူညီရန် သင့်အိုင်တီစီမံခန့်ခွဲသူသည် ချွတ်ယွင်းချက်အစီရင်ခံစာကို တောင်းဆိုထားသည်။ အက်ပ်များနှင့် ဒေတာကိုမျှဝေထားနိုင်ပါသည်။"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"မျှဝေပါ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ငြင်းပယ်ပါ"</string>
<string name="select_input_method" msgid="8547250819326693584">"ကီးဘုတ် ပြောင်းလဲရန်"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"ကီးဘုတ်များကို ရွေးရန်"</string>
<string name="show_ime" msgid="2506087537466597099">"စက်၏ကီးဘုတ်ကိုအသုံးပြုနေစဉ် ၎င်းကိုမျက်နှာပြင်ပေါ်တွင် ထားပါ"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"နောက်ခံ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"နောက်ခံပြောင်းခြင်း"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"အကြောင်းကြားချက် နားတောင်သူ"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR နားထောင်မှုစနစ်"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"အခြေအနေ စီမံပေးသူ"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"သတိပေးချက် အကူ"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"သတိပေးချက် အဆင့်သတ်မှတ်ခြင်းဝန်ဆောင်မှု"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ဖွင့်ထားပါသည်"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g>မှVPNအလုပ်လုပ်နေသည်"</string>
<string name="vpn_text" msgid="3011306607126450322">"ကွန်ရက် ထိန်းသိမ်းရန် တို့ထိပါ"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"ဤစက်ပစ္စည်းကို သာမန်အသုံးပြုနိုင်ရန် စက်ရုံထုတ်အတိုင်း ပြန်လည်သတ်မှတ်ပါ"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"ပိုမိုလေ့လာရန် တို့ပါ။"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 7ed5518..6c2358b 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Talehjelp"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lås nå"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Innholdet er skjult"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Innholdet er skjult i henhold til retningslinjene"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikkermodus"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Trykk for å se flere alternativer."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-feilsøking tilkoblet"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Trykk for å slå av USB-feilsøking."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Kjører feilrapport …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vil du dele feilrapporten?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Deler feilrapporten …"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"IT-administratoren har bedt om en feilrapport for å hjelpe med feilsøkingen på denne enheten. Appene og dataene kan bli delt. Dette kan midlertidig gjøre enheten din tregere."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"IT-administratoren har bedt om en feilrapport for å hjelpe med feilsøkingen på denne enheten. Appene og dataene kan bli delt."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Dette kan midlertidig gjøre enheten din tregere"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"GODTA"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"AVSLÅ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"IT-administratoren har bedt om en feilrapport for å hjelpe med feilsøkingen på denne enheten. Apper og data kan bli delt."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"DEL"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"AVSLÅ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Endre tastatur"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Velg tastatur"</string>
<string name="show_ime" msgid="2506087537466597099">"Ha den på skjermen mens det fysiske tastaturet er aktivt"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Bakgrunnsbilde"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Velg bakgrunnsbilde"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Varsellytteren"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Lyttetjeneste for virtuell virkelighet"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Betingelsesleverandør"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Varselassistent"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Tjeneste for rangering av varsler"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN er aktivert"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN er aktivert av <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Trykk for å administrere nettverket."</string>
@@ -1567,4 +1566,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Tilbakestill til fabrikkstandard for å bruke denne enheten som normalt"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Trykk for å finne ut mer."</string>
</resources>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 6291551..40d13f0 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"आवाज सहायता"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"अब बन्द गर्नुहोस्"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"९९९+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"लुकेका सामाग्रीहरू"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"नीतिद्वारा लुकाइएका सामग्री"</string>
<string name="safeMode" msgid="2788228061547930246">"सुरक्षित मोड"</string>
@@ -1058,13 +1057,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"थप विकल्पहरूका लागि छुनुहोस्।"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB डिबग गर्ने जडित छ"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB डिबग गर्ने असक्षम पार्न छुनुहोस्।"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"बग रिपोर्ट लिँदै..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"बग रिपोर्टलाई साझेदारी गर्ने हो?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"बग रिपोर्टलाई साझेदारी गर्दै ..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"तपाईंको IT प्रशासकले यो यन्त्रको समस्या निवारण गर्नमा मद्दत गर्न बग रिपोर्टहरूका लागि अनुरोध गर्नु भएको छ । अनुप्रयोगहरू र डेटा साझेदारी गर्न सकिन्छ र तपाईंको यन्त्र अस्थायी रूपमा सुस्त हुन सक्छ।"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"तपाईंको IT प्रशासकले यो यन्त्रको समस्या निवारण गर्नमा मदत गर्न बग रिपोर्टहरूका लागि अनुरोध गर्नु भएको छ । Apps र डेटा साझेदारी गर्न सक्नुहुन्छ ।"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"यसले अस्थायी रूपमा तपाईंको यन्त्रलाई सुस्त बनाउन सक्छ"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"स्वीकार गर्नुहोस्"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"अस्वीकार गर्नुहोस्"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"तपाईँको IT प्रशासकले यस यन्त्रको समस्या निवारण गर्नमा मद्दत गर्न बग रिपोर्टका लागि अनुरोध गर्नुभएको छ। अनुप्रयोग र डेटा साझेदारी हुन सक्छ।"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"साझेदारी गर्नुहोस्"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"अस्वीकार गर्नुहोस्"</string>
<string name="select_input_method" msgid="8547250819326693584">"कुञ्जीपाटी परिवर्तन गर्नुहोस्"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"कीबोर्ड छान्नुहोस्"</string>
<string name="show_ime" msgid="2506087537466597099">"भौतिक किबोर्ड सक्रिय हुँदा यसलाई स्क्रिनमा राख्नुहोस्"</string>
@@ -1143,8 +1141,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"वालपेपर"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"वालपेपर परिवर्तन गर्नुहोस्"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"सूचना सुन्नेवाला"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR श्रोता"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"सर्त प्रदायक"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"सूचना सहायक"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"सूचनालाई श्रेणी प्रदान गर्ने सेवा"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN सक्रिय भयो"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN <xliff:g id="APP">%s</xliff:g>द्वारा सक्रिय गरिएको हो"</string>
<string name="vpn_text" msgid="3011306607126450322">"नेटवर्क प्रबन्ध गर्न छुनुहोस्।"</string>
@@ -1571,4 +1570,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"यस यन्त्रलाई सामान्य रूपमा प्रयोग गर्नका लागि फ्याक्ट्री रिसेट गर्नुहोस्"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"थप जान्नका लागि स्पर्श गर्नुहोस्।"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 404fdb4..b48fc33 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Spraakassistent"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Nu vergrendelen"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999 +"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Inhoud verborgen"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Content verborgen op basis van beleid"</string>
<string name="safeMode" msgid="2788228061547930246">"Veilige modus"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Tik voor meer opties."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-foutopsporing verbonden"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Tik om USB-foutopsporing uit te schakelen."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Bugrapport genereren…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Bugrapport delen?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Bugrapport delen…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Je IT-beheerder heeft een bugrapport aangevraagd om problemen met dit apparaat op te lossen. Apps en gegevens kunnen worden gedeeld en je apparaat kan hierdoor tijdelijk worden vertraagd."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Je IT-beheerder heeft een bugrapport aangevraagd om problemen met dit apparaat op te lossen. Apps en gegevens kunnen worden gedeeld."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Dit kan je apparaat tijdelijk vertragen"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCEPTEREN"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"WEIGEREN"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Je IT-beheerder heeft een bugrapport aangevraagd om problemen met dit apparaat op te lossen. Apps en gegevens kunnen worden gedeeld."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"DELEN"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"WEIGEREN"</string>
<string name="select_input_method" msgid="8547250819326693584">"Toetsenbord wijzigen"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Toetsenborden kiezen"</string>
<string name="show_ime" msgid="2506087537466597099">"Dit op het scherm weergeven terwijl het fysieke toetsenbord actief is"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Achtergrond"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Achtergrond wijzigen"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Listener voor meldingen"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR-listener"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Provider van voorwaarden"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Meldingsassistent"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Rangschikkingsservice voor meldingen"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN is geactiveerd"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN wordt geactiveerd door <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak aan om het netwerk te beheren."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Zet dit apparaat terug op de fabrieksinstellingen om het normaal te gebruiken"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Tik voor meer informatie."</string>
</resources>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index fa181da..7ff2071 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"ਵੌਇਸ ਅਸਿਸਟ"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ਹੁਣ ਲੌਕ ਕਰੋ"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"ਸਮੱਗਰੀਆਂ ਲੁਕਾਈਆਂ ਗਈਆਂ"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ਨੀਤੀ ਦੁਆਰਾ ਸਮੱਗਰੀ ਲੁਕਾਈ ਗਈ"</string>
<string name="safeMode" msgid="2788228061547930246">"ਸੁਰੱਖਿਅਤ ਮੋਡ"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"ਹੋਰ ਵਿਕਲਪਾਂ ਲਈ ਸਪਰਸ਼ ਕਰੋ।"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ਡੀਬਗਿੰਗ ਕਨੈਕਟ ਕੀਤੀ"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB ਡੀਬਗਿੰਗ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਉਣ ਲਈ ਛੋਹਵੋ।"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ਬੱਗ ਰਿਪਰੋਟ ਪ੍ਰਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ਕੀ ਬੱਗ ਰਿਪੋਰਟ ਸਾਂਝੀ ਕਰਨੀ ਹੈ?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"ਬੱਗ ਰਿਪੋਰਟ ਸਾਂਝੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"ਤੁਹਾਡੇ IT ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਇਸ ਡੀਵਾਈਸ ਦੀ ਸਮੱਸਿਆ ਨੂੰ ਠੀਕ ਕਰਨ ਵਿੱਚ ਮਦਦ ਲਈ ਬੱਗ ਰਿਪੋਰਟ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ। ਐਪਾਂ ਅਤੇ ਡੈਟੇ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ ਅਤੇ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਆਰਜ਼ੀ ਤੌਰ \'ਤੇ ਹੌਲੀ ਹੋ ਸਕਦੀ ਹੈ।"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"ਤੁਹਾਡੇ IT ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਇਸ ਡੀਵਾਈਸ ਦੀ ਸਮੱਸਿਆ ਨੂੰ ਠੀਕ ਕਰਨ ਵਿੱੱਚ ਮਦਦ ਲਈ ਬੱਗ ਰਿਪੋਰਟ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ। ਐਪਾਂ ਅਤੇ ਡੈਟੇ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"ਇਹ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਨੂੰ ਆਰਜ਼ੀ ਤੌਰ \'ਤੇ ਹੌਲੀ ਕਰ ਸਕਦਾ ਹੈ"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"ਤੁਹਾਡੇ IT ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਇਸ ਡੀਵਾਈਸ ਦੀ ਸਮੱਸਿਆ ਨੂੰ ਠੀਕ ਕਰਨ ਵਿੱੱਚ ਮਦਦ ਲਈ ਬੱਗ ਰਿਪੋਰਟ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ। ਐਪਾਂ ਅਤੇ ਡੈਟੇ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ਸਾਂਝੀ ਕਰੋ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
<string name="select_input_method" msgid="8547250819326693584">"ਕੀਬੋਰਡ ਬਦਲੋ"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"ਕੀਬੋਰਡਸ ਚੁਣੋ"</string>
<string name="show_ime" msgid="2506087537466597099">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡ ਸਰਗਰਮ ਹੋਣ ਦੌਰਾਨ ਇਸ ਨੂੰ ਸਕ੍ਰੀਨ \'ਤੇ ਬਣਾਈ ਰੱਖੋ"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"ਵਾਲਪੇਪਰ"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"ਵਾਲਪੇਪਰ ਬਦਲੋ"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"ਸੂਚਨਾ ਸੁਣਨ ਵਾਲਾ"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR ਸਰੋਤਾ"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"ਸਥਿਤੀ ਪ੍ਰਦਾਤਾ"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"ਸੂਚਨਾ ਸਹਾਇਕ"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"ਸੂਚਨਾ ਰੈਂਕਰ ਸੇਵਾ"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ਸਕਿਰਿਆ ਕੀਤਾ"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN <xliff:g id="APP">%s</xliff:g> ਰਾਹੀਂ ਸਕਿਰਿਆ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
<string name="vpn_text" msgid="3011306607126450322">"ਨੈਟਵਰਕ ਵਿਵਸਥਿਤ ਕਰਨ ਲਈ ਛੋਹਵੋ।"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"ਇਸ ਡੀਵਾਈਸ ਨੂੰ ਸਧਾਰਨ ਰੂਪ ਵਿੱਚ ਵਰਤਣ ਲਈ ਫੈਕਟਰੀ ਰੀਸੈੱਟ ਕਰੋ"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"ਹੋਰ ਜਾਣਨ ਲਈ ਸਪਰਸ਼ ਕਰੋ।"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index d1441bd..b68b45a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -233,7 +233,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asystent głosowy"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Zablokuj teraz"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Treści ukryte"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Treść ukryta z powodu zasad"</string>
<string name="safeMode" msgid="2788228061547930246">"Tryb awaryjny"</string>
@@ -1068,13 +1067,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Kliknij, by zobaczyć więcej opcji."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Podłączono moduł debugowania USB"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Dotknij, aby wyłączyć debugowanie USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Zgłaszam błąd…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Udostępnić raport o błędzie?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Udostępniam raport o błędzie…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Administrator poprosił o raport o błędzie, by szybciej rozwiązać problemy na tym urządzeniu. Raport może zawierać informacje o aplikacjach i inne dane. Urządzenie może przez chwilę działać wolniej."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Administrator poprosił o raport o błędzie, który pomoże w rozwiązaniu problemów na tym urządzeniu. Mogą zostać udostępnione aplikacje i dane."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Urządzenie może przez chwilę działać wolniej"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"AKCEPTUJ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ODRZUĆ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Administrator poprosił o raport o błędzie, który pomoże w rozwiązaniu problemów na tym urządzeniu. Mogą zostać udostępnione aplikacje i dane."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"UDOSTĘPNIJ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODRZUĆ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Zmień klawiaturę"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Wybierz klawiatury"</string>
<string name="show_ime" msgid="2506087537466597099">"Pozostaw na ekranie, gdy aktywna jest klawiatura fizyczna"</string>
@@ -1153,8 +1151,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Zmień tapetę"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Odbiornik powiadomień"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Odbiornik rzeczywistości wirtualnej"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Dostawca warunków"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Asystent powiadomień"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Usługa rankingu powiadomień"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktywny"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Obsługa sieci VPN została włączona przez aplikację <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotknij, aby zarządzać siecią."</string>
@@ -1603,4 +1602,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Aby używać tego urządzenia normalnie, przywróć ustawienia fabryczne"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Kliknij, by dowiedzieć się więcej."</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index b444e0a..cd0de0e 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ajuda de voz"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear agora"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Conteúdo oculto"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Conteúdo ocultado pela política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
@@ -243,7 +242,7 @@
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"enviar e ver mensagens SMS"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"envie e veja mensagens SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do dispositivo"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Toque para ver mais opções."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toque para desativar a depuração do USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Gerando relatório do bug..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Compartilhar relatório do bug?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Compartilhando relatório do bug…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Seu administrador de TI solicitou um relatório de bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados, o que pode deixar seu dispositivo temporariamente mais lento."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Seu administrador de TI solicitou um relatório de bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Isso pode deixar seu dispositivo temporariamente mais lento"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACEITAR"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"RECUSAR"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Seu administrador de TI solicitou um relatório de bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTILHAR"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Alterar teclado"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Escolher teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Manter na tela enquanto o teclado físico estiver ativo"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Plano de fundo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Alterar plano de fundo"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Ouvinte de notificações"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Ouvinte de RV"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Provedor de condições"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Assistente de notificação"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Serviço de classificação de notificação"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ativada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A VPN está ativada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toque para gerenciar a rede."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Redefinir para a configuração original para usar este dispositivo normalmente"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toque para saber mais."</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 4a5b0054..dde0131 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Assist. de voz"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear agora"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Conteúdo oculto"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Conteúdo ocultado pela política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Toque para ver mais opções."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB ligada"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toque para desat. a depuração USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"A criar relatório de erro…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Pretende partilhar o relatório de erro?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"A partilhar relatório de erro…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"O seu administrador de TI solicitou um relatório de erro para ajudar na resolução de problemas deste dispositivo. As aplicações e os dados podem ser partilhados e o dispositivo pode tornar-se temporariamente mais lento."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"O seu administrador de TI solicitou um relatório de erro para ajudar na resolução de problemas deste dispositivo. As aplicações e os dados podem ser partilhados."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Esta ação pode tornar o dispositivo mais lento temporariamente"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACEITAR"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"RECUSAR"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"O seu administrador de TI solicitou um relatório de erro para ajudar na resolução de problemas deste dispositivo. As aplicações e os dados podem ser partilhados."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"PARTILHAR"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Alterar teclado"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Escolher teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Manter no ecrã enquanto o teclado físico estiver ativo"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Imagem de fundo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Alterar imagem de fundo"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Serviço de escuta de notificações"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Serviço de escuta de RV"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Fornecedor de condição"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Assistente de notificações"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Serviço de classificação de notificações"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ativada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A VPN foi ativada pelo <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toque para gerir a rede."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Fazer uma reposição de dados de fábrica para utilizar este dispositivo normalmente"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toque para saber mais."</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index b444e0a..cd0de0e 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ajuda de voz"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Bloquear agora"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Conteúdo oculto"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Conteúdo ocultado pela política"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string>
@@ -243,7 +242,7 @@
<string name="permgrouplab_calendar" msgid="5863508437783683902">"Agenda"</string>
<string name="permgroupdesc_calendar" msgid="3889615280211184106">"acesse sua agenda"</string>
<string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="4656988620100940350">"enviar e ver mensagens SMS"</string>
+ <string name="permgroupdesc_sms" msgid="4656988620100940350">"envie e veja mensagens SMS"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"acesse fotos, mídia e arquivos do dispositivo"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Microfone"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Toque para ver mais opções."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Toque para desativar a depuração do USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Gerando relatório do bug..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Compartilhar relatório do bug?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Compartilhando relatório do bug…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Seu administrador de TI solicitou um relatório de bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados, o que pode deixar seu dispositivo temporariamente mais lento."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Seu administrador de TI solicitou um relatório de bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Isso pode deixar seu dispositivo temporariamente mais lento"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACEITAR"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"RECUSAR"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Seu administrador de TI solicitou um relatório de bug para ajudar a resolver problemas deste dispositivo. É possível que apps e dados sejam compartilhados."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTILHAR"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string>
<string name="select_input_method" msgid="8547250819326693584">"Alterar teclado"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Escolher teclados"</string>
<string name="show_ime" msgid="2506087537466597099">"Manter na tela enquanto o teclado físico estiver ativo"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Plano de fundo"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Alterar plano de fundo"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Ouvinte de notificações"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Ouvinte de RV"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Provedor de condições"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Assistente de notificação"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Serviço de classificação de notificação"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ativada"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"A VPN está ativada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toque para gerenciar a rede."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Redefinir para a configuração original para usar este dispositivo normalmente"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Toque para saber mais."</string>
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index a89f1b3..1c6e024 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -231,7 +231,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Asistent vocal"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Blocați acum"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"˃999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Conținutul este ascuns"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Conținutul este ascuns conform politicii"</string>
<string name="safeMode" msgid="2788228061547930246">"Mod sigur"</string>
@@ -1060,13 +1059,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Atingeți pentru mai multe opțiuni."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Depanarea USB este conectată"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Atingeți pentru a dezactiva depanarea USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Se creează un raport de eroare…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Trimiteți raportul de eroare?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Se trimite raportul de eroare…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Administratorul IT a solicitat un raport de eroare pentru a remedia problemele acestui dispozitiv. Este posibil să se permită accesul la date și aplicații, iar funcționarea dispozitivului poate fi încetinită temporar."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Administratorul IT a solicitat un raport de eroare pentru a remedia problemele acestui dispozitiv. Este posibil să se permită accesul la date și aplicații."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Acest proces poate încetini temporar funcționarea dispozitivului"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ACCEPTAȚI"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"REFUZAȚI"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Administratorul IT a solicitat un raport de eroare pentru a remedia problemele acestui dispozitiv. Este posibil să se permită accesul la date și aplicații."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"TRIMITEȚI"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REFUZAȚI"</string>
<string name="select_input_method" msgid="8547250819326693584">"Schimbați tastatura"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Alegeți tastaturi"</string>
<string name="show_ime" msgid="2506087537466597099">"Se păstrează pe ecran cât timp este activată tastatura fizică"</string>
@@ -1145,8 +1143,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Imagine de fundal"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Modificați imaginea de fundal"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Serviciu de citire a notificărilor"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Instrument de ascultare pentru Realitatea virtuală"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Furnizor de condiții"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Asistent pentru notificări"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Serviciul de clasificare a notificărilor"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN activat"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN este activată de <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Atingeți pentru a gestiona rețeaua."</string>
@@ -1584,4 +1583,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Reveniți la setările din fabrică pentru a folosi acest dispozitiv ca de obicei"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Atingeți pentru a afla mai multe."</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 85cbdc4..f134357 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -233,7 +233,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Аудиоподсказки"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Заблокировать"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Содержимое скрыто"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Содержимое скрыто в соответствии с заданными правилами"</string>
<string name="safeMode" msgid="2788228061547930246">"Безопасный режим"</string>
@@ -1068,13 +1067,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Нажмите, чтобы настроить."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Отладка по USB разрешена"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Нажмите, чтобы отключить отладку по USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Подготовка отчета об ошибке"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Разрешить доступ к информации об ошибке?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Отправка отчета об ошибке"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Администратор запросил отчет об ошибке, чтобы устранить неполадку. Он может получить доступ к приложениям и данным. Возможно временное снижение скорости работы вашего устройства."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Администратор запросил отчет об ошибке, чтобы устранить неполадку. Он может получить доступ к приложениям и данным."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Скорость работы устройства может быть временно снижена."</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ПРИНЯТЬ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ОТКЛОНИТЬ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Администратор запросил отчет об ошибке, чтобы устранить неполадку. Он может получить доступ к приложениям и данным."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ПРЕДОСТАВИТЬ ДОСТУП"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ОТКЛОНИТЬ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Выбор раскладки"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Выбрать раскладку"</string>
<string name="show_ime" msgid="2506087537466597099">"Показывать на экране, когда физическая клавиатура включена"</string>
@@ -1153,8 +1151,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Фоновый рисунок"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Сменить обои"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Служба просмотра уведомлений"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR-режим"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Поставщик условий"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Ассистент уведомлений"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Сервис для оценки важности уведомлений"</string>
<string name="vpn_title" msgid="19615213552042827">"Сеть VPN активна"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Сеть VPN активирована приложением <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Нажмите, чтобы открыть настройки."</string>
@@ -1603,4 +1602,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Сброс до заводских настроек в целях безопасности"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Нажмите, чтобы узнать больше."</string>
</resources>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index f37eb58..9a6d3af 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"හඬ සහායක"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"දැන් අගුළු දමන්න"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"සැඟවුණු සම්බන්ධතා"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"ප්රතිපත්තිය විසින් අන්තර්ගතය සඟවන ලදී"</string>
<string name="safeMode" msgid="2788228061547930246">"ආරක්ෂිත ආකාරය"</string>
@@ -1054,13 +1053,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"වඩා වැඩි විකල්ප සඳහා ස්පර්ශ කරන්න."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB නිදොස්කරණය සම්බන්ධිතයි"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB නිදොස්කරණය අබල කිරීමට ස්පර්ශ කරන්න."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"දෝෂ වාර්තාවක් ගනිමින්…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"දෝෂ වාර්තාව බෙදා ගන්නද?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"දෝෂ වාර්තාවක් බෙදා ගනිමින්..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"මෙම උපාංගය දෝෂාවේක්ෂණය සඳහා උදවු කිරීමට ඔබේ IT පරිපාලක දෝෂ වාර්තාවක් ඉල්ලා ඇත. යෙදුම් සහ දත්ත බෙදා ගත හැකි අතර ඔබේ උපාංගය තාවකාලිකව මන්දගාමී විය හැකිය."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"මෙම උපාංගය දෝෂාවේක්ෂණය සඳහා උදවු කිරීමට ඔබේ IT පරිපාලක දෝෂ වාර්තාවක් ඉල්ලා ඇත. යෙදුම් සහ දත්ත බෙදා ගත හැකිය."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"මෙය ඔබේ උපාංගය තාවකාලිකව මන්දගාමී කළ හැකිය"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"පිළිගන්න"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ප්රතික්ෂේප කරන්න"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"මෙම උපාංගය දෝෂාවේක්ෂණය සඳහා උදවු කිරීමට ඔබේ IT පරිපාලක දෝෂ වාර්තාවක් ඉල්ලා ඇත. යෙදුම් සහ දත්ත බෙදා ගත හැකිය."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"බෙදා ගන්න"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ප්රතික්ෂේප කරන්න"</string>
<string name="select_input_method" msgid="8547250819326693584">"යතුරු පුවරු වෙනස් කිරීම"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"යතුරු පුවරු තෝරන්න"</string>
<string name="show_ime" msgid="2506087537466597099">"භෞතික යතුරු පුවරුව සක්රිය අතරතුර එය තිරය මත තබා ගන්න"</string>
@@ -1139,8 +1137,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"බිතුපත"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"බිතුපත වෙනස් කරන්න"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"දැනුම්දීම් අසන්නා"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR සවන් දෙන්නා"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"තත්ත්වය සපයන්නා"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"දැනුම්දීම් සහායක"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"දැනුම්දීම් ශ්රේණිගත කිරීමේ සේවාව"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN ක්රියාත්මකයි"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> මඟින් VPN සක්රීය කරන ලදි"</string>
<string name="vpn_text" msgid="3011306607126450322">"ජාලය කළමනාකරණය කිරීමට ස්පර්ශ කරන්න."</string>
@@ -1567,4 +1566,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"මෙම උපාංගය සාමාන්ය ලෙස භාවිත කිරීමට කර්මාන්තශාලා යළි සැකසීම"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"තව දැන ගැනීමට ස්පර්ශ කරන්න."</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index ddb162a..f4794a6 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -233,7 +233,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Hlasový asistent"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Uzamknúť"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Skrytý obsah"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Obsah je na základe pravidiel skrytý"</string>
<string name="safeMode" msgid="2788228061547930246">"Núdzový režim"</string>
@@ -1068,13 +1067,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Ďalšie možnosti zobrazíte klepnutím."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Ladenie cez USB pripojené"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Klepnutím zakážete ladenie cez USB"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Preberá sa hlásenie chyby…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Chcete zdieľať hlásenie chyby?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Zdieľa sa hlásenie chyby…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Správca IT si vyžiadal hlásenie chyby, aby mohol vyriešiť problém na tomto zariadení. Aplikácie a dáta môžu byť zdieľané. Môže to dočasne spomaliť vaše zariadenie."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Správca IT si vyžiadal hlásenie chyby, aby mohol vyriešiť problém na tomto zariadení. Aplikácie a dáta môžu byť zdieľané."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Môže to dočasne spomaliť vaše zariadenie."</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"PRIJAŤ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ODMIETNUŤ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Správca IT si vyžiadal hlásenie chyby, aby mohol vyriešiť problém na tomto zariadení. Aplikácie a dáta môžu byť zdieľané."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ZDIEĽAŤ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ODMIETNUŤ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Zmeniť klávesnicu"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Vybrať klávesnicu"</string>
<string name="show_ime" msgid="2506087537466597099">"Ponechať na obrazovke, keď je aktívna fyzická klávesnica"</string>
@@ -1153,8 +1151,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Tapeta"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Zmeniť tapetu"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Aplikácia na počúvanie upozornení"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Prijímač VR"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Poskytovateľ podmienky"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Asistent upozornení"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Služba na hodnotenie upozornení"</string>
<string name="vpn_title" msgid="19615213552042827">"Sieť VPN je aktivovaná"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikáciu <xliff:g id="APP">%s</xliff:g> aktivovala sieť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykom môžete spravovať sieť."</string>
@@ -1603,4 +1602,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Ak chcete toto zariadenie používať normálnym spôsobom, obnovte na ňom továrenské nastavenia"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Klepnutím získate ďalšie informácie."</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 47330fb..3e0cfaf 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -233,7 +233,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Glas. pomočnik"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Zakleni zdaj"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999 +"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Vsebina je skrita"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Pravilnik je skril vsebino"</string>
<string name="safeMode" msgid="2788228061547930246">"Varni način"</string>
@@ -1068,13 +1067,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Za več možnosti se dotaknite."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Iskanje in odpravljanje napak USB je povezano"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Dotaknite se, če želite onemogočiti iskanje in odpravljanje napak prek vrat USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Zajemanje poročila o napakah …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Želite poslati poročilo o napakah?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Pošiljanje poročila o napakah …"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Skrbnik za IT je zahteval poročilo o napakah za pomoč pri odpravljanju napak v tej napravi. Aplikacije in podatki bodo morda dani v skupno rabo in delovanje naprave bo morda začasno upočasnjeno."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Skrbnik za IT je zahteval poročilo o napakah za pomoč pri odpravljanju napak v tej napravi. Aplikacije in podatki bodo morda dani v skupno rabo."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"To bo morda začasno upočasnilo delovanje naprave"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"SPREJMEM"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"NE SPREJMEM"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Skrbnik za IT je zahteval poročilo o napakah za pomoč pri odpravljanju napak v tej napravi. Aplikacije in podatki bodo morda dani v skupno rabo."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"SKUPNA RABA"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"NE SPREJMEM"</string>
<string name="select_input_method" msgid="8547250819326693584">"Sprememba tipkovnice"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Izbira tipkovnic"</string>
<string name="show_ime" msgid="2506087537466597099">"Ohrani na zaslonu, dokler je aktivna fizična tipkovnica"</string>
@@ -1153,8 +1151,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Ozadje"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Spreminjanje ozadja"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Poslušalec obvestil"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Poslušalec za navidezno resničnost"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Ponudnik pogojev"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Pomočnik za obvestila"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Storitev za določanje stopenj pomembnosti obvestil"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN aktiviran"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN je aktivirala aplikacija <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotaknite se, če želite upravljati omrežje."</string>
@@ -1603,4 +1602,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Napravo ponastavite na tovarniške nastavitve, če jo želite uporabljati brez težav"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Dotaknite se, če želite izvedeti več."</string>
</resources>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index db46b89..bc8bd2b 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ndihma zanore"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Kyç tani"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Përmbajtjet janë të fshehura"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Përmbajtja është e fshehur për shkak të politikës"</string>
<string name="safeMode" msgid="2788228061547930246">"Modaliteti i sigurisë"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Prek për më shumë opsione."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Korrigjuesi i USB-së i lidhur"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Prek për të çaktivizuar korrigjimin e gabimeve të USB-së."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Po merret raporti i defekteve në kod…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Të ndahet raporti i defektit në kod?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Po ndan raportin e defekteve në kod..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Administratori i teknologjisë së informacionit kërkoi një raport të defekteve në kod për të ndihmuar me zgjidhjen e problemeve. Aplikacioni dhe të dhënat mund të ndahen dhe kjo mund të ngadalësojë përkohësisht pajisjen tënde."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Administratori i teknologjisë së informacionit kërkoi një raport të defekteve në kod për të ndihmuar me zgjidhjen e problemeve. Aplikacioni dhe të dhënat mund të ndahen."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Kjo mund të ngadalësojë përkohësisht pajisjen tënde"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"PRANO"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"REFUZO"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Administratori i teknologjisë së informacionit kërkoi një raport të defekteve në kod për të ndihmuar me zgjidhjen e problemeve. Aplikacioni dhe të dhënat mund të ndahen."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHPËRNDA"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REFUZO"</string>
<string name="select_input_method" msgid="8547250819326693584">"Ndërro tastierë"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Zgjidh tastierat"</string>
<string name="show_ime" msgid="2506087537466597099">"Mbaje në ekran ndërsa tastiera fizike është aktive"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Imazhi i sfondit"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ndrysho imazhin e sfondit"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Dëgjues njoftimesh"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Dëgjues VR"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Ofrues kushtesh"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Asistenti i njoftimeve"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Shërbimi i klasifikimit të njoftimeve"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN-ja u aktivizua"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-ja është aktivizuar nga <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Prek për të menaxhuar rrjetin."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Rivendos cilësimet e fabrikës për ta përdorur normalisht këtë pajisje"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Prek për të mësuar më shumë."</string>
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 1e3a886..d14c166 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -231,7 +231,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Гласовна помоћ"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Закључај одмах"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Садржај је сакривен"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Садржај је сакривен смерницама"</string>
<string name="safeMode" msgid="2788228061547930246">"Безбедни режим"</string>
@@ -1060,13 +1059,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Додирните за још опција."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Отклањање грешака са USB-а је успостављено"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Додирните да бисте онемогућили отклањање грешака са USB-а."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Извештај о грешци се генерише…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Желите ли да поделите извештај о грешци?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Дели се извештај о грешци…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"ИТ администратор је затражио извештај о грешци ради лакшег решавања проблема у вези са овим уређајем. Апликације и подаци могу да се деле, а уређај ће се привремено успорити."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"ИТ администратор је затражио извештај о грешци ради лакшег решавања проблема у вези са овим уређајем. Апликације и подаци могу да се деле."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Ово ће привремено успорити уређај"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ПРИХВАТИ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ОДБИЈ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"ИТ администратор је затражио извештај о грешци ради лакшег решавања проблема у вези са овим уређајем. Апликације и подаци могу да се деле."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ДЕЛИ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ОДБИЈ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Промените тастатуру"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Изаберите тастатуре"</string>
<string name="show_ime" msgid="2506087537466597099">"Задржи га на екрану док је физичка тастатура активна"</string>
@@ -1145,8 +1143,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Позадина"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Промена позадине"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Монитор обавештења"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Обрађивач за виртуелну реалност"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Добављач услова"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Помоћник за обавештења"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Услуга рангирања обавештења"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN је активиран"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Апликација <xliff:g id="APP">%s</xliff:g> је активирала VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Додирните да бисте управљали мрежом."</string>
@@ -1584,4 +1583,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Ресетујте уређај на фабричка подешавања да бисте га нормално користили"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Додирните да бисте сазнали више."</string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index ab61097..3eb4ae3 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lås nu"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Innehåll har dolts"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Innehåll har dolts p.g.a. en policy"</string>
<string name="safeMode" msgid="2788228061547930246">"Säkert läge"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Visa fler alternativ genom att trycka."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB-felsökning ansluten"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Tryck om du vill inaktivera USB-felsökning."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Felrapporten överförs …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vill du dela felrapporten?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Felrapporten delas …"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"IT-administratören har bett om en felrapport som hjälp vid felsökningen av den här enheten. Appar och data kan komma att delas. Detta kan tillfälligt göra enheten långsammare."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"IT-administratören har bett om en felrapport som hjälp vid felsökningen av den här enheten. Appar och data kan komma att delas."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Detta kan tillfälligt göra enheten långsammare"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"GODKÄNN"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"AVVISA"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"IT-administratören har bett om en felrapport som hjälp vid felsökningen av den här enheten. Appar och data kan komma att delas."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"DELA"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"AVVISA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Byt tangentbord"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Välj tangentbord"</string>
<string name="show_ime" msgid="2506087537466597099">"Ha kvar den på skärmen när det fysiska tangentbordet används"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Bakgrund"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Ändra bakgrund"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Meddelandelyssnare"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Lyssnare för virtuell verklighet"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Leverantör"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Aviseringsassistent"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Rankningstjänst för aviseringar"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN är aktiverat"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveras av <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tryck om du vill hantera nätverket."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Återställ standardinställningarna om du vill använda enheten normalt"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Tryck här om du vill läsa mer."</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 271c48d..04994d5 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -231,7 +231,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Usaidizi wa Sauti"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Funga sasa"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Maudhui yamefichwa"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Maudhui yamefichwa kulingana na sera"</string>
<string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
@@ -1054,13 +1053,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Gusa kwa chaguo zaidi."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Utatuaji wa USB umeunganishwa"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Gusa ili uzime utatuaji wa USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Inatayarisha ripoti ya hitilafu…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Ungependa kushiriki ripoti ya hitilafu?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Inashiriki ripoti ya hitilafu…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Msimamizi wako wa Teknolojia ya Habari ameomba ripoti ya hitilafu ili kusaidia katika utatuzi wa kifaa hiki. Huenda hatua hii ikasababisha programu na data kushirikiwa na kupunguza kasi ya kifaa chako kwa muda."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Msimamizi wako wa TEHAMA ameomba ripoti ya hitilafu ili kusaidia katika utatuzi wa hitilafu kwenye kifaa hiki. Programu na data zinaweza kushirikiwa."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Hatua hii inaweza kupunguza kasi ya kifaa chako kwa muda"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"KUBALI"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"KATAA"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Msimamizi wako wa TEHAMA ameomba ripoti ya hitilafu ili kusaidia katika utatuzi wa hitilafu kwenye kifaa hiki. Programu na data zinaweza kushirikiwa."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"SHIRIKI"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"KATAA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Badilisha kibodi"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Chagua kibodi"</string>
<string name="show_ime" msgid="2506087537466597099">"Iweke kwenye skrini wakati kibodi inapotumika"</string>
@@ -1139,8 +1137,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Mandhari"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Badilisha mandhari"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Kisikilizi cha arifa"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Kisikilizaji cha Uhalisia Pepe"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Mtoa masharti"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Mratibu wa arifa"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Huduma ya kupanga arifa"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN imewezeshwa"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN imeamilishwa na <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Gusa ili kudhibiti mtandao."</string>
@@ -1567,4 +1566,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Rejesha mipangilio ya kiwandani katika kifaa hiki ili ukitumie kwa njia ya kawaida"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Gusa ili kupata maelezo zaidi."</string>
</resources>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index f49c1b5..2cb08c9 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"குரல் உதவி"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"இப்போது பூட்டு"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"மறைந்துள்ள உள்ளடக்கம்"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"கொள்கையின்படி உள்ளடக்கம் மறைக்கப்பட்டது"</string>
<string name="safeMode" msgid="2788228061547930246">"பாதுகாப்பு பயன்முறை"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"கூடுதல் விருப்பங்களுக்காகத் தொடவும்."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB பிழைதிருத்தம் இணைக்கப்பட்டது"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB பிழைத்திருத்தத்தை முடக்க, தொடவும்."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"பிழை அறிக்கையை எடுக்கிறது…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"பிழை அறிக்கையைப் பகிரவா?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"பிழை அறிக்கையைப் பகிர்கிறது…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"இந்தச் சாதனத்தின் பிழைகாண்பதற்கு உதவ, உங்கள் ஐடி நிர்வாகி பிழை அறிக்கையைக் கோரியுள்ளார். பயன்பாடுகளும் தரவும் பகிரப்படலாம். தற்காலிகமாக சாதனத்தின் வேகம் குறையலாம்."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"இந்தச் சாதனத்தின் பிழைகாண்பதற்கு உதவ, உங்கள் ஐடி நிர்வாகி பிழை அறிக்கையைக் கோரியுள்ளார். பயன்பாடுகளும் தரவும் பகிரப்படலாம்."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"இது தற்காலிகமாக சாதனத்தின் வேகத்தைக் குறைக்கலாம்"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"சரி"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"வேண்டாம்"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"இந்தச் சாதனத்தின் பிழைகாண்பதற்கு உதவ, உங்கள் ஐடி நிர்வாகி பிழை அறிக்கையைக் கோரியுள்ளார். பயன்பாடுகளும் தரவும் பகிரப்படலாம்."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"பகிர்"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"வேண்டாம்"</string>
<string name="select_input_method" msgid="8547250819326693584">"விசைப்பலகையை மாற்று"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"விசைப்பலகைகளைத் தேர்வுசெய்க"</string>
<string name="show_ime" msgid="2506087537466597099">"கைமுறை விசைப்பலகை இயக்கத்தில் இருக்கும் போது IMEஐ திரையில் வைத்திரு"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"வால்பேப்பர்"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"வால்பேப்பரை மாற்று"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"அறிவிப்புகளைக் கண்காணிக்கும் சேவை"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR லிஷனர்"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"நிபந்தனை வழங்குநர்"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"அறிவிப்பு உதவி"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"அறிவிப்பை மதிப்பீடு செய்யும் சேவை"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN செயல்படுத்தப்பட்டது"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ஆல் VPN செயல்படுத்தப்பட்டது"</string>
<string name="vpn_text" msgid="3011306607126450322">"நெட்வொர்க்கை நிர்வகிக்கத் தொடவும்."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"சாதாரணமாக இந்தச் சாதனத்தைப் பயன்படுத்த, ஆரம்ப நிலைக்கு மீட்டமைக்கவும்"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"மேலும் அறிய தொடவும்."</string>
</resources>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 8047621..4fd8aad 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"వాయిస్ సహాయకం"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ఇప్పుడు లాక్ చేయండి"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"కంటెంట్లు దాచబడ్డాయి"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"విధానం ద్వారా కంటెంట్లు దాచబడ్డాయి"</string>
<string name="safeMode" msgid="2788228061547930246">"సురక్షిత మోడ్"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"మరిన్ని ఎంపికల కోసం తాకండి."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB డీబగ్గింగ్ కనెక్ట్ చేయబడింది"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB డీబగ్గింగ్ను నిలిపివేయడానికి తాకండి."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"బగ్ నివేదికను తీస్తోంది…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"బగ్ నివేదికను భాగస్వామ్యం చేయాలా?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"బగ్ నివేదికను భాగస్వామ్యం చేస్తోంది..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"మీ ఐటి నిర్వాహకులు ఈ పరికరం సమస్యకు పరిష్కారాన్ని కనుగొనడంలో సహాయం కోసం బగ్ నివేదికను అభ్యర్థించారు. అనువర్తనాలు మరియు డేటా భాగస్వామ్యం చేయబడవచ్చు మరియు మీ పరికరం పనితీరు తాత్కాలికంగా నెమ్మదించవచ్చు."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"మీ ఐటి నిర్వాహకులు ఈ పరికరం సమస్యకు పరిష్కారాన్ని కనుగొనడంలో సహాయం కోసం బగ్ నివేదికను అభ్యర్థించారు. అనువర్తనాలు మరియు డేటా భాగస్వామ్యం చేయబడవచ్చు."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"దీని వల్ల మీ పరికరం పనితీరు తాత్కాలికంగా నెమ్మదించవచ్చు"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ఆమోదిస్తున్నాను"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"తిరస్కరిస్తున్నాను"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"మీ ఐటి నిర్వాహకులు ఈ పరికరం సమస్యకు పరిష్కారాన్ని కనుగొనడంలో సహాయం కోసం బగ్ నివేదికను అభ్యర్థించారు. అనువర్తనాలు మరియు డేటా భాగస్వామ్యం చేయబడవచ్చు."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"భాగస్వామ్యం చేయి"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"తిరస్కరిస్తున్నాను"</string>
<string name="select_input_method" msgid="8547250819326693584">"కీబోర్డ్ను మార్చు"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"కీబోర్డ్లను ఎంచుకోండి"</string>
<string name="show_ime" msgid="2506087537466597099">"దీన్ని భౌతిక కీబోర్డ్ సక్రియంగా ఉన్నప్పుడు స్క్రీన్పై ఉంచుతుంది"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"వాల్పేపర్"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"వాల్పేపర్ను మార్చండి"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"నోటిఫికేషన్ పరిశీలన"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR పరిశీలన"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"షరతు ప్రదాత"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"నోటిఫికేషన్ సహాయకం"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"నోటిఫికేషన్ ర్యాంకర్ సేవ"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN సక్రియం చేయబడింది"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> ద్వారా VPN సక్రియం చేయబడింది"</string>
<string name="vpn_text" msgid="3011306607126450322">"నెట్వర్క్ను నిర్వహించడానికి తాకండి."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"ఈ పరికరాన్ని సాధారణంగా ఉపయోగించడానికి ఫ్యాక్టరీ రీసెట్ చేయండి"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"మరింత తెలుసుకోవడానికి తాకండి."</string>
</resources>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index ae19150..3408c21 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -25,7 +25,7 @@
<bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
<!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
- <string translatable="false" name="config_defaultPictureInPictureBounds">"1420 100 1820 325"</string>
+ <string translatable="false" name="config_defaultPictureInPictureBounds">"1328 54 1808 324"</string>
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
is located in center. -->
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 114b31b..fabecc3 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"ตัวช่วยเสียง"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ล็อกเลย"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"เนื้อหาถูกซ่อนไว้"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"มีการซ่อนเนื้อหาโดยนโยบาย"</string>
<string name="safeMode" msgid="2788228061547930246">"โหมดปลอดภัย"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"แตะเพื่อดูตัวเลือกเพิ่มเติม"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"เชื่อมต่อการแก้ไขข้อบกพร่อง USB แล้ว"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"แตะเพื่อปิดใช้งานการแก้ไขข้อบกพร่อง USB"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"กำลังสร้างรายงานข้อบกพร่อง…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"แชร์รายงานข้อบกพร่องไหม"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"กำลังแชร์รายงานข้อบกพร่อง…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"ผู้ดูแลระบบไอทีของคุณขอรายงานข้อบกพร่องเพื่อช่วยในการแก้ปัญหาอุปกรณ์นี้ อาจมีการแชร์แอปและข้อมูล ซึ่งอาจทำให้อุปกรณ์ทำงานช้าลงชั่วคราว"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"ผู้ดูแลระบบไอทีของคุณขอรายงานข้อบกพร่องเพื่อช่วยในการแก้ปัญหาอุปกรณ์นี้ อาจมีการแชร์แอปและข้อมูล"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"การดำเนินการนี้อาจทำให้อุปกรณ์ทำงานช้าลงชั่วคราว"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ยอมรับ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ปฏิเสธ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"ผู้ดูแลระบบไอทีของคุณขอรายงานข้อบกพร่องเพื่อช่วยในการแก้ปัญหาอุปกรณ์นี้ อาจมีการแชร์แอปและข้อมูล"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"แชร์"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ปฏิเสธ"</string>
<string name="select_input_method" msgid="8547250819326693584">"เปลี่ยนแป้นพิมพ์"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"เลือกแป้นพิมพ์"</string>
<string name="show_ime" msgid="2506087537466597099">"เปิดทิ้งไว้บนหน้าจอในระหว่างใช้งานแป้นพิมพ์จริง"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"วอลเปเปอร์"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"เปลี่ยนวอลเปเปอร์"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"ตัวฟังการแจ้งเตือน"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Listener ความเป็นจริงเสมือน"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"ผู้เสนอเงื่อนไข"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"ผู้ช่วยการแจ้งเตือน"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"บริการตัวจัดอันดับการแจ้งเตือน"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN เปิดใช้งานแล้ว"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"เปิดใช้งาน VPN โดย <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"แตะเพื่อจัดการเครือข่าย"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"รีเซ็ตเป็นค่าเริ่มต้นเพื่อใช้อุปกรณ์นี้ตามปกติ"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"แตะเพื่อเรียนรู้เพิ่มเติม"</string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 1e2a606..981567d 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"I-lock ngayon"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Nakatago ang mga content"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Itinago ang mga content alinsunod sa patakaran"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Pindutin para sa higit pang mga opsyon."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Konektado ang debugging ng USB"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Pindutin upang i-disable ang pagde-debug ng USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Kinukuha ang ulat ng bug…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Gusto mo bang ibahagi ang ulat ng bug?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Ibinabahagi ang ulat ng bug…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Humiling ang iyong IT admin ng isang ulat ng bug upang makatulong sa pag-troubleshoot sa device na ito. Maaaring ibahagi ang mga app at data at maaaring pansamantalang bumagal ang iyong device."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Humiling ang iyong IT admin ng isang ulat ng bug upang makatulong sa pag-troubleshoot sa device na ito. Maaaring ibahagi ang mga app at data."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Maaaring pansamantalang bumagal ang iyong device dahil dito"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"TANGGAPIN"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"TANGGIHAN"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Humiling ang iyong IT admin ng isang ulat ng bug upang makatulong sa pag-troubleshoot sa device na ito. Maaaring ibahagi ang mga app at data."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"IBAHAGI"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"TANGGIHAN"</string>
<string name="select_input_method" msgid="8547250819326693584">"Baguhin ang keyboard"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Pumili ng mga keyboard"</string>
<string name="show_ime" msgid="2506087537466597099">"Panatilihin ito sa screen habang aktibo ang pisikal na keyboard"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Wallpaper"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Baguhin ang wallpaper"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Notification listener"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR listener"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Nagbibigay ng kundisyon"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Notification assistant"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Serbisyo sa pag-rank ng notification"</string>
<string name="vpn_title" msgid="19615213552042827">"Naka-activate ang VPN"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Isinaaktibo ang VPN ng <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Pindutin upang pamahalaan ang network."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Mag-factory reset upang magamit nang normal ang device na ito"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Pindutin upang matuto nang higit pa."</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index ab0781b..af5652b 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Sesli Yardım"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Şimdi kilitle"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"İçerik gizlendi"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"İçerikler politika nedeniyle gizlendi"</string>
<string name="safeMode" msgid="2788228061547930246">"Güvenli mod"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Daha fazla seçenek için dokunun."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB hata ayıklaması bağlandı"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB hata ayıklama özelliğini devre dışı bırakmak için dokunun."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Hata raporu alınıyor…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Hata raporu paylaşılsın mı?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Hata raporu paylaşılıyor..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"BT yöneticiniz, bu cihazda sorun gidermeye yardımcı olması için bir hata raporu istedi. Uygulamalar ve veriler paylaşılabilir. Bu durum, cihazınızı geçici olarak yavaşlatabilir."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"BT yöneticiniz, bu cihazda sorun gidermeye yardımcı olması için bir hata raporu istedi. Uygulamalar ve veriler paylaşılabilir."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Bu durum, cihazınızı geçici olarak yavaşlatabilir"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"KABUL ET"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"REDDET"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"BT yöneticiniz, bu cihazda sorun gidermeye yardımcı olması için bir hata raporu istedi. Uygulamalar ve veriler paylaşılabilir."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"PAYLAŞ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"REDDET"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klavyeyi değiştir"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Klavyeyi seç"</string>
<string name="show_ime" msgid="2506087537466597099">"Fiziksel klavye etkin durumdayken ekranda tut"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Duvar Kağıdı"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Duvar kağıdını değiştir"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Bildirim dinleyici"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Sanal Gerçeklik dinleyici"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Durum sağlayıcı"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Bildirim yardımcısı"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Bildirim sıralama hizmeti"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN etkinleştirildi"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN, <xliff:g id="APP">%s</xliff:g> tarafından etkinleştirildi"</string>
<string name="vpn_text" msgid="3011306607126450322">"Ağı yönetmek için dokunun."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Cihazı normal olarak 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>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 439d68f..344797f 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -233,7 +233,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Голос. підказки"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Блокувати зараз"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Вміст сховано"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Вміст сховано згідно з правилом"</string>
<string name="safeMode" msgid="2788228061547930246">"Безп. режим"</string>
@@ -1068,13 +1067,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Торкніться, щоб побачити більше опцій."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Налагодження USB завершено"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Торкніться, щоб вимкнути налагодження USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Створюється повідомлення про помилку…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Надіслати звіт про помилку?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Надсилається звіт про помилку…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Ваш IT-адміністратор просить надіслати повідомлення про помилку, щоб вирішити проблему з пристроєм. Він може отримати доступ до ваших додатків і даних. Це також може тимчасово сповільнити роботу вашого пристрою."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Ваш IT-адміністратор просить надіслати повідомлення про помилку, щоб вирішити проблему з пристроєм. Він може отримати доступ до ваших додатків і даних."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Це може тимчасово сповільнити роботу вашого пристрою"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"ПРИЙНЯТИ"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"ВІДХИЛИТИ"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Ваш IT-адміністратор просить надіслати повідомлення про помилку, щоб вирішити проблему з пристроєм. Він може отримати доступ до ваших додатків і даних."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ПОДІЛИТИСЯ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ВІДХИЛИТИ"</string>
<string name="select_input_method" msgid="8547250819326693584">"Змінити клавіатуру"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Вибрати клавіатури"</string>
<string name="show_ime" msgid="2506087537466597099">"Утримуйте на екрані, коли активна фізична клавіатура"</string>
@@ -1153,8 +1151,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Фоновий мал."</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Змінити фоновий малюнок"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Служба читання сповіщень"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Обробник віртуальної реальності"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Постачальник умов"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Диспетчер сповіщень"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Служба позиціонування сповіщень"</string>
<string name="vpn_title" msgid="19615213552042827">"Мережу VPN активовано"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"Мережу VPN активовано програмою <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Торкніться, щоб керувати мережею."</string>
@@ -1603,4 +1602,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Відновлення заводських налаштувань для належної роботи пристрою"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Торкніться, щоб дізнатися більше."</string>
</resources>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 1bf224b..a58f5c2 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"ابھی مقفل کریں"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"مواد مخفی ہیں"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"مواد پالیسی کے تحت مخفی ہے"</string>
<string name="safeMode" msgid="2788228061547930246">"حفاظتی وضع"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"مزید اختیارات کیلئے ٹچ کریں۔"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB ڈیبگ کرنا مربوط ہو گیا"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"USB ڈیبگنگ کو غیر فعال کرنے کیلئے ٹچ کریں۔"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"بگ رپورٹ لی جا رہی ہے…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"بگ رپورٹ کا اشتراک کریں؟"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"بگ رپورٹ کا اشتراک ہو رہا ہے…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"آپ کے IT منتظم نے اس آلہ کا مسئلہ حل کرنے میں مدد کیلئے ایک بگ رپورٹ کی درخواست کی ہے۔ ایپس اور ڈیٹا کا اشتراک ہو سکتا ہے اور آپ کا آلہ عارضی طور پر سست ہو سکتا ہے۔"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"آپ کے IT منتظم نے اس آلہ کا مسئلہ حل کرنے میں مدد کیلئے ایک بگ رپورٹ کی درخواست کی ہے۔ ایپس اور ڈیٹا کا اشتراک ہو سکتا ہے۔"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"اس سے آپ کا آلہ عارضی طور پر سست ہو سکتا ہے"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"قبول کریں"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"مسترد کریں"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"آپ کے IT منتظم نے اس آلہ کا مسئلہ حل کرنے میں مدد کیلئے ایک بگ رپورٹ کی درخواست کی ہے۔ ایپس اور ڈیٹا کا اشتراک ہو سکتا ہے۔"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"اشتراک کریں"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"مسترد کریں"</string>
<string name="select_input_method" msgid="8547250819326693584">"کی بورڈ تبدیل کریں"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"کی بورڈز منتخب کریں"</string>
<string name="show_ime" msgid="2506087537466597099">"جب فزیکل کی بورڈ فعال ہو تو IME کو اسکرین پر رکھیں"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"وال پیپر"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"وال پیپر تبدیل کریں"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"اطلاع سننے والا"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR سامع"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"شرط فراہم کنندہ"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"اطلاع کا معاون"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"اطلاع کی درجہ بندی سروس"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN فعال ہوگیا"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> کے ذریعہ VPN فعال ہے"</string>
<string name="vpn_text" msgid="3011306607126450322">"نیٹ ورک کا نظم کرنے کیلئے چھوئیں۔"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"اس آلہ کو معمولاً استعمال کرنے کیلئے فیکٹری ری سیٹ کریں"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"مزید جاننے کیلئے ٹچ کریں۔"</string>
</resources>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index d862036..f95a37d 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Ovozli yordam"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Qulflash"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Kontent yashirildi"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Qoidaga muvofiq kontent yashirilgan"</string>
<string name="safeMode" msgid="2788228061547930246">"Xavfsiz usul"</string>
@@ -895,7 +894,7 @@
<string name="whichApplication" msgid="4533185947064773386">"Ilovani tanlang"</string>
<string name="whichApplicationNamed" msgid="8260158865936942783">"“%1$s” bilan ochish"</string>
<string name="whichViewApplication" msgid="3272778576700572102">"Ochish…"</string>
- <string name="whichViewApplicationNamed" msgid="2286418824011249620">"“%1$s” yordamida ochish"</string>
+ <string name="whichViewApplicationNamed" msgid="2286418824011249620">"%1$s bilan ochish"</string>
<string name="whichEditApplication" msgid="144727838241402655">"Tahrirlash…"</string>
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"“%1$s” yordamida tahrirlash"</string>
<string name="whichSendApplication" msgid="6902512414057341668">"Ulashish…"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Sozlash uchun bosing."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"USB orqali nosozliklarni tuzatish"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"O‘chirib qo‘yish uchun bosing."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Xatoliklar hisoboti olinmoqda…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Xatoliklar hisoboti yuborilsinmi?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Xatoliklar hisoboti yuborilmoqda…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Administratoringiz bu qurilma nosozliklarini tuzatish uchun xatoliklar hisobotini so‘ramoqda. Ilova va ma’lumotlardan foydalanilishi va bu vaqtincha qurilmangizni sekinlashtirishi ham mumkin."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Administratoringiz bu qurilma nosozliklarini tuzatish uchun xatoliklar hisobotini so‘ramoqda. Ilova va ma’lumotlardan foydalanilishi mumkin."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Qurilmaning ishlash tezligi vaqtincha pasayishi mumkin."</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"QABUL QILISH"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"RAD ETISH"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Administratoringiz bu qurilma nosozliklarini tuzatish uchun xatoliklar hisobotini so‘ramoqda. Ilova va ma’lumotlardan foydalanilishi mumkin."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"ULASHISH"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RAD ETISH"</string>
<string name="select_input_method" msgid="8547250819326693584">"Klaviaturani o‘zgartirish"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Klaviaturani tanlash"</string>
<string name="show_ime" msgid="2506087537466597099">"Tashqi klaviaturadan foydalanilayotganda buni ekranda saqlab turish"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Fon rasmi"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Fon rasmini o‘zgartirish"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Bildirishnoma tinglovchisi"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR rejimi"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Shartlarni taqdim etuvchi"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Bildirishnoma yordamchisi"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Bildirishnomalarni baholash xizmati"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN faollashtirildi"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN <xliff:g id="APP">%s</xliff:g> tomonidan faollashtirilgan"</string>
<string name="vpn_text" msgid="3011306607126450322">"Tarmoqni boshqarish uchun bosing."</string>
@@ -1505,7 +1504,7 @@
<item quantity="other">%d soat</item>
<item quantity="one">1 soat</item>
</plurals>
- <string name="zen_mode_until" msgid="7336308492289875088">"Ushbu vaqtgacha: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
+ <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha"</string>
<string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha (keyingi signal)"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Men o‘chirmaguncha"</string>
<string name="zen_mode_forever_dnd" msgid="3792132696572189081">"“Bezovta qilinmasin” rejimi o‘chirilmaguncha"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Bu qurilmadan odatdagidek foydalanish uchun zavod sozlamalarini tiklang"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Ko‘proq o‘rganish uchun bosing."</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index af69c31..283198f 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Trợ lý thoại"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Khóa ngay"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Nội dung bị ẩn"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Nội dung bị ẩn theo chính sách"</string>
<string name="safeMode" msgid="2788228061547930246">"Chế độ an toàn"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Chạm để có các tùy chọn khác."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Gỡ lỗi USB đã được kết nối"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Chạm để vô hiệu hóa gỡ lỗi USB."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Đang thu thập báo cáo lỗi…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Chia sẻ báo cáo lỗi?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Đang chia sẻ báo cáo lỗi…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Quản trị viên CNTT của bạn đã yêu cầu báo cáo lỗi để giúp khắc phục sự cố thiết bị này. Bạn có thể chia sẻ ứng dụng, đồng thời dữ liệu và thiết bị của bạn tạm thời có thể bị chậm."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Quản trị viên CNTT của bạn đã yêu cầu báo cáo lỗi để giúp khắc phục sự cố thiết bị này. Bạn có thể chia sẻ ứng dụng và dữ liệu."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Tác vụ này tạm thời có thể làm chậm thiết bị của bạn"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"CHẤP NHẬN"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"TỪ CHỐI"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Quản trị viên CNTT của bạn đã yêu cầu báo cáo lỗi để giúp khắc phục sự cố thiết bị này. Bạn có thể chia sẻ ứng dụng và dữ liệu."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"CHIA SẺ"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"TỪ CHỐI"</string>
<string name="select_input_method" msgid="8547250819326693584">"Thay đổi bàn phím"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Chọn bàn phím"</string>
<string name="show_ime" msgid="2506087537466597099">"Tiếp tục sử dụng ứng dụng trên màn hình trong khi bàn phím thực đang hoạt động"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Hình nền"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Thay đổi hình nền"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Trình xử lý thông báo"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Trình nghe VR"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Trình cung cấp điều kiện"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Trợ lý thông báo"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Dịch vụ xếp hạng thông báo"</string>
<string name="vpn_title" msgid="19615213552042827">"Đã kích hoạt VPN"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN được <xliff:g id="APP">%s</xliff:g> kích hoạt"</string>
<string name="vpn_text" msgid="3011306607126450322">"Chạm để quản lý mạng."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Thiết lập cài đặt gốc để sử dụng thiết bị này một cách bình thường"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Chạm để tìm hiểu thêm."</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 24a5b79..8dfbaed 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"语音助理"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"立即锁定"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g> 条)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"内容已隐藏"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"内容已隐藏(根据政策规定)"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"触摸以查看更多选项。"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"已连接到USB调试"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"触摸可停用USB调试。"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"正在生成错误报告…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"要分享错误报告吗?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"正在分享错误报告…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"您的 IT 管理员希望获取错误报告,以便排查此设备的问题。报告可能会透露您设备上的应用和数据,设备运行速度也可能会因此而暂时减慢。"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"您的 IT 管理员希望获取错误报告,以便排查此设备的问题。报告可能会透露您设备上的应用和数据。"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"这可能会暂时减慢您设备的运行速度"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"接受"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"拒绝"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"您的 IT 管理员希望获取错误报告,以便排查此设备的问题。报告可能会透露您设备上的应用和数据。"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"分享"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"拒绝"</string>
<string name="select_input_method" msgid="8547250819326693584">"更改键盘"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"选择键盘"</string>
<string name="show_ime" msgid="2506087537466597099">"连接到实体键盘时使其在屏幕上保持显示状态"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"壁纸"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"更改壁纸"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"通知侦听器"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR 监听器"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"条件提供程序"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"通知助手"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"通知重要性排序服务"</string>
<string name="vpn_title" msgid="19615213552042827">"已激活VPN"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g>已激活VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"触摸可管理网络。"</string>
@@ -1333,9 +1332,9 @@
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"删除"</string>
<string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"要将音量调高到推荐水平以上吗?\n\n长时间保持高音量可能会损伤听力。"</string>
- <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"持续按住双指即可启用无障碍功能。"</string>
- <string name="accessibility_enabled" msgid="1381972048564547685">"无障碍功能已启用。"</string>
- <string name="enable_accessibility_canceled" msgid="3833923257966635673">"已取消无障碍功能。"</string>
+ <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"持续按住双指即可启用辅助功能。"</string>
+ <string name="accessibility_enabled" msgid="1381972048564547685">"辅助功能已启用。"</string>
+ <string name="enable_accessibility_canceled" msgid="3833923257966635673">"已取消辅助功能。"</string>
<string name="user_switched" msgid="3768006783166984410">"当前用户是<xliff:g id="NAME">%1$s</xliff:g>。"</string>
<string name="user_switching_message" msgid="2871009331809089783">"正在切换为<xliff:g id="NAME">%1$s</xliff:g>…"</string>
<string name="user_logging_out_message" msgid="8939524935808875155">"正在将<xliff:g id="NAME">%1$s</xliff:g>退出帐号…"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"恢复出厂设置即可正常使用此设备"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"触摸即可了解详情。"</string>
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 4c46574..c52d843 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"語音助手"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"立即鎖定"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"內容已隱藏"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"已根據政策隱藏內容"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"輕觸以瀏覽更多選項。"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"已連接 USB 偵錯工具"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"輕觸即可停用 USB 偵錯。"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"正在取得錯誤報告…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"要分享錯誤報告嗎?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"正在分享錯誤報告…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"您的 IT 管理員要求您提供錯誤報告,以協助解決此裝置的問題。報告可能包含應用程式和相關資料,裝置的運作速度也可能暫時減慢。"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"您的 IT 管理員要求您提供錯誤報告,以協助解決此裝置的問題。報告可能會披露裝置中的應用程式和相關資料。"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"裝置的運作速度可能因而減慢"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"接受"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"拒絕"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"您的 IT 管理員要求您提供錯誤報告,以協助解決此裝置的問題。報告可能包含應用程式和相關資料。"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"分享"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"拒絕"</string>
<string name="select_input_method" msgid="8547250819326693584">"變更鍵盤"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"選擇鍵盤"</string>
<string name="show_ime" msgid="2506087537466597099">"在實體鍵盤處於連接狀態時保持顯示"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"桌布"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"變更桌布"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"通知接聽器"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"虛擬現實接聽器"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"條件供應商"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"通知小幫手"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"通知排序服務"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN 已啟用。"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> 已啟用 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"輕觸即可管理網絡。"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"將此裝置回復至原廠設定,方可正常使用"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"輕觸以瞭解詳情。"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 7db8794..8ff0fed 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"語音小幫手"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"立即鎖定"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"超過 999"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"內容已隱藏"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"內容已依據政策隱藏"</string>
<string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"輕觸即可顯示更多選項。"</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"已連接 USB 偵錯工具"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"輕觸即可停用 USB 偵錯。"</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"正在接收錯誤報告…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"要分享錯誤報告嗎?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"正在分享錯誤報告…"</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"您的 IT 管理員要求您提供錯誤報告,以便排解這個裝置發生的問題。報告可能會揭露裝置中的應用程式和相關資料,裝置運行速度也可能會因此暫時減慢。"</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"您的 IT 管理員要求您提供錯誤報告,以便排解這個裝置發生的問題。報告可能會揭露裝置中的應用程式和相關資料。"</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"這可能會暫時減慢裝置運行速度"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"接受"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"拒絕"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"您的 IT 管理員要求您提供錯誤報告,以便排解這個裝置發生的問題。報告可能會揭露裝置中的應用程式和相關資料。"</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"分享"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"拒絕"</string>
<string name="select_input_method" msgid="8547250819326693584">"變更鍵盤"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"選擇鍵盤"</string>
<string name="show_ime" msgid="2506087537466597099">"有連接的實體鍵盤時保持顯示"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"桌布"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"變更桌布"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"通知接聽器"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"VR 接聽器"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"條件提供者"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"通知小幫手"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"通知重要性排序服務"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN 已啟用"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> 已啟用 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"輕觸即可管理網路。"</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"恢復原廠設定即可正常使用這個裝置"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"輕觸即可瞭解詳情。"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 9432024..fe2c8a6 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -229,7 +229,6 @@
<string name="global_action_voice_assist" msgid="7751191495200504480">"Isisekeli sezwi"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Khiya manje"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
- <string name="notification_children_count_bracketed" msgid="1769425473168347839">"(<xliff:g id="NOTIFICATIONCOUNT">%d</xliff:g>)"</string>
<string name="notification_hidden_text" msgid="1135169301897151909">"Okuqukethwe kufihliwe"</string>
<string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"Okuqukethwe kufihlwe inqubomgomo"</string>
<string name="safeMode" msgid="2788228061547930246">"Imodi ephephile"</string>
@@ -1052,13 +1051,12 @@
<string name="usb_notification_message" msgid="7347368030849048437">"Thinta ukuze uthole ezinye izinketho."</string>
<string name="adb_active_notification_title" msgid="6729044778949189918">"Ukulungisa iphutha le-USB kuxhunyiwe"</string>
<string name="adb_active_notification_message" msgid="1016654627626476142">"Thinta ukwenza ukuthi ukudibhaga kwe-USB kungasebenzi."</string>
+ <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Ithatha umbiko wesiphazamisi..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Yabelana ngombiko wesiphazamisi?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="7572089031496651372">"Yabelana ngombiko wesiphazamisi..."</string>
- <string name="share_remote_bugreport_notification_message" msgid="752583906074230920">"Umlawuli wakho we-IT ucele umbiko wesiphazamisi ukusiza ukuxazulula inkinga yale divayisi. Izinhlelo zokusebenza zingabelwana futhi idivayisi yakho ingahle yehle kancane."</string>
- <string name="share_finished_remote_bugreport_notification_message" msgid="4627312060769912353">"Umqondisi wakho we-IT ucele umbiko wesiphazamisi ukukusiza ukuxazulula inkinga kule divayisi. Izinhlelo zokusebenza nedatha ingabiwa."</string>
- <string name="sharing_remote_bugreport_notification_message" msgid="673106383408474893">"Lokhu kungahambisa kancane idivayisi yakho okwesikhashana"</string>
- <string name="share_remote_bugreport_notification_accept" msgid="8203856129078669677">"YAMUKELA"</string>
- <string name="share_remote_bugreport_notification_decline" msgid="6337969352057443969">"YENQABA"</string>
+ <string name="share_remote_bugreport_notification_message_finished" msgid="8610614010660772643">"Umqondisi wakho we-IT ucele umbiko wesiphazamisi ukukusiza ukuxazulula inkinga kule divayisi. Izinhlelo zokusebenza nedatha ingabiwa."</string>
+ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"YABELANA"</string>
+ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"YENQABA"</string>
<string name="select_input_method" msgid="8547250819326693584">"Shintsha ikhibhodi"</string>
<string name="configure_input_methods" msgid="4769971288371946846">"Khetha amakhibhodi"</string>
<string name="show_ime" msgid="2506087537466597099">"Yigcine kusikrini ngenkathi kusebenza ikhibhodi ephathekayo"</string>
@@ -1137,8 +1135,9 @@
<string name="wallpaper_binding_label" msgid="1240087844304687662">"Iphephadonga"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"Shintsha iphephadonga"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"Umlaleli wesaziso"</string>
+ <string name="vr_listener_binding_label" msgid="4316591939343607306">"Isilaleli se-VR"</string>
<string name="condition_provider_service_binding_label" msgid="1321343352906524564">"Umhlinzeki wesimo"</string>
- <string name="notification_assistant_binding_label" msgid="909456055569102952">"Umsizi wesaziso"</string>
+ <string name="notification_ranker_binding_label" msgid="774540592299064747">"Isevisi yesilinganisi sesaziso"</string>
<string name="vpn_title" msgid="19615213552042827">"I-VPN isiyasebenza"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"i-VPN ivuswe ngu <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Thinta ukuze wengamele inethiwekhi."</string>
@@ -1565,4 +1564,6 @@
<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="audit_safemode_notification" msgid="6351827251856877200">"Setha kabusha ngokwefekthri ukuze usebenzise le divayisi ngokuvamile"</string>
+ <string name="audit_safemode_notification_details" msgid="1860601176690176413">"Thinta ukuze ufunde kabanzi."</string>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 90a573b..50c7bfb 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -593,6 +593,9 @@
the appearance matches the branding of the app requesting the fingerprint scan.-->
<attr name="fingerprintAuthDrawable" format="reference" />
+ <!-- Asset that should be used to show users the position of the NFC antenna on the device. -->
+ <attr name="nfcAntennaPositionDrawable" format="reference" />
+
<!-- ============ -->
<!-- Panel styles -->
<!-- ============ -->
@@ -8170,6 +8173,8 @@
Defined in same coordinates as the path itself -->
<attr name="endY" format="float" />
+ <!-- Defines the tile mode of the gradient. SweepGradient don't support tiling. -->
+ <attr name="tileMode"/>
</declare-styleable>
<!-- Describes an item of a GradientColor. Minimally need 2 items to define the gradient
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9a2a6eb..01b2c47 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -805,6 +805,13 @@
-->
<integer name="config_longPressOnPowerBehavior">1</integer>
+ <!-- Control the behavior when the user long presses the back button. Non-zero values are only
+ valid for watches as part of CDD/CTS.
+ 0 - Nothing
+ 1 - Go to voice assist
+ -->
+ <integer name="config_longPressOnBackBehavior">0</integer>
+
<!-- Control the behavior when the user short presses the power button.
0 - Nothing
1 - Go to sleep (doze)
@@ -1853,6 +1860,10 @@
<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>
+
<!-- When true use the linux /dev/input/event subsystem to detect the switch changes
on the headphone/microphone jack. When false use the older uevent framework. -->
<bool name="config_useDevInputEventForAudioJack">false</bool>
@@ -2440,6 +2451,14 @@
flag). -->
<bool name="config_forceWindowDrawsStatusBarBackground">true</bool>
+ <!-- Controls the opacity of the navigation bar depending on the visibility of the
+ various workspace stacks.
+ 0 - Nav bar is always opaque when either the freeform stack or docked stack is visible.
+ 1 - Nav bar is always translucent when the freeform stack is visible, otherwise always
+ opaque.
+ -->
+ <integer name="config_navBarOpacityMode">0</integer>
+
<!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
<string translatable="false" name="config_defaultPictureInPictureBounds">"0 0 100 100"</string>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 152473a..081d613 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -151,16 +151,13 @@
<dimen name="notification_content_picture_margin">56dp</dimen>
<!-- height of the content margin to accomodate for the header -->
- <dimen name="notification_content_margin_top">30dp</dimen>
+ <dimen name="notification_content_margin_top">37.5dp</dimen>
<!-- height of the content margin on the bottom -->
- <dimen name="notification_content_margin_bottom">13dp</dimen>
-
- <!-- height of notification header view if present -->
- <dimen name="notification_header_height">32dp</dimen>
+ <dimen name="notification_content_margin_bottom">16dp</dimen>
<!-- Height of a small notification in the status bar -->
- <dimen name="notification_min_height">84dp</dimen>
+ <dimen name="notification_min_height">92dp</dimen>
<!-- The width of the big icons in notifications. -->
<dimen name="notification_large_icon_width">64dp</dimen>
@@ -177,7 +174,7 @@
<dimen name="media_notification_expanded_image_max_size">94dp</dimen>
<!-- The maximum size of the image in the expanded media notification -->
- <dimen name="media_notification_expanded_image_margin_bottom">16dp</dimen>
+ <dimen name="media_notification_expanded_image_margin_bottom">20dp</dimen>
<!-- The margin of the content to an image-->
<dimen name="notification_content_image_margin_end">8dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 06e2248..2b0ef42 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2705,6 +2705,7 @@
<public type="attr" name="countDown" />
<public type="attr" name="canRecord" />
<public type="attr" name="tunerCount" />
+ <public type="attr" name="nfcAntennaPositionDrawable" />
<public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
<public type="style" name="Widget.Material.SeekBar.Discrete" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7b11302..e7640e2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -548,9 +548,6 @@
[CHAR LIMIT=4] -->
<string name="status_bar_notification_info_overflow">999+</string>
- <!-- The number of notifications in the notification header. An example would be (2) or (12) -->
- <string name="notification_children_count_bracketed">(<xliff:g id="notificationCount" example="1">%d</xliff:g>)</string>
-
<!-- The divider symbol between different parts of the notification header. not translatable [CHAR LIMIT=1] -->
<string name="notification_header_divider_symbol" translatable="false">•</string>
@@ -2917,20 +2914,18 @@
<!-- Message of notification shown when ADB is actively connected to the phone. -->
<string name="adb_active_notification_message">Touch to disable USB debugging.</string>
+ <!-- Title of notification shown to indicate that bug report is being collected. -->
+ <string name="taking_remote_bugreport_notification_title">Taking bug report\u2026</string>
<!-- Title of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. -->
<string name="share_remote_bugreport_notification_title">Share bug report?</string>
<!-- Title of notification shown to indicate that bug report is still being collected after sharing was accepted. -->
<string name="sharing_remote_bugreport_notification_title">Sharing bug report\u2026</string>
- <!-- Message of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. -->
- <string name="share_remote_bugreport_notification_message">Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared and your device may temporarily slow down.</string>
- <!-- Message of notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. -->
- <string name="share_finished_remote_bugreport_notification_message">Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared.</string>
- <!-- Message of notification shown to shown to indicate that bug report is still being collected after sharing was accepted. -->
- <string name="sharing_remote_bugreport_notification_message">This may temporarily slow down your device</string>
+ <!-- Message of a notification shown to ask for user consent for sharing a bugreport that was requested remotely by the IT administrator. -->
+ <string name="share_remote_bugreport_notification_message_finished">Your IT admin requested a bug report to help troubleshoot this device. Apps and data may be shared.</string>
<!-- Acceptance label of notification shown to ask for user consent for sharing the remote bugreport. -->
- <string name="share_remote_bugreport_notification_accept">ACCEPT</string>
+ <string name="share_remote_bugreport_action">SHARE</string>
<!-- Decline label of notification shown to ask for user consent for sharing the remote bugreport. -->
- <string name="share_remote_bugreport_notification_decline">DECLINE</string>
+ <string name="decline_remote_bugreport_action">DECLINE</string>
<!-- Used to replace %s in urls retreived from the signin server with locales. For Some -->
<!-- devices we don't support all the locales we ship to and need to replace the '%s' with a -->
@@ -3137,11 +3132,13 @@
<!-- Label to show for a service that is running because it is observing
the user's notifications. -->
<string name="notification_listener_binding_label">Notification listener</string>
+ <!-- Label to show for a service that is running because the system is in VR mode. -->
+ <string name="vr_listener_binding_label">VR listener</string>
<!-- Label to show for a service that is running because it is providing conditions. -->
<string name="condition_provider_service_binding_label">Condition provider</string>
<!-- Label to show for a service that is running because it is observing and modifying the
importance of the user's notifications. -->
- <string name="notification_assistant_binding_label">Notification assistant</string>
+ <string name="notification_ranker_binding_label">Notification ranker service</string>
<!-- Do Not Translate: Alternate eri.xml -->
<string name="alternate_eri_file">/data/eri.xml</string>
@@ -4231,4 +4228,9 @@
<!-- 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 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. -->
+ <string name="audit_safemode_notification_details">Touch to learn more.</string>
+
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index a9c8a06..a65a813 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -993,8 +993,7 @@
</style>
<style name="Widget.Material.SuggestionItem" parent="@android:style/TextAppearance.Material.Body1">
- <item name="alpha">.87</item>
- <item name="textColor">@color/black</item>
+ <item name="textColor">#8a000000</item> <!-- alpha=.54, textColor=@color/black -->
<item name="drawablePadding">8dip</item>
<item name="gravity">start|center_vertical</item>
<item name="layout_gravity">start|center_vertical</item>
@@ -1009,12 +1008,11 @@
</style>
<style name="TextAppearance.Material.TextSuggestionHighlight" parent="Widget.Material.SuggestionItem">
- <item name="textColor">#009688</item>
+ <item name="textColor">#de000000</item> <!-- alpha=.87, textColor=@color/black -->
</style>
<style name="Widget.Material.SuggestionButton" parent="@android:style/TextAppearance.Material.Button">
- <item name="alpha">.87</item>
- <item name="textColor">#009688</item>
+ <item name="textColor">#de009688</item> <!-- alpha=.87, textColor=#009688 -->
<item name="drawablePadding">8dip</item>
<item name="gravity">start|center_vertical</item>
<item name="layout_gravity">start|center_vertical</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 11f42ec..32a01318 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -306,6 +306,7 @@
<java-symbol type="bool" name="config_freeformWindowManagement" />
<java-symbol type="bool" name="config_supportsMultiWindow" />
<java-symbol type="bool" name="config_guestUserEphemeral" />
+ <java-symbol type="bool" name="config_localDisplaysMirrorContent" />
<java-symbol type="string" name="config_defaultPictureInPictureBounds" />
<java-symbol type="string" name="config_centeredPictureInPictureBounds" />
<java-symbol type="string" name="config_pictureInPictureBoundsInRecents" />
@@ -382,6 +383,7 @@
<java-symbol type="integer" name="config_extraFreeKbytesAbsolute" />
<java-symbol type="integer" name="config_immersive_mode_confirmation_panic" />
<java-symbol type="integer" name="config_longPressOnPowerBehavior" />
+ <java-symbol type="integer" name="config_longPressOnBackBehavior" />
<java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAdjust" />
<java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAbsolute" />
<java-symbol type="integer" name="config_max_pan_devices" />
@@ -1771,13 +1773,12 @@
<java-symbol type="string" name="accessibility_binding_label" />
<java-symbol type="string" name="adb_active_notification_message" />
<java-symbol type="string" name="adb_active_notification_title" />
+ <java-symbol type="string" name="taking_remote_bugreport_notification_title" />
<java-symbol type="string" name="share_remote_bugreport_notification_title" />
- <java-symbol type="string" name="share_remote_bugreport_notification_message" />
- <java-symbol type="string" name="share_finished_remote_bugreport_notification_message" />
<java-symbol type="string" name="sharing_remote_bugreport_notification_title" />
- <java-symbol type="string" name="sharing_remote_bugreport_notification_message" />
- <java-symbol type="string" name="share_remote_bugreport_notification_accept" />
- <java-symbol type="string" name="share_remote_bugreport_notification_decline" />
+ <java-symbol type="string" name="share_remote_bugreport_notification_message_finished" />
+ <java-symbol type="string" name="share_remote_bugreport_action" />
+ <java-symbol type="string" name="decline_remote_bugreport_action" />
<java-symbol type="string" name="aerr_application" />
<java-symbol type="string" name="aerr_process" />
<java-symbol type="string" name="aerr_application_repeated" />
@@ -1843,8 +1844,9 @@
<java-symbol type="string" name="low_internal_storage_view_text_no_boot" />
<java-symbol type="string" name="low_internal_storage_view_title" />
<java-symbol type="string" name="notification_listener_binding_label" />
+ <java-symbol type="string" name="vr_listener_binding_label" />
<java-symbol type="string" name="condition_provider_service_binding_label" />
- <java-symbol type="string" name="notification_assistant_binding_label" />
+ <java-symbol type="string" name="notification_ranker_binding_label" />
<java-symbol type="string" name="report" />
<java-symbol type="string" name="select_input_method" />
<java-symbol type="string" name="select_keyboard_layout_notification_title" />
@@ -1895,6 +1897,8 @@
<java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
<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="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
@@ -2392,6 +2396,7 @@
<java-symbol type="string" name="config_packagedKeyboardName" />
<java-symbol type="bool" name="config_forceWindowDrawsStatusBarBackground" />
+ <java-symbol type="integer" name="config_navBarOpacityMode" />
<java-symbol type="color" name="system_bar_background_semi_transparent" />
<!-- EditText suggestion popup. -->
@@ -2405,11 +2410,9 @@
<java-symbol type="id" name="notification_material_reply_text_2" />
<java-symbol type="id" name="notification_material_reply_text_3" />
- <java-symbol type="string" name="notification_children_count_bracketed" />
<java-symbol type="string" name="notification_hidden_text" />
<java-symbol type="string" name="notification_hidden_by_policy_text" />
<java-symbol type="id" name="app_name_text" />
- <java-symbol type="id" name="number_of_children" />
<java-symbol type="id" name="header_sub_text" />
<java-symbol type="id" name="expand_button" />
<java-symbol type="id" name="notification_header" />
@@ -2422,13 +2425,13 @@
<java-symbol type="drawable" name="ic_collapse_notification" />
<java-symbol type="drawable" name="ic_expand_bundle" />
<java-symbol type="drawable" name="ic_collapse_bundle" />
- <java-symbol type="dimen" name="notification_header_height" />
<java-symbol type="dimen" name="notification_min_content_height" />
<java-symbol type="dimen" name="notification_header_shrink_min_width" />
<java-symbol type="dimen" name="notification_content_margin_start" />
<java-symbol type="dimen" name="notification_content_margin_end" />
<java-symbol type="dimen" name="notification_content_picture_margin" />
<java-symbol type="dimen" name="notification_content_margin_top" />
+ <java-symbol type="dimen" name="notification_content_margin_bottom" />
<java-symbol type="string" name="importance_from_user" />
<java-symbol type="string" name="importance_from_person" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index cf7978c..c02a01a 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -102,7 +102,7 @@
<!-- United Kingdom (Great Britain): 4-6 digits, common codes [5-8]xxxx, plus EU:
http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf,
visual voicemail code for EE: 887 -->
- <shortcode country="gb" pattern="\\d{4,6}" premium="[5-8]\\d{4}" free="116\\d{3}|887|83669|34664|40406" />
+ <shortcode country="gb" pattern="\\d{4,6}" premium="[5-8]\\d{4}" free="116\\d{3}|2020|35890|61002|61202|887|83669|34664|40406" />
<!-- Georgia: 4 digits, known premium codes listed -->
<shortcode country="ge" pattern="\\d{4}" premium="801[234]|888[239]" />
@@ -214,7 +214,7 @@
<!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
visual voicemail code for T-Mobile: 122 -->
- <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:567|578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" free="122|87902" />
+ <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567" free="122|87902" />
<!-- Vietnam -->
<shortcode country="vn" free="5001" />
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index c92863d..b908d92 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -905,6 +905,10 @@
a1.start();
a2.reverse();
a3.start();
+ // Check that the animators' values are immediately set to end value in the case of
+ // 0-duration.
+ assertEquals(A1_END_VALUE, a1.getAnimatedValue());
+ assertEquals(A2_START_VALUE, a2.getAnimatedValue());
}
});
Thread.sleep(POLL_INTERVAL);
@@ -951,6 +955,10 @@
a1.start();
a2.start();
+
+ // In the case of 0 duration scale applied to a non-0 duration, check that the
+ // value is immediately set to the start value.
+ assertEquals(A2_START_VALUE, a2.getAnimatedValue());
}
});
Thread.sleep(POLL_INTERVAL);
@@ -962,6 +970,8 @@
assertTrue(l2.startCalled);
assertTrue(l1.endCalled);
assertTrue(l2.endCalled);
+ assertEquals(A1_END_VALUE, a1.getAnimatedValue());
+ assertEquals(A2_END_VALUE, a2.getAnimatedValue());
}
});
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index af2a944..bae4ecc 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -81,8 +81,7 @@
protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000; // 5 seconds
protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000; // 1 second
- protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
- protected static final int MAX_WAIT_FOR_LARGE_DOWNLOAD_TIME = 15 * 60 * 1000; // 15 minutes
+ protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 30 * 1000; // 30 seconds
protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
@@ -970,7 +969,7 @@
protected void verifyInt(Cursor cursor, String columnName, int expected) {
int index = cursor.getColumnIndex(columnName);
int actual = cursor.getInt(index);
- assertEquals(expected, actual);
+ assertEquals(String.format("Expected = %d : Actual = %d", expected, actual), expected, actual);
}
/**
diff --git a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index 7019980..4a53852 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -23,18 +23,16 @@
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.test.suitebuilder.annotation.LargeTest;
-
-import android.test.suitebuilder.annotation.Suppress;
import com.google.mockwebserver.MockResponse;
import java.io.File;
+import java.util.concurrent.TimeoutException;
import java.util.Iterator;
import java.util.Set;
/**
* Integration tests of the DownloadManager API.
*/
-@Suppress // Failing.
public class DownloadManagerFunctionalTest extends DownloadManagerBaseTest {
private static final String TAG = "DownloadManagerFunctionalTest";
private final static String CACHE_DIR =
@@ -79,7 +77,11 @@
request.setTitle(DEFAULT_FILENAME);
long dlRequest = mDownloadManager.enqueue(request);
- waitForDownloadOrTimeout(dlRequest);
+ try {
+ waitForDownloadOrTimeout(dlRequest);
+ } catch (TimeoutException ex) {
+ // it is expected to timeout as download never finishes
+ }
Cursor cursor = getCursor(dlRequest);
try {
@@ -114,7 +116,7 @@
verifyDownload(dlRequest, blobData);
mDownloadManager.remove(dlRequest);
}
-
+
/**
* Helper to verify a standard single-file download from the mock server, and clean up after
* verification
@@ -135,9 +137,7 @@
verifyFileSize(pfd, fileSize);
verifyFileContents(pfd, fileData);
- int colIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
- String fileName = cursor.getString(colIndex);
- assertTrue(fileName.startsWith(CACHE_DIR));
+ assertTrue(new File(CACHE_DIR + "/" + DEFAULT_FILENAME).exists());
} finally {
pfd.close();
cursor.close();
@@ -161,7 +161,6 @@
Uri localUri = Uri.fromFile(existentFile);
request.setDestinationUri(localUri);
-
long dlRequest = mDownloadManager.enqueue(request);
// wait for the download to complete
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
index e9fd5fb..47554a6 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
@@ -17,20 +17,18 @@
package android.content.res;
import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.TypedValue;
import com.android.frameworks.coretests.R;
import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
public class ConfigurationBoundResourceCacheTest
extends ActivityInstrumentationTestCase2<ResourceCacheActivity> {
ConfigurationBoundResourceCache<Float> mCache;
- Method mCalcConfigChanges;
-
public ConfigurationBoundResourceCacheTest() {
super(ResourceCacheActivity.class);
}
@@ -38,36 +36,45 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- mCache = new ConfigurationBoundResourceCache<Float>(getActivity().getResources());
+ mCache = new ConfigurationBoundResourceCache<>();
}
+ @SmallTest
public void testGetEmpty() {
- assertNull(mCache.get(-1, null));
+ final Resources res = getActivity().getResources();
+ assertNull(mCache.getInstance(-1, res, null));
}
+ @SmallTest
public void testSetGet() {
mCache.put(1, null, new DummyFloatConstantState(5f));
- assertEquals(5f, mCache.get(1, null));
- assertNotSame(5f, mCache.get(1, null));
- assertEquals(null, mCache.get(1, getActivity().getTheme()));
+ final Resources res = getActivity().getResources();
+ assertEquals(5f, mCache.getInstance(1, res, null));
+ assertNotSame(5f, mCache.getInstance(1, res, null));
+ assertEquals(null, mCache.getInstance(1, res, getActivity().getTheme()));
}
+ @SmallTest
public void testSetGetThemed() {
mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f));
- assertEquals(null, mCache.get(1, null));
- assertEquals(5f, mCache.get(1, getActivity().getTheme()));
- assertNotSame(5f, mCache.get(1, getActivity().getTheme()));
+ final Resources res = getActivity().getResources();
+ assertEquals(null, mCache.getInstance(1, res, null));
+ assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()));
+ assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()));
}
+ @SmallTest
public void testMultiThreadPutGet() {
mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f));
mCache.put(1, null, new DummyFloatConstantState(10f));
- assertEquals(10f, mCache.get(1, null));
- assertNotSame(10f, mCache.get(1, null));
- assertEquals(5f, mCache.get(1, getActivity().getTheme()));
- assertNotSame(5f, mCache.get(1, getActivity().getTheme()));
+ final Resources res = getActivity().getResources();
+ assertEquals(10f, mCache.getInstance(1, res, null));
+ assertNotSame(10f, mCache.getInstance(1, res, null));
+ assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()));
+ assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()));
}
+ @SmallTest
public void testVoidConfigChange()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
TypedValue staticValue = new TypedValue();
@@ -83,11 +90,12 @@
Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
- assertEquals(staticDim, mCache.get(key, getActivity().getTheme()));
+ assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()));
mCache.onConfigurationChange(changes);
- assertEquals(staticDim, mCache.get(key, getActivity().getTheme()));
+ assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()));
}
+ @SmallTest
public void testEffectiveConfigChange()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
TypedValue changingValue = new TypedValue();
@@ -105,11 +113,12 @@
Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
- assertEquals(changingDim, mCache.get(key, getActivity().getTheme()));
+ assertEquals(changingDim, mCache.getInstance(key, res, getActivity().getTheme()));
mCache.onConfigurationChange(changes);
assertNull(mCache.get(key, getActivity().getTheme()));
}
+ @SmallTest
public void testConfigChangeMultipleResources()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
TypedValue staticValue = new TypedValue();
@@ -130,17 +139,19 @@
Configuration.ORIENTATION_PORTRAIT
: Configuration.ORIENTATION_LANDSCAPE;
int changes = calcConfigChanges(res, newCnf);
- assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic,
+ assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res,
getActivity().getTheme()));
- assertEquals(changingDim, mCache.get(R.dimen.resource_cache_test_orientation_dependent,
- getActivity().getTheme()));
+ assertEquals(changingDim,
+ mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
+ getActivity().getTheme()));
mCache.onConfigurationChange(changes);
- assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic,
+ assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res,
getActivity().getTheme()));
- assertNull(mCache.get(R.dimen.resource_cache_test_orientation_dependent,
+ assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
getActivity().getTheme()));
}
+ @SmallTest
public void testConfigChangeMultipleThemes()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
TypedValue[] staticValues = new TypedValue[]{new TypedValue(), new TypedValue()};
@@ -172,31 +183,27 @@
int changes = calcConfigChanges(res, newCnf);
for (int i = 0; i < 2; i++) {
final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
- assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic, theme));
+ assertEquals(staticDim,
+ mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme));
assertEquals(changingDim,
- mCache.get(R.dimen.resource_cache_test_orientation_dependent, theme));
+ mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
+ theme));
}
mCache.onConfigurationChange(changes);
for (int i = 0; i < 2; i++) {
final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
- assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic, theme));
- assertNull(mCache.get(R.dimen.resource_cache_test_orientation_dependent, theme));
+ assertEquals(staticDim,
+ mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme));
+ assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
+ theme));
}
}
- private int calcConfigChanges(Resources resources, Configuration configuration)
- throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
- if (mCalcConfigChanges == null) {
- mCalcConfigChanges = Resources.class.getDeclaredMethod("calcConfigChanges",
- Configuration.class);
- mCalcConfigChanges.setAccessible(true);
- }
- return (Integer) mCalcConfigChanges.invoke(resources, configuration);
-
+ private static int calcConfigChanges(Resources resources, Configuration configuration) {
+ return resources.calcConfigChanges(configuration);
}
- static class DummyFloatConstantState extends
- ConstantState<Float> {
+ static class DummyFloatConstantState extends ConstantState<Float> {
final Float mObj;
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
new file mode 100644
index 0000000..3cadbf6
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.content.res;
+
+import android.annotation.NonNull;
+import android.app.ResourcesManager;
+import android.os.Binder;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.DisplayMetrics;
+import android.util.LocaleList;
+import android.util.TypedValue;
+import android.view.Display;
+import junit.framework.TestCase;
+
+public class ResourcesManagerTest extends TestCase {
+ private static final String APP_ONE_RES_DIR = "app_one.apk";
+ private static final String APP_ONE_RES_SPLIT_DIR = "app_one_split.apk";
+ private static final String APP_TWO_RES_DIR = "app_two.apk";
+ private static final String LIB_RES_DIR = "lib.apk";
+
+ private ResourcesManager mResourcesManager;
+ private DisplayMetrics mDisplayMetrics;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mDisplayMetrics = new DisplayMetrics();
+ mDisplayMetrics.setToDefaults();
+
+ // Override defaults (which take device specific properties).
+ mDisplayMetrics.density = 1.0f;
+ mDisplayMetrics.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
+ mDisplayMetrics.xdpi = DisplayMetrics.DENSITY_DEFAULT;
+ mDisplayMetrics.ydpi = DisplayMetrics.DENSITY_DEFAULT;
+ mDisplayMetrics.noncompatDensity = mDisplayMetrics.density;
+ mDisplayMetrics.noncompatDensityDpi = mDisplayMetrics.densityDpi;
+ mDisplayMetrics.noncompatXdpi = DisplayMetrics.DENSITY_DEFAULT;
+ mDisplayMetrics.noncompatYdpi = DisplayMetrics.DENSITY_DEFAULT;
+
+ mResourcesManager = new ResourcesManager() {
+ @Override
+ protected AssetManager createAssetManager(@NonNull ResourcesKey key) {
+ return new AssetManager();
+ }
+
+ @Override
+ protected DisplayMetrics getDisplayMetricsLocked(int displayId) {
+ return mDisplayMetrics;
+ }
+ };
+ }
+
+ @SmallTest
+ public void testMultipleCallsWithIdenticalParametersCacheReference() {
+ Resources resources = mResourcesManager.getResources(
+ null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources);
+
+ Resources newResources = mResourcesManager.getResources(
+ null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(newResources);
+ assertSame(resources, newResources);
+ }
+
+ @SmallTest
+ public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() {
+ Resources resources = mResourcesManager.getResources(
+ null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources);
+
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.smallestScreenWidthDp = 200;
+ Resources newResources = mResourcesManager.getResources(
+ null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, overrideConfig,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(newResources);
+ assertNotSame(resources, newResources);
+ }
+
+ @SmallTest
+ public void testAddingASplitCreatesANewImpl() {
+ Resources resources1 = mResourcesManager.getResources(
+ null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources1);
+
+ Resources resources2 = mResourcesManager.getResources(
+ null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null,
+ Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources2);
+
+ assertNotSame(resources1, resources2);
+ assertNotSame(resources1.getImpl(), resources2.getImpl());
+ }
+
+ @SmallTest
+ public void testUpdateConfigurationUpdatesAllAssetManagers() {
+ Resources resources1 = mResourcesManager.getResources(
+ null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources1);
+
+ Resources resources2 = mResourcesManager.getResources(
+ null, APP_TWO_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources2);
+
+ Binder activity = new Binder();
+ final Configuration overrideConfig = new Configuration();
+ overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ Resources resources3 = mResourcesManager.getResources(
+ activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY,
+ overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources3);
+
+ // No Resources object should be the same.
+ assertNotSame(resources1, resources2);
+ assertNotSame(resources1, resources3);
+ assertNotSame(resources2, resources3);
+
+ // Each ResourcesImpl should be different.
+ assertNotSame(resources1.getImpl(), resources2.getImpl());
+ assertNotSame(resources1.getImpl(), resources3.getImpl());
+ assertNotSame(resources2.getImpl(), resources3.getImpl());
+
+ Configuration newConfig = new Configuration();
+ newConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null);
+
+ final Configuration expectedConfig = new Configuration();
+ expectedConfig.setLocales(LocaleList.getAdjustedDefault());
+ expectedConfig.densityDpi = mDisplayMetrics.densityDpi;
+ expectedConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
+
+ assertEquals(expectedConfig, resources1.getConfiguration());
+ assertEquals(expectedConfig, resources2.getConfiguration());
+ assertEquals(expectedConfig, resources3.getConfiguration());
+ }
+
+ @SmallTest
+ public void testTwoActivitiesWithIdenticalParametersShareImpl() {
+ Binder activity1 = new Binder();
+ Resources resources1 = mResourcesManager.getResources(
+ activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources1);
+
+ Binder activity2 = new Binder();
+ Resources resources2 = mResourcesManager.getResources(
+ activity2, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources1);
+
+ // The references themselves should be unique.
+ assertNotSame(resources1, resources2);
+
+ // The implementations should be the same.
+ assertSame(resources1.getImpl(), resources2.getImpl());
+
+ final Configuration overrideConfig = new Configuration();
+ overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ Resources resources3 = mResourcesManager.getResources(
+ activity2, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY,
+ overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+
+ // Since we requested new resources for activity2, the resource should be the same
+ // as the one returned before for activity2.
+ assertSame(resources2, resources3);
+
+ // But the implementation has changed.
+ assertNotSame(resources1.getImpl(), resources2.getImpl());
+ }
+
+ @SmallTest
+ public void testThemesGetUpdatedWithNewImpl() {
+ Binder activity1 = new Binder();
+ Resources resources1 = mResourcesManager.getResources(
+ activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources1);
+
+ Resources.Theme theme = resources1.newTheme();
+ assertSame(resources1, theme.getResources());
+ theme.applyStyle(android.R.style.Theme_NoTitleBar, false);
+
+ TypedValue value = new TypedValue();
+ assertTrue(theme.resolveAttribute(android.R.attr.windowNoTitle, value, true));
+ assertEquals(TypedValue.TYPE_INT_BOOLEAN, value.type);
+ assertTrue(value.data != 0);
+
+ final Configuration overrideConfig = new Configuration();
+ overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
+ Resources resources2 = mResourcesManager.getResources(
+ activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY,
+ overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ assertNotNull(resources2);
+ assertSame(resources1, resources2);
+ assertSame(resources2, theme.getResources());
+
+ // Make sure we can still access the data.
+ assertTrue(theme.resolveAttribute(android.R.attr.windowNoTitle, value, true));
+ assertEquals(TypedValue.TYPE_INT_BOOLEAN, value.type);
+ assertTrue(value.data != 0);
+ }
+}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index a723977..9074f8a 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -17,8 +17,8 @@
package android.net;
import static android.net.NetworkStats.ROAMING_ALL;
-import static android.net.NetworkStats.ROAMING_DEFAULT;
-import static android.net.NetworkStats.ROAMING_ROAMING;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.ROAMING_YES;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.SET_DBG_VPN_IN;
@@ -46,57 +46,57 @@
public void testFindIndex() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 4)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L, 0L,
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L, 0L,
0L, 10)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 1024L,
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 1024L,
8L, 11)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
1024L, 8L, 12)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 1024L, 8L,
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_YES, 1024L, 8L,
1024L, 8L, 12);
- assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING));
- assertEquals(2, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT));
- assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT));
- assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT));
- assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT));
+ assertEquals(3, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_YES));
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO));
+ assertEquals(1, stats.findIndex(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO));
+ assertEquals(0, stats.findIndex(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO));
+ assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, ROAMING_NO));
}
public void testFindIndexHinted() {
final NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L, 0L,
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L, 0L,
0L, 10)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 1024L,
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 1024L,
8L, 11)
- .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
1024L, 8L, 12)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 1024L, 8L,
0L, 0L, 10)
- .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 0L, 0L, 1024L,
+ .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, ROAMING_NO, 0L, 0L, 1024L,
8L, 11)
- .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
1024L, 8L, 12)
- .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 1024L, 8L,
+ .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, ROAMING_YES, 1024L, 8L,
1024L, 8L, 12);
// verify that we correctly find across regardless of hinting
for (int hint = 0; hint < stats.size(); hint++) {
assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE,
- ROAMING_DEFAULT, hint));
+ ROAMING_NO, hint));
assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE,
- ROAMING_DEFAULT, hint));
+ ROAMING_NO, hint));
assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE,
- ROAMING_DEFAULT, hint));
+ ROAMING_NO, hint));
assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE,
- ROAMING_DEFAULT, hint));
+ ROAMING_NO, hint));
assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D,
- ROAMING_DEFAULT, hint));
+ ROAMING_NO, hint));
assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
- ROAMING_DEFAULT, hint));
+ ROAMING_NO, hint));
assertEquals(6, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE,
- ROAMING_ROAMING, hint));
+ ROAMING_YES, hint));
assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE,
- ROAMING_DEFAULT, hint));
+ ROAMING_NO, hint));
}
}
@@ -106,41 +106,41 @@
assertEquals(0, stats.size());
assertEquals(3, stats.internalSize());
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1L, 1L, 2L,
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1L, 1L, 2L,
2L, 3);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 2L, 2L, 2L,
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 2L, 2L, 2L,
2L, 4);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 3L, 3L, 2L,
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_YES, 3L, 3L, 2L,
2L, 5);
assertEquals(3, stats.size());
assertEquals(3, stats.internalSize());
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 4L, 40L, 4L,
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 4L, 40L, 4L,
40L, 7);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 5L, 50L, 4L,
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 5L, 50L, 4L,
40L, 8);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 6L, 60L, 5L,
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 6L, 60L, 5L,
50L, 10);
- stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 7L, 70L, 5L,
+ stats.addValues(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_YES, 7L, 70L, 5L,
50L, 11);
assertEquals(7, stats.size());
assertTrue(stats.internalSize() >= 7);
- assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1L, 1L,
+ assertValues(stats, 0, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1L, 1L,
2L, 2L, 3);
- assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 2L, 2L,
+ assertValues(stats, 1, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 2L, 2L,
2L, 2L, 4);
- assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 3L, 3L,
+ assertValues(stats, 2, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_YES, 3L, 3L,
2L, 2L, 5);
- assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 4L,
+ assertValues(stats, 3, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 4L,
40L, 4L, 40L, 7);
- assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 5L,
+ assertValues(stats, 4, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 5L,
50L, 4L, 40L, 8);
- assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 6L,
+ assertValues(stats, 5, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_NO, 6L,
60L, 5L, 50L, 10);
- assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 7L,
+ assertValues(stats, 6, TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, ROAMING_YES, 7L,
70L, 5L, 50L, 11);
}
@@ -152,19 +152,19 @@
stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L,
-128L, -1L, -1);
- assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 384L, 3L,
+ assertValues(stats, 0, TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, ROAMING_NO, 384L, 3L,
128L, 1L, 9);
- assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, ROAMING_DEFAULT, 128L, 1L, 128L,
+ assertValues(stats, 1, TEST_IFACE, 1001, SET_DEFAULT, 0xff, ROAMING_NO, 128L, 1L, 128L,
1L, 2);
// now try combining that should create row
stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L,
128L, 1L, 3);
- assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 1L,
+ assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 1L,
128L, 1L, 3);
stats.combineValues(TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, 128L, 1L,
128L, 1L, 3);
- assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 256L, 2L,
+ assertValues(stats, 2, TEST_IFACE, 5005, SET_DEFAULT, TAG_NONE, ROAMING_NO, 256L, 2L,
256L, 2L, 6);
}
@@ -180,9 +180,9 @@
final NetworkStats result = after.subtract(before);
// identical data should result in zero delta
- assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 0L,
+ assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 0L,
0L, 0);
- assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 0L,
+ assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 0L,
0L, 0);
}
@@ -198,9 +198,9 @@
final NetworkStats result = after.subtract(before);
// expect delta between measurements
- assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1L, 1L, 2L,
+ assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1L, 1L, 2L,
1L, 4);
- assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 3L, 1L, 4L,
+ assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 3L, 1L, 4L,
1L, 8);
}
@@ -217,11 +217,11 @@
final NetworkStats result = after.subtract(before);
// its okay to have new rows
- assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 0L,
+ assertValues(result, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 0L,
0L, 0);
- assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 0L, 0L, 0L,
+ assertValues(result, 1, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 0L, 0L, 0L,
0L, 0);
- assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
1024L, 8L, 20);
}
@@ -237,7 +237,7 @@
// should silently drop omitted rows
assertEquals(1, result.size());
- assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1L,
+ assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1L,
2L, 3L, 4L, 0);
assertEquals(4L, result.getTotalBytes());
}
@@ -264,11 +264,11 @@
assertEquals(64L, uidTag.getTotalBytes());
final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L, 0L,
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L, 0L, 0L,
0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L, 0L,
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L, 0L, 0L,
0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 32L, 0L, 0L, 0L,
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_YES, 32L, 0L, 0L, 0L,
0L);
assertEquals(96L, uidRoaming.getTotalBytes());
}
@@ -283,11 +283,11 @@
public void testGroupedByIfaceAll() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
- .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L, 2L,
+ .addValues(IFACE_ALL, 100, SET_ALL, TAG_NONE, ROAMING_NO, 128L, 8L, 0L, 2L,
20L)
- .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ .addValues(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
2L, 20L)
- .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, ROAMING_ROAMING, 128L, 8L, 0L, 2L,
+ .addValues(IFACE_ALL, 101, SET_ALL, TAG_NONE, ROAMING_YES, 128L, 8L, 0L, 2L,
20L);
final NetworkStats grouped = uidStats.groupedByIface();
@@ -300,19 +300,19 @@
public void testGroupedByIface() throws Exception {
final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 512L, 32L, 0L,
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 512L, 32L, 0L,
0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 4L, 0L, 0L,
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, ROAMING_NO, 64L, 4L, 0L, 0L,
0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 512L, 32L,
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 512L, 32L,
0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 128L, 8L, 0L, 0L,
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_NO, 128L, 8L, 0L, 0L,
0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 128L, 8L, 0L,
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_YES, 128L, 8L, 0L,
0L, 0L);
final NetworkStats grouped = uidStats.groupedByIface();
@@ -328,49 +328,49 @@
public void testAddAllValues() {
final NetworkStats first = new NetworkStats(TEST_START, 5)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L, 0L,
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L, 0L, 0L,
0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L,
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 32L, 0L, 0L,
0L, 0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_ROAMING, 32L, 0L, 0L,
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_YES, 32L, 0L, 0L,
0L, 0L);
final NetworkStats second = new NetworkStats(TEST_START, 2)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L, 0L, 0L,
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L, 0L, 0L,
0L)
- .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L, 0L,
+ .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L, 0L,
0L, 0L, 0L)
- .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_ROAMING, 32L, 0L, 0L,
+ .addValues(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_YES, 32L, 0L, 0L,
0L, 0L);
first.combineAllValues(second);
assertEquals(4, first.size());
- assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 64L, 0L, 0L,
+ assertValues(first, 0, TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 64L, 0L, 0L,
0L, 0L);
- assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 32L, 0L,
+ assertValues(first, 1, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 32L, 0L,
0L, 0L, 0L);
- assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_ROAMING, 64L, 0L,
+ assertValues(first, 2, TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, ROAMING_YES, 64L, 0L,
0L, 0L, 0L);
- assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 32L,
+ assertValues(first, 3, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_NO, 32L,
0L, 0L, 0L, 0L);
}
public void testGetTotal() {
final NetworkStats stats = new NetworkStats(TEST_START, 7)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
2L, 20L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 512L, 32L, 0L,
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 512L, 32L, 0L,
0L, 0L)
- .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 4L, 0L, 0L,
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, ROAMING_NO, 64L, 4L, 0L, 0L,
0L)
- .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 512L, 32L,
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 512L, 32L,
0L, 0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L, 0L,
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L, 0L,
0L, 0L)
- .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 128L, 8L, 0L, 0L,
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_NO, 128L, 8L, 0L, 0L,
0L)
- .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 128L, 8L, 0L,
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, ROAMING_YES, 128L, 8L, 0L,
0L, 0L);
assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
@@ -396,9 +396,9 @@
final NetworkStats after = before.withoutUids(new int[] { 100 });
assertEquals(6, before.size());
assertEquals(2, after.size());
- assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 8L,
+ assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 8L,
0L, 0L, 0L);
- assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 128L, 8L, 0L,
+ assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, ROAMING_NO, 128L, 8L, 0L,
0L, 0L);
}
@@ -457,53 +457,53 @@
assertEquals(21, delta.size());
// tunIface and TEST_IFACE entries are not changed.
- assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
39605L, 46L, 12259L, 55L, 0L);
- assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 0L, 0L,
+ assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 0L, 0L,
0L, 0L, 0L);
- assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_NO,
72667L, 197L, 43909L, 241L, 0L);
- assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT,
+ assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
9297L, 17L, 4128L, 21L, 0L);
- assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO,
4983L, 10L, 1801L, 12L, 0L);
- assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 0L, 0L,
+ assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 0L, 0L,
0L, 0L, 0L);
- assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, ROAMING_DEFAULT,
+ assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1, ROAMING_NO,
21691L, 41L, 13820L, 51L, 0L);
- assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, ROAMING_DEFAULT, 1281L,
+ assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, ROAMING_NO, 1281L,
2L, 665L, 2L, 0L);
- assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1685L, 5L,
+ assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1685L, 5L,
2070L, 6L, 0L);
// Existing underlying Iface entries are updated
- assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, ROAMING_NO,
44783L, 54L, 13829L, 60L, 0L);
- assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT,
+ assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
0L, 0L, 0L, 0L, 0L);
// VPN underlying Iface entries are updated
- assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, ROAMING_NO,
28304L, 27L, 1719L, 12L, 0L);
- assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT,
+ assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
0L, 0L, 0L, 0L, 0L);
// New entries are added for new application's underlying Iface traffic
- assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE, ROAMING_NO,
72667L, 197L, 41872L, 219L, 0L);
- assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT,
+ assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE, ROAMING_NO,
9297L, 17L, 3936, 19L, 0L);
- assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, ROAMING_DEFAULT,
+ assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1, ROAMING_NO,
21691L, 41L, 13179L, 46L, 0L);
- assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, ROAMING_DEFAULT,
+ assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1, ROAMING_NO,
1281L, 2L, 634L, 1L, 0L);
// New entries are added for debug purpose
- assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_DEFAULT,
+ assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
39605L, 46L, 11690, 49, 0);
- assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, ROAMING_DEFAULT,
+ assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
81964, 214, 45808, 238, 0);
- assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE, ROAMING_DEFAULT,
+ assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE, ROAMING_NO,
4983, 10, 1717, 10, 0);
assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE, ROAMING_ALL,
126552, 270, 59215, 297, 0);
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
index 3feb0e9..c9bc8aa 100644
--- a/core/tests/coretests/src/android/print/BasePrintTest.java
+++ b/core/tests/coretests/src/android/print/BasePrintTest.java
@@ -61,7 +61,6 @@
public abstract class BasePrintTest extends InstrumentationTestCase {
private static final long OPERATION_TIMEOUT = 30000;
- private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s";
private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable ";
@@ -249,8 +248,9 @@
protected void clearPrintSpoolerData() throws Exception {
assertTrue("failed to clear print spooler data",
runShellCommand(getInstrumentation(), String.format(
- "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME))
- .contains(PM_CLEAR_SUCCESS_OUTPUT));
+ "pm clear --user %d %s", CURRENT_USER_ID,
+ PrintManager.PRINT_SPOOLER_PACKAGE_NAME))
+ .contains(PM_CLEAR_SUCCESS_OUTPUT));
}
@SuppressWarnings("unchecked")
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index 5179b42..cbf17a4 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -27,24 +27,9 @@
import android.os.Process;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.print.IPrintDocumentAdapter;
-import android.print.IPrintJobStateChangeListener;
-import android.print.IPrintManager;
-import android.print.IPrinterDiscoveryObserver;
-import android.print.PageRange;
-import android.print.PrintAttributes;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintJob;
-import android.print.PrintJobId;
-import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.print.PrinterCapabilitiesInfo;
-import android.print.PrinterDiscoverySession;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
import android.printservice.PrintServiceInfo;
import android.print.mockservice.MockPrintService;
@@ -134,7 +119,7 @@
if (session.getPrinters().isEmpty()) {
final String PRINTER_NAME = "good printer";
- List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+ List<PrinterInfo> printers = new ArrayList<>();
// Add the printer.
mGoodPrinterId = session.getService()
@@ -184,6 +169,18 @@
}
/**
+ * Create a IPrintServicesChangeListener object.
+ *
+ * @return the object
+ * @throws Exception if the object could not be created.
+ */
+ private IPrintServicesChangeListener createMockIPrintServicesChangeListener() throws Exception {
+ return new PrintManager.PrintServicesChangeListenerWrapper(null,
+ new Handler(Looper.getMainLooper()));
+ }
+
+
+ /**
* Create a IPrinterDiscoveryObserver object.
*
* @return the object
@@ -193,6 +190,16 @@
return new PrinterDiscoverySession.PrinterDiscoveryObserver(null);
}
+ private void startPrinting() {
+ mGoodPrintJob = print(createMockAdapter(), null);
+
+ // Wait for PrintActivity to be ready
+ waitForStartAdapterCallbackCalled();
+
+ // Wait for printer discovery session to be ready
+ waitForPrinterDiscoverySessionStartCallbackCalled();
+ }
+
@Override
public void setUp() throws Exception {
super.setUp();
@@ -201,20 +208,12 @@
mGoodComponentName = getActivity().getComponentName();
- mGoodPrintJob = print(createMockAdapter(), null);
-
mIPrintManager = IPrintManager.Stub
.asInterface(ServiceManager.getService(Context.PRINT_SERVICE));
// Generate dummy printerId which is a valid PrinterId object, but does not correspond to a
// printer
mBadPrinterId = new PrinterId(mGoodComponentName, "dummy printer");
-
- // Wait for PrintActivity to be ready
- waitForStartAdapterCallbackCalled();
-
- // Wait for printer discovery session to be ready
- waitForPrinterDiscoverySessionStartCallbackCalled();
}
/**
@@ -222,11 +221,11 @@
*/
private interface Invokable {
/**
- * Execute the {@link Invokable}
+ * Execute the invokable
*
* @throws Exception
*/
- public void run() throws Exception;
+ void run() throws Exception;
}
/**
@@ -255,6 +254,8 @@
* test IPrintManager.getPrintJobInfo
*/
public void testGetPrintJobInfo() throws Exception {
+ startPrinting();
+
assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(),
mAppId, mUserId).getId());
assertEquals(null, mIPrintManager.getPrintJobInfo(mBadPrintJobId, mAppId, mUserId));
@@ -274,6 +275,8 @@
* test IPrintManager.getPrintJobInfos
*/
public void testGetPrintJobInfos() throws Exception {
+ startPrinting();
+
List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId);
boolean foundPrintJob = false;
@@ -304,7 +307,7 @@
final IPrintDocumentAdapter adapter = new PrintManager
.PrintDocumentAdapterDelegate(getActivity(), createMockAdapter());
- // Valid parameters are tested in setUp()
+ startPrinting();
assertException(new Invokable() {
@Override
@@ -352,6 +355,8 @@
* test IPrintManager.cancelPrintJob
*/
public void testCancelPrintJob() throws Exception {
+ startPrinting();
+
// Invalid print jobs IDs do not produce an exception
mIPrintManager.cancelPrintJob(mBadPrintJobId, mAppId, mUserId);
mIPrintManager.cancelPrintJob(null, mAppId, mUserId);
@@ -373,6 +378,8 @@
* test IPrintManager.restartPrintJob
*/
public void testRestartPrintJob() throws Exception {
+ startPrinting();
+
mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
// Invalid print jobs IDs do not produce an exception
@@ -438,22 +445,103 @@
}
/**
- * test IPrintManager.getInstalledPrintServices
+ * test IPrintManager.addPrintServicesChangeListener
*/
- public void testGetInstalledPrintServices() throws Exception {
- List<PrintServiceInfo> printServices = mIPrintManager.getInstalledPrintServices(mUserId);
- assertTrue(printServices.size() >= 2);
+ public void testAddPrintServicesChangeListener() throws Exception {
+ final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
+
+ mIPrintManager.addPrintServicesChangeListener(listener, mUserId);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.addPrintServicesChangeListener(null, mUserId);
+ }
+ }, NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
}
/**
- * test IPrintManager.getEnabledPrintServices
+ * test IPrintManager.removePrintServicesChangeListener
*/
- public void testGetEnabledPrintServices() throws Exception {
- List<PrintServiceInfo> printServices = mIPrintManager.getEnabledPrintServices(mUserId);
+ public void testRemovePrintServicesChangeListener() throws Exception {
+ final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
+
+ mIPrintManager.addPrintServicesChangeListener(listener, mUserId);
+ mIPrintManager.removePrintServicesChangeListener(listener, mUserId);
+
+ // Removing unknown listeners is a no-op
+ mIPrintManager.removePrintServicesChangeListener(listener, mUserId);
+
+ mIPrintManager.addPrintServicesChangeListener(listener, mUserId);
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.removePrintServicesChangeListener(null, mUserId);
+ }
+ }, NullPointerException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.getPrintServices
+ */
+ public void testGetPrintServices() throws Exception {
+ List<PrintServiceInfo> printServices = mIPrintManager.getPrintServices(
+ PrintManager.ALL_SERVICES, mUserId);
assertTrue(printServices.size() >= 2);
+ printServices = mIPrintManager.getPrintServices(0, mUserId);
+ assertEquals(printServices, null);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.getPrintServices(~PrintManager.ALL_SERVICES, mUserId);
+ }
+ }, IllegalArgumentException.class);
+
+ // Cannot test bad user Id as these tests are allowed to call across users
+ }
+
+ /**
+ * test IPrintManager.setPrintServiceEnabled
+ */
+ public void testSetPrintServiceEnabled() throws Exception {
+ final ComponentName printService = mIPrintManager.getPrintServices(
+ PrintManager.ALL_SERVICES, mUserId).get(0).getComponentName();
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.setPrintServiceEnabled(printService, false, mUserId);
+ }
+ }, SecurityException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.setPrintServiceEnabled(printService, true, mUserId);
+ }
+ }, SecurityException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.setPrintServiceEnabled(new ComponentName("bad", "name"), true,
+ mUserId);
+ }
+ }, SecurityException.class);
+
+ assertException(new Invokable() {
+ @Override
+ public void run() throws Exception {
+ mIPrintManager.setPrintServiceEnabled(null, true, mUserId);
+ }
+ }, SecurityException.class);
+
// Cannot test bad user Id as these tests are allowed to call across users
}
@@ -486,6 +574,8 @@
* test IPrintManager.startPrinterDiscovery
*/
public void testStartPrinterDiscovery() throws Exception {
+ startPrinting();
+
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
final List<PrinterId> goodPrinters = new ArrayList<>();
goodPrinters.add(mGoodPrinterId);
@@ -549,6 +639,8 @@
* test IPrintManager.validatePrinters
*/
public void testValidatePrinters() throws Exception {
+ startPrinting();
+
final List<PrinterId> goodPrinters = new ArrayList<>();
goodPrinters.add(mGoodPrinterId);
@@ -587,6 +679,8 @@
* test IPrintManager.startPrinterStateTracking
*/
public void testStartPrinterStateTracking() throws Exception {
+ startPrinting();
+
mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
// Bad printers do no cause exceptions
@@ -606,6 +700,8 @@
* test IPrintManager.getCustomPrinterIcon
*/
public void testGetCustomPrinterIcon() throws Exception {
+ startPrinting();
+
mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId);
// Bad printers do no cause exceptions
@@ -625,6 +721,8 @@
* test IPrintManager.stopPrinterStateTracking
*/
public void testStopPrinterStateTracking() throws Exception {
+ startPrinting();
+
mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId);
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index d2e811c..3be9cfc 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -29,7 +29,8 @@
/**
* Test backspace key handling of {@link android.text.method.BaseKeyListner}.
*
- * TODO: Move some of test cases to the CTS.
+ * Only contains edge cases. For normal cases, see {@see android.text.method.cts.BackspaceTest}.
+ * TODO: introduce test cases for surrogate pairs and replacement span.
*/
public class BackspaceTest extends KeyListenerTestCase {
private static final BaseKeyListener mKeyListener = new BaseKeyListener() {
@@ -65,85 +66,9 @@
}
@SmallTest
- public void testSurrogatePairs() {
- EditorState state = new EditorState();
-
- state.setByString("U+1F441 |");
- backspace(state, 0);
- state.assertEquals("|");
-
- state.setByString("U+1F441 U+1F5E8 |");
- backspace(state, 0);
- state.assertEquals("U+1F441 |");
- backspace(state, 0);
- state.assertEquals("|");
-
- // TODO: introduce edge cases.
- }
-
- @SmallTest
- public void testReplacementSpan() {
- EditorState state = new EditorState();
-
- // ReplacementSpan will be set to "()" region.
- state.setByString("'abc' ( 'de' ) 'fg' |");
- backspace(state, 0);
- state.assertEquals("'abc' ( 'de' ) 'f' |");
- backspace(state, 0);
- state.assertEquals("'abc' ( 'de' ) |");
- backspace(state, 0);
- state.assertEquals("'abc' |");
- backspace(state, 0);
- state.assertEquals("'ab' |");
- backspace(state, 0);
- state.assertEquals("'a' |");
- backspace(state, 0);
- state.assertEquals("|");
-
- state.setByString("'abc' [ ( 'de' ) ] 'fg'");
- backspace(state, 0);
- state.assertEquals("'abc' | 'fg'");
- backspace(state, 0);
- state.assertEquals("'ab' | 'fg'");
- backspace(state, 0);
- state.assertEquals("'a' | 'fg'");
- backspace(state, 0);
- state.assertEquals("| 'fg'");
- backspace(state, 0);
- state.assertEquals("| 'fg'");
-
- state.setByString("'ab' [ 'c' ( 'de' ) 'f' ] 'g'");
- backspace(state, 0);
- state.assertEquals("'ab' | 'g'");
- backspace(state, 0);
- state.assertEquals("'a' | 'g'");
- backspace(state, 0);
- state.assertEquals("| 'g'");
- backspace(state, 0);
- state.assertEquals("| 'g'");
-
- // TODO: introduce edge cases.
- }
-
- @SmallTest
public void testCombiningEnclosingKeycaps() {
EditorState state = new EditorState();
- // U+20E3 is COMBINING ENCLOSING KEYCAP.
- state.setByString("'1' U+20E3 |");
- backspace(state, 0);
- state.assertEquals("|");
-
- // Variation selector before COMBINING ECLOSING KEYCAP
- state.setByString("'1' U+FE0E U+20E3 |");
- backspace(state, 0);
- state.assertEquals("|");
-
- state.setByString("'1' U+E0101 U+20E3 |");
- backspace(state, 0);
- state.assertEquals("|");
-
- // Edge cases
// multiple COMBINING ENCLOSING KEYCAP
state.setByString("'1' U+20E3 U+20E3 |");
backspace(state, 0);
@@ -168,17 +93,6 @@
public void testVariationSelector() {
EditorState state = new EditorState();
- // U+FE0F is VARIATION SELECTOR-16.
- state.setByString("'#' U+FE0F |");
- backspace(state, 0);
- state.assertEquals("|");
-
- // U+E0100 is VARIATION SELECTOR-17.
- state.setByString("U+845B U+E0100 |");
- backspace(state, 0);
- state.assertEquals("|");
-
- // Edge cases
// Isolated variation selector
state.setByString("U+FE0F |");
backspace(state, 0);
@@ -243,20 +157,6 @@
public void testEmojiZWJSequence() {
EditorState state = new EditorState();
- // U+200D is ZERO WIDTH JOINER.
- state.setByString("U+1F441 U+200D U+1F5E8 |");
- backspace(state, 0);
- state.assertEquals("|");
-
- state.setByString("U+1F441 U+200D U+1F5E8 U+FE0E |");
- 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("|");
-
- // Edge cases
// End with ZERO WIDTH JOINER
state.setByString("U+1F441 U+200D |");
backspace(state, 0);
@@ -307,35 +207,6 @@
public void testFlags() {
EditorState state = new EditorState();
- // U+1F1FA is REGIONAL INDICATOR SYMBOL LETTER U.
- // U+1F1F8 is REGIONAL INDICATOR SYMBOL LETTER S.
- state.setByString("U+1F1FA U+1F1F8 |");
- backspace(state, 0);
- state.assertEquals("|");
-
- state.setByString("'a' U+1F1FA U+1F1F8 |");
- backspace(state, 0);
- state.assertEquals("'a' |");
- backspace(state, 0);
- state.assertEquals("|");
-
- state.setByString("U+1F1FA U+1F1F8 U+1F1FA U+1F1F8 |");
- backspace(state, 0);
- state.assertEquals("U+1F1FA U+1F1F8 |");
- backspace(state, 0);
- state.assertEquals("|");
-
- state.setByString("'a' U+1F1FA U+1F1F8 'b' U+1F1FA U+1F1F8 |");
- backspace(state, 0);
- state.assertEquals("'a' U+1F1FA U+1F1F8 'b' |");
- backspace(state, 0);
- state.assertEquals("'a' U+1F1FA U+1F1F8 |");
- backspace(state, 0);
- state.assertEquals("'a' |");
- backspace(state, 0);
- state.assertEquals("|");
-
- // Edcae cases
// Isolated regional indicator symbol
state.setByString("U+1F1FA |");
backspace(state, 0);
@@ -353,12 +224,6 @@
public void testEmojiModifier() {
EditorState state = new EditorState();
- // U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2.
- state.setByString("U+1F466 U+1F3FB |");
- backspace(state, 0);
- state.assertEquals("|");
-
- // Edge cases
// Isolated emoji modifier
state.setByString("U+1F3FB |");
backspace(state, 0);
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index da17045..f7dab2d 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -29,7 +29,8 @@
/**
* Test forward delete key handling of {@link android.text.method.BaseKeyListener}.
*
- * TODO: Move some of test cases to the CTS.
+ * Only contains edge cases. For normal cases, see {@see android.text.method.cts.ForwardDeleteTest}.
+ * TODO: introduce test cases for surrogate pairs and replacement span.
*/
public class ForwardDeleteTest extends KeyListenerTestCase {
private static final BaseKeyListener mKeyListener = new BaseKeyListener() {
@@ -65,73 +66,9 @@
}
@SmallTest
- public void testSurrogatePairs() {
- EditorState state = new EditorState();
-
- // U+1F441 is EYE
- state.setByString("| U+1F441");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- // U+1F5E8 is LEFT SPEECH BUBBLE
- state.setByString("| U+1F441 U+1F5E8");
- forwardDelete(state, 0);
- state.assertEquals("| U+1F5E8");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- // TODO: introduce edge cases.
- }
-
- @SmallTest
- public void testReplacementSpan() {
- EditorState state = new EditorState();
-
- state.setByString("| 'abc' ( 'de' ) 'fg'");
- forwardDelete(state, 0);
- state.assertEquals("| 'bc' ( 'de' ) 'fg'");
- forwardDelete(state, 0);
- state.assertEquals("| 'c' ( 'de' ) 'fg'");
- forwardDelete(state, 0);
- state.assertEquals("| ( 'de' ) 'fg'");
- forwardDelete(state, 0);
- state.assertEquals("| 'fg'");
- forwardDelete(state, 0);
- state.assertEquals("| 'g'");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- state.setByString("'abc' [ ( 'de' ) ] 'fg'");
- forwardDelete(state, 0);
- state.assertEquals("'abc' | 'fg'");
- forwardDelete(state, 0);
- state.assertEquals("'abc' | 'g'");
- forwardDelete(state, 0);
- state.assertEquals("'abc' |");
- forwardDelete(state, 0);
- state.assertEquals("'abc' |");
-
- state.setByString("'ab' [ 'c' ( 'de' ) 'f' ] 'g'");
- forwardDelete(state, 0);
- state.assertEquals("'ab' | 'g'");
- forwardDelete(state, 0);
- state.assertEquals("'ab' |");
- forwardDelete(state, 0);
- state.assertEquals("'ab' |");
-
- // TODO: introduce edge cases.
- }
-
- @SmallTest
public void testCombiningEnclosingKeycaps() {
EditorState state = new EditorState();
- // U+20E3 is COMBINING ENCLOSING KEYCAP.
- state.setByString("| '1' U+20E3");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- // Edge cases
// multiple COMBINING ENCLOSING KEYCAP
state.setByString("| '1' U+20E3 U+20E3");
forwardDelete(state, 0);
@@ -152,17 +89,6 @@
public void testVariationSelector() {
EditorState state = new EditorState();
- // U+FE0F is VARIATION SELECTOR-16.
- state.setByString("| '#' U+FE0F");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- // U+E0100 is VARIATION SELECTOR-17.
- state.setByString("| U+845B U+E0100");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- // Edge cases
// Isolated variation selectors
state.setByString("| U+FE0F");
forwardDelete(state, 0);
@@ -211,16 +137,6 @@
public void testEmojiZeroWidthJoinerSequence() {
EditorState state = new EditorState();
- // U+200D is ZERO WIDTH JOINER.
- state.setByString("| U+1F441 U+200D U+1F5E8");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- state.setByString("| U+1F468 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- // Edge cases
// End with ZERO WIDTH JOINER
state.setByString("| U+1F441 U+200D");
forwardDelete(state, 0);
@@ -255,19 +171,6 @@
public void testFlags() {
EditorState state = new EditorState();
- // U+1F1FA is REGIONAL INDICATOR SYMBOL LETTER U.
- // U+1F1F8 is REGIONAL INDICATOR SYMBOL LETTER S.
- state.setByString("| U+1F1FA U+1F1F8");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- state.setByString("| U+1F1FA U+1F1F8 U+1F1FA U+1F1F8");
- forwardDelete(state, 0);
- state.assertEquals("| U+1F1FA U+1F1F8");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- // Edge cases
// Isolated regional indicator symbol
state.setByString("| U+1F1FA");
forwardDelete(state, 0);
@@ -285,12 +188,6 @@
public void testEmojiModifier() {
EditorState state = new EditorState();
- // U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2.
- state.setByString("| U+1F466 U+1F3FB");
- forwardDelete(state, 0);
- state.assertEquals("|");
-
- // Edge cases
// Isolated emoji modifier
state.setByString("| U+1F3FB");
forwardDelete(state, 0);
diff --git a/core/tests/coretests/src/android/transition/AutoTransitionTest.java b/core/tests/coretests/src/android/transition/AutoTransitionTest.java
new file mode 100644
index 0000000..834fb7a
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/AutoTransitionTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.transition;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+public class AutoTransitionTest {
+ @Test
+ @SmallTest
+ public void testFadeOutMoveFadeIn() throws Throwable {
+ AutoTransition autoTransition = new AutoTransition();
+ assertEquals(3, autoTransition.getTransitionCount());
+ Transition fadeOut = autoTransition.getTransitionAt(0);
+ assertNotNull(fadeOut);
+ assertTrue(fadeOut instanceof Fade);
+ assertEquals(Visibility.MODE_OUT, ((Fade)fadeOut).getMode());
+
+ Transition move = autoTransition.getTransitionAt(1);
+ assertNotNull(move);
+ assertTrue(move instanceof ChangeBounds);
+
+ Transition fadeIn = autoTransition.getTransitionAt(2);
+ assertNotNull(fadeIn);
+ assertTrue(fadeIn instanceof Fade);
+ assertEquals(Visibility.MODE_IN, ((Fade)fadeIn).getMode());
+
+ assertEquals(TransitionSet.ORDERING_SEQUENTIAL, autoTransition.getOrdering());
+ }
+}
diff --git a/core/tests/coretests/src/android/transition/SlideTransitionTest.java b/core/tests/coretests/src/android/transition/SlideTransitionTest.java
new file mode 100644
index 0000000..8b9ec74
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/SlideTransitionTest.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.transition;
+
+import android.animation.AnimatorSetActivity;
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
+public class SlideTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+
+ Activity mActivity;
+
+ public SlideTransitionTest() {
+ super(AnimatorSetActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ mActivity = getActivity();
+ }
+
+ @SmallTest
+ public void testShortSlide() throws Throwable {
+ final float slideFraction = 0.5f;
+ final View square1 = mActivity.findViewById(R.id.square1);
+ final View sceneRoot = mActivity.findViewById(R.id.container);
+ final SlideTranslationValueRatchet ratchet = new SlideTranslationValueRatchet(square1);
+ square1.getViewTreeObserver().addOnPreDrawListener(ratchet);
+
+ final Slide slideOut = new Slide(Gravity.BOTTOM);
+ final float finalOffsetOut = sceneRoot.getHeight() * slideFraction;
+ slideOut.setSlideFraction(slideFraction);
+ TransitionLatch latch = setVisibilityInTransition(slideOut, R.id.square1, View.INVISIBLE);
+ assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ assertEquals(0f, square1.getTranslationY(), 0.1f);
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ Thread.sleep(100);
+ assertFalse(square1.getTranslationY() < 0.1
+ || square1.getTranslationY() > finalOffsetOut - 0.1);
+ assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ // Give this 20% slop in case some frames get dropped.
+ assertTrue(finalOffsetOut * 0.8 < ratchet.maxY);
+ assertTrue(finalOffsetOut + 0.1 > ratchet.maxY);
+ assertEquals(View.INVISIBLE, square1.getVisibility());
+
+ ratchet.reset();
+ final Slide slideIn = new Slide(Gravity.BOTTOM);
+ final float initialOffsetIn = sceneRoot.getHeight() * slideFraction;
+ slideIn.setSlideFraction(slideFraction);
+ latch = setVisibilityInTransition(slideIn, R.id.square1, View.VISIBLE);
+ assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ assertEquals(initialOffsetIn, square1.getTranslationY(), 0.1f);
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ Thread.sleep(100);
+ assertFalse(square1.getTranslationY() < 0.1
+ || square1.getTranslationY() > initialOffsetIn - 0.1);
+ assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(0f, ratchet.minY, 0.1);
+ assertEquals(0f, square1.getTranslationY(), 0.1);
+ assertEquals(View.VISIBLE, square1.getVisibility());
+
+ square1.getViewTreeObserver().removeOnPreDrawListener(ratchet);
+ }
+
+ public TransitionLatch setVisibilityInTransition(final Transition transition, int viewId,
+ final int visibility) throws Throwable {
+ final ViewGroup sceneRoot = (ViewGroup) mActivity.findViewById(R.id.container);
+ final View view = sceneRoot.findViewById(viewId);
+ TransitionLatch latch = new TransitionLatch();
+ transition.addListener(latch);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(sceneRoot, transition);
+ view.setVisibility(visibility);
+ }
+ });
+ return latch;
+ }
+
+ public static class TransitionLatch implements Transition.TransitionListener {
+ public CountDownLatch startLatch = new CountDownLatch(1);
+ public CountDownLatch endLatch = new CountDownLatch(1);
+ public CountDownLatch cancelLatch = new CountDownLatch(1);
+ public CountDownLatch pauseLatch = new CountDownLatch(1);
+ public CountDownLatch resumeLatch = new CountDownLatch(1);
+
+ @Override
+ public void onTransitionStart(Transition transition) {
+ startLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ endLatch.countDown();
+ transition.removeListener(this);
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ cancelLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ pauseLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionResume(Transition transition) {
+ resumeLatch.countDown();
+ }
+ }
+
+ private static class SlideTranslationValueRatchet
+ implements ViewTreeObserver.OnPreDrawListener {
+
+ private final View mView;
+ private boolean mInitialized;
+ public float minX = Float.NaN;
+ public float minY = Float.NaN;
+ public float maxX = Float.NaN;
+ public float maxY = Float.NaN;
+
+ public SlideTranslationValueRatchet(View view) {
+ mView = view;
+ }
+
+ public void reset() {
+ minX = minY = maxX = maxY = Float.NaN;
+ mInitialized = false;
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ if (!mInitialized) {
+ minX = maxX = mView.getTranslationX();
+ minY = maxY = mView.getTranslationY();
+ mInitialized = true;
+ } else {
+ minX = Math.min(minX, mView.getTranslationX());
+ minY = Math.min(minY, mView.getTranslationY());
+ maxX = Math.max(maxX, mView.getTranslationX());
+ maxY = Math.max(maxY, mView.getTranslationY());
+ }
+ return true;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/transition/TransitionTest.java b/core/tests/coretests/src/android/transition/TransitionTest.java
new file mode 100644
index 0000000..7e72e25
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/TransitionTest.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.transition;
+
+import android.animation.AnimatorSetActivity;
+import android.app.Activity;
+import android.graphics.Rect;
+import android.test.ActivityInstrumentationTestCase2;
+import android.transition.Transition.EpicenterCallback;
+import android.util.ArrayMap;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.TextView;
+
+import com.android.frameworks.coretests.R;
+
+public class TransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+ Activity mActivity;
+ public TransitionTest() {
+ super(AnimatorSetActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ mActivity = getActivity();
+ }
+
+ public void testClone() throws Throwable {
+ View square1 = mActivity.findViewById(R.id.square1);
+ View square2 = mActivity.findViewById(R.id.square2);
+ View square3 = mActivity.findViewById(R.id.square3);
+ Fade fade = new Fade();
+ fade.setStartDelay(1000);
+ fade.setDuration(1001);
+
+ fade.addTarget(square1);
+ fade.excludeTarget(square2, true);
+ fade.excludeChildren(square3, true);
+
+ fade.addTarget(R.id.square4);
+ fade.excludeTarget(R.id.square3, true);
+ fade.excludeChildren(R.id.square2, true);
+
+ fade.addTarget("hello");
+ fade.excludeTarget("world", true);
+
+ fade.addTarget(View.class);
+ fade.excludeTarget(TextView.class, true);
+
+ fade.setMatchOrder(Transition.MATCH_ID);
+ fade.setPropagation(new CircularPropagation());
+ fade.setPathMotion(new ArcMotion());
+ fade.setInterpolator(new AccelerateInterpolator());
+ fade.setNameOverrides(new ArrayMap<>());
+
+ EpicenterCallback epicenterCallback = new EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return null;
+ }
+ };
+
+ fade.setEpicenterCallback(epicenterCallback);
+
+ Fade clone = (Fade) fade.clone();
+ assertEquals(fade.mStartDelay, clone.mStartDelay);
+ assertEquals(fade.mDuration, clone.mDuration);
+ assertEquals(fade.mInterpolator, clone.mInterpolator);
+ assertEquals(fade.mPropagation, clone.mPropagation);
+ assertEquals(fade.getPathMotion(), clone.getPathMotion());
+ assertEquals(fade.getEpicenterCallback(), clone.getEpicenterCallback());
+ assertEquals(fade.mNameOverrides, clone.mNameOverrides);
+ assertEquals(fade.mMatchOrder, clone.mMatchOrder);
+
+ assertEquals(fade.mTargets, clone.mTargets);
+ assertEquals(fade.mTargetExcludes, clone.mTargetExcludes);
+ assertEquals(fade.mTargetChildExcludes, clone.mTargetChildExcludes);
+
+ assertEquals(fade.mTargetIds, clone.mTargetIds);
+ assertEquals(fade.mTargetIdExcludes, clone.mTargetIdExcludes);
+ assertEquals(fade.mTargetIdChildExcludes, clone.mTargetIdChildExcludes);
+
+ assertEquals(fade.mTargetNames, clone.mTargetNames);
+ assertEquals(fade.mTargetNameExcludes, clone.mTargetNameExcludes);
+
+ assertEquals(fade.mTargetTypes, clone.mTargetTypes);
+ assertEquals(fade.mTargetTypeExcludes, clone.mTargetTypeExcludes);
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index 3d8fe69..a37abf1 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -32,6 +32,8 @@
/**
* SuggestionsPopupWindowTest tests.
+ *
+ * TODO: Add tests for when there are no suggestions
*/
public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2<TextViewActivity> {
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 00df87d..923b829 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -143,6 +143,8 @@
onView(withId(R.id.textview)).check(hasSelection(""));
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("i")));
+
+ // TODO: Add tests for suggestions
}
@SmallTest
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 5dae4a8..4a4727f 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -44,8 +44,11 @@
import com.android.frameworks.coretests.R;
+import android.support.test.espresso.action.EspressoKey;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.text.Selection;
+import android.text.Spannable;
import android.view.KeyEvent;
import static org.hamcrest.Matchers.anyOf;
@@ -172,6 +175,12 @@
onView(withId(R.id.textview)).check(hasSelection(""));
assertNoSelectionHandles();
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex("abc ghi.def".length()));
+
+ // Test undo returns to the original state.
+ onView(withId(R.id.textview)).perform(pressKey(
+ (new EspressoKey.Builder()).withCtrlPressed(true).withKeyCode(KeyEvent.KEYCODE_Z)
+ .build()));
+ onView(withId(R.id.textview)).check(matches(withText(text)));
}
@SmallTest
@@ -527,4 +536,68 @@
.perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('i')));
onView(withId(R.id.textview)).check(hasSelection("hijk"));
}
+
+ @SmallTest
+ public void testSetSelectionAndActionMode() throws Exception {
+ final String text = "abc def";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(replaceText(text));
+
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ assertFloatingToolbarIsNotDisplayed();
+ textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
+ getInstrumentation().waitForIdleSync();
+ sleepForFloatingToolbarPopup();
+ // Don't automatically start action mode.
+ assertFloatingToolbarIsNotDisplayed();
+ // Make sure that "Select All" is included in the selection action mode when the entire text
+ // is not selected.
+ onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('e')));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ // Changing the selection range by API should not interrupt the selection action mode.
+ textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0, 3));
+ getInstrumentation().waitForIdleSync();
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.selectAll));
+ // Make sure that "Select All" is no longer included when the entire text is selected by
+ // API.
+ textView.post(
+ () -> Selection.setSelection((Spannable) textView.getText(), 0, text.length()));
+ getInstrumentation().waitForIdleSync();
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ assertFloatingToolbarDoesNotContainItem(
+ getActivity().getString(com.android.internal.R.string.selectAll));
+ // Make sure that shrinking the selection range to cursor (an empty range) by API
+ // terminates selection action mode and does not trigger the insertion action mode.
+ textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0));
+ getInstrumentation().waitForIdleSync();
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsNotDisplayed();
+ // Make sure that user click can trigger the insertion action mode.
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
+ onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ // Make sure that an existing insertion action mode keeps alive after the insertion point is
+ // moved by API.
+ textView.post(() -> Selection.setSelection((Spannable) textView.getText(), 0));
+ getInstrumentation().waitForIdleSync();
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ assertFloatingToolbarDoesNotContainItem(
+ getActivity().getString(com.android.internal.R.string.copy));
+ // Make sure that selection action mode is started after selection is created by API when
+ // insertion action mode is active.
+ textView.post(
+ () -> Selection.setSelection((Spannable) textView.getText(), 1, text.length()));
+ getInstrumentation().waitForIdleSync();
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ assertFloatingToolbarContainsItem(
+ getActivity().getString(com.android.internal.R.string.copy));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index 719b274..6b5e4ad 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -219,9 +219,28 @@
final InputMethodSubtype nonAutoHi = createDummyInputMethodSubtype("hi",
SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
!IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoSrCyrl = createDummyInputMethodSubtype("sr",
+ "sr-Cyrl", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
+ !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+ !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoSrLatn = createDummyInputMethodSubtype("sr_ZZ",
+ "sr-Latn", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
+ !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
+ !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
final InputMethodSubtype nonAutoHandwritingEn = createDummyInputMethodSubtype("en",
SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
!IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoHandwritingFr = createDummyInputMethodSubtype("fr",
+ SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
+ !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoHandwritingSrCyrl = createDummyInputMethodSubtype("sr",
+ "sr-Cyrl", SUBTYPE_MODE_HANDWRITING, !IS_AUX,
+ !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+ !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
+ final InputMethodSubtype nonAutoHandwritingSrLatn = createDummyInputMethodSubtype("sr_ZZ",
+ "sr-Latn", SUBTYPE_MODE_HANDWRITING, !IS_AUX,
+ !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
+ !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype =
createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
@@ -242,6 +261,8 @@
subtypes.add(autoSubtype); // overridesImplicitlyEnabledSubtype == true
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -264,6 +285,8 @@
subtypes.add(nonAutoFil);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -271,7 +294,9 @@
final ArrayList<InputMethodSubtype> result =
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(LOCALE_EN_US), imi);
+ assertEquals(2, result.size());
verifyEquality(nonAutoEnUS, result.get(0));
+ verifyEquality(nonAutoHandwritingEn, result.get(1));
}
// Make sure that a subtype whose locale is exactly equal to the specified locale is
@@ -284,6 +309,8 @@
subtypes.add(nonAutoJa);
subtypes.add(nonAutoFil);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -291,8 +318,9 @@
final ArrayList<InputMethodSubtype> result =
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(LOCALE_EN_GB), imi);
- assertEquals(1, result.size());
+ assertEquals(2, result.size());
verifyEquality(nonAutoEnGB, result.get(0));
+ verifyEquality(nonAutoHandwritingEn, result.get(1));
}
// If there is no automatic subtype (overridesImplicitlyEnabledSubtype:true) and
@@ -306,6 +334,8 @@
subtypes.add(nonAutoFil);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -313,8 +343,9 @@
final ArrayList<InputMethodSubtype> result =
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(LOCALE_FR), imi);
- assertEquals(1, result.size());
+ assertEquals(2, result.size());
verifyEquality(nonAutoFrCA, result.get(0));
+ verifyEquality(nonAutoHandwritingFr, result.get(1));
}
// Then make sure that a subtype (locale: "fr") can be found with locale: "fr_CA".
{
@@ -324,6 +355,8 @@
subtypes.add(nonAutoFil);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -331,8 +364,9 @@
final ArrayList<InputMethodSubtype> result =
InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
getResourcesForLocales(LOCALE_FR_CA), imi);
- assertEquals(1, result.size());
+ assertEquals(2, result.size());
verifyEquality(nonAutoFrCA, result.get(0));
+ verifyEquality(nonAutoHandwritingFr, result.get(1));
}
// Make sure that subtypes which have "EnabledWhenDefaultIsNotAsciiCapable" in its
@@ -343,6 +377,8 @@
subtypes.add(nonAutoJa); // not ASCII capable
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
"com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
@@ -363,6 +399,7 @@
subtypes.add(nonAutoHi);
subtypes.add(nonAutoEnUS);
subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
@@ -379,6 +416,7 @@
subtypes.add(nonAutoEnUS);
subtypes.add(nonAutoHi);
subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
final InputMethodInfo imi = createDummyInputMethodInfo(
"com.android.apps.inputmethod.latin",
@@ -393,6 +431,7 @@
{
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
subtypes.add(nonAutoEnUS);
subtypes.add(nonAutoHi);
subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
@@ -407,6 +446,85 @@
verifyEquality(nonAutoEnUS, result.get(0));
}
+ // Make sure that both language and script are taken into account to find the best matching
+ // subtype.
+ {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
+ subtypes.add(nonAutoEnUS);
+ subtypes.add(nonAutoSrCyrl);
+ subtypes.add(nonAutoSrLatn);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
+ subtypes.add(nonAutoHandwritingSrCyrl);
+ subtypes.add(nonAutoHandwritingSrLatn);
+ final InputMethodInfo imi = createDummyInputMethodInfo(
+ "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+ subtypes);
+ final ArrayList<InputMethodSubtype> result =
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ getResourcesForLocales(Locale.forLanguageTag("sr-Latn-RS")), imi);
+ assertEquals(2, result.size());
+ assertThat(nonAutoSrLatn, isIn(result));
+ assertThat(nonAutoHandwritingSrLatn, isIn(result));
+ }
+ {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
+ subtypes.add(nonAutoEnUS);
+ subtypes.add(nonAutoSrCyrl);
+ subtypes.add(nonAutoSrLatn);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
+ subtypes.add(nonAutoHandwritingSrCyrl);
+ subtypes.add(nonAutoHandwritingSrLatn);
+ final InputMethodInfo imi = createDummyInputMethodInfo(
+ "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+ subtypes);
+ final ArrayList<InputMethodSubtype> result =
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ getResourcesForLocales(Locale.forLanguageTag("sr-Cyrl-RS")), imi);
+ assertEquals(2, result.size());
+ assertThat(nonAutoSrCyrl, isIn(result));
+ assertThat(nonAutoHandwritingSrCyrl, isIn(result));
+ }
+
+ // Make sure that secondary locales are taken into account to find the best matching
+ // subtype.
+ {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
+ subtypes.add(nonAutoEnUS);
+ subtypes.add(nonAutoEnGB);
+ subtypes.add(nonAutoSrCyrl);
+ subtypes.add(nonAutoSrLatn);
+ subtypes.add(nonAutoFr);
+ subtypes.add(nonAutoFrCA);
+ subtypes.add(nonAutoHandwritingEn);
+ subtypes.add(nonAutoHandwritingFr);
+ subtypes.add(nonAutoHandwritingSrCyrl);
+ subtypes.add(nonAutoHandwritingSrLatn);
+ final InputMethodInfo imi = createDummyInputMethodInfo(
+ "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+ subtypes);
+ final ArrayList<InputMethodSubtype> result =
+ InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+ getResourcesForLocales(
+ Locale.forLanguageTag("sr-Latn-RS-x-android"),
+ Locale.forLanguageTag("ja-JP"),
+ Locale.forLanguageTag("fr-FR"),
+ Locale.forLanguageTag("en-GB"),
+ Locale.forLanguageTag("en-US")),
+ imi);
+ assertEquals(6, result.size());
+ assertThat(nonAutoEnGB, isIn(result));
+ assertThat(nonAutoFr, isIn(result));
+ assertThat(nonAutoSrLatn, isIn(result));
+ assertThat(nonAutoHandwritingEn, isIn(result));
+ assertThat(nonAutoHandwritingFr, isIn(result));
+ assertThat(nonAutoHandwritingSrLatn, isIn(result));
+ }
+
// Make sure that 3-letter language code can be handled.
{
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
@@ -755,7 +873,15 @@
private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode,
boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype,
boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable) {
+ return createDummyInputMethodSubtype(locale, null /* languageTag */, mode, isAuxiliary,
+ overridesImplicitlyEnabledSubtype, isAsciiCapable,
+ isEnabledWhenDefaultIsNotAsciiCapable);
+ }
+ private static InputMethodSubtype createDummyInputMethodSubtype(String locale,
+ String languageTag, String mode, boolean isAuxiliary,
+ boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable,
+ boolean isEnabledWhenDefaultIsNotAsciiCapable) {
final StringBuilder subtypeExtraValue = new StringBuilder();
if (isEnabledWhenDefaultIsNotAsciiCapable) {
subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR);
@@ -773,6 +899,7 @@
.setSubtypeNameResId(0)
.setSubtypeIconResId(0)
.setSubtypeLocale(locale)
+ .setLanguageTag(languageTag)
.setSubtypeMode(mode)
.setSubtypeExtraValue(subtypeExtraValue.toString())
.setIsAuxiliary(isAuxiliary)
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
index b9c2da7..deba40f 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
@@ -50,6 +50,22 @@
}
@SmallTest
+ public void testFilterDoesNotMatchAnything() throws Exception {
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("en-US"));
+ availableLocales.add(Locale.forLanguageTag("fr-CA"));
+ availableLocales.add(Locale.forLanguageTag("in"));
+ availableLocales.add(Locale.forLanguageTag("ja"));
+ availableLocales.add(Locale.forLanguageTag("fil"));
+
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("zh-Hans-TW");
+
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(0, dest.size());
+ }
+
+ @SmallTest
public void testFilterByLanguageEmptySource() throws Exception {
final ArrayList<Locale> availableLocales = new ArrayList<>();
@@ -124,21 +140,36 @@
@SmallTest
public void testFilterByLanguage() throws Exception {
- final ArrayList<Locale> availableLocales = new ArrayList<>();
- availableLocales.add(Locale.forLanguageTag("en-US"));
- availableLocales.add(Locale.forLanguageTag("fr-CA"));
- availableLocales.add(Locale.forLanguageTag("in"));
- availableLocales.add(Locale.forLanguageTag("ja"));
- availableLocales.add(Locale.forLanguageTag("fil"));
+ {
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("en-US"));
+ availableLocales.add(Locale.forLanguageTag("fr-CA"));
+ availableLocales.add(Locale.forLanguageTag("in"));
+ availableLocales.add(Locale.forLanguageTag("ja"));
+ availableLocales.add(Locale.forLanguageTag("fil"));
- final LocaleList preferredLocales = LocaleList.forLanguageTags("fr,en-US,ja-JP");
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("fr,en-US,ja-JP");
- final ArrayList<Locale> dest = new ArrayList<>();
- LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
- assertEquals(3, dest.size());
- assertEquals(availableLocales.get(1), dest.get(0)); // "fr-CA"
- assertEquals(availableLocales.get(0), dest.get(1)); // "en-US"
- assertEquals(availableLocales.get(3), dest.get(2)); // "ja"
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(3, dest.size());
+ assertEquals(availableLocales.get(1), dest.get(0)); // "fr-CA"
+ assertEquals(availableLocales.get(0), dest.get(1)); // "en-US"
+ assertEquals(availableLocales.get(3), dest.get(2)); // "ja"
+ }
+ {
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("en-US"));
+ availableLocales.add(Locale.forLanguageTag("en-GB"));
+ availableLocales.add(Locale.forLanguageTag("en-IN"));
+
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("en-US");
+
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(0), dest.get(0)); // "en-US"
+ }
}
@SmallTest
@@ -191,4 +222,165 @@
assertEquals(availableLocales.get(1), dest.get(0)); // "en-CA"
}
}
+
+ @SmallTest
+ public void testFilterByLanguageFallbackRules() throws Exception {
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-BA"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-CS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-ME"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-BA"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-CS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-ME"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(7), dest.get(0)); // "sr-Latn-RS"
+ }
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS-x-android");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-BA"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-CS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-ME"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-BA"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-CS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-ME"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(7), dest.get(0)); // "sr-Latn-RS"
+ }
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-BA-x-android"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-CS-x-android"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-ME-x-android"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS-x-android"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-BA-x-android"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-CS-x-android"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-ME-x-android"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-RS-x-android"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(7), dest.get(0)); // "sr-Latn-RS-x-android"
+ }
+
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(2), dest.get(0)); // "sr-Latn"
+ }
+
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-RS");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr"));
+ availableLocales.add(Locale.forLanguageTag("sr-RS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(0), dest.get(0)); // "sr"
+ }
+
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr"));
+ availableLocales.add(Locale.forLanguageTag("sr-RS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(2), dest.get(0)); // "sr-Latn"
+ }
+
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr"));
+ availableLocales.add(Locale.forLanguageTag("sr-RS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(0), dest.get(0)); // "sr"
+ }
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+ availableLocales.add(Locale.forLanguageTag("sr-RS"));
+ availableLocales.add(Locale.forLanguageTag("sr"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(1), dest.get(0)); // "sr-RS"
+ }
+
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(0), dest.get(0)); // "sr-Cyrl-RS"
+ }
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
+ availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ assertEquals(availableLocales.get(0), dest.get(0)); // "sr-Latn-RS"
+ }
+ }
+
+ public void testFilterKnownLimitation() throws Exception {
+ // Following test cases are not for intentional behavior but checks for preventing the
+ // behavior from becoming worse.
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("ja-Hrkt");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("ja-Jpan"));
+ availableLocales.add(Locale.forLanguageTag("ja-Hrkt"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ // Should be ja-Jpan since it supports ja-Hrkt and listed before ja-Hrkt.
+ assertEquals(availableLocales.get(1), dest.get(0));
+ }
+ {
+ final LocaleList preferredLocales = LocaleList.forLanguageTags("zh-Hani");
+ final ArrayList<Locale> availableLocales = new ArrayList<>();
+ availableLocales.add(Locale.forLanguageTag("zh-Hans"));
+ availableLocales.add(Locale.forLanguageTag("zh-Hant"));
+ availableLocales.add(Locale.forLanguageTag("zh-Hanb"));
+ availableLocales.add(Locale.forLanguageTag("zh-Hani"));
+ final ArrayList<Locale> dest = new ArrayList<>();
+ LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+ assertEquals(1, dest.size());
+ // Should be zh-Hans since it supports zh-Hani. Also zh-Hant, zh-Hanb supports zh-Hani.
+ assertEquals(availableLocales.get(3), dest.get(0));
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
index 12a75b8..327f3fd 100644
--- a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -16,7 +16,7 @@
package com.android.internal.net;
-import static android.net.NetworkStats.ROAMING_DEFAULT;
+import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
@@ -157,7 +157,7 @@
private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
int tag, long rxBytes, long txBytes) {
- final int i = stats.findIndex(iface, uid, set, tag, ROAMING_DEFAULT);
+ final int i = stats.findIndex(iface, uid, set, tag, ROAMING_NO);
final NetworkStats.Entry entry = stats.getValues(i, null);
assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
assertEquals("unexpected txBytes", txBytes, entry.txBytes);
@@ -165,7 +165,7 @@
private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set,
int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) {
- final int i = stats.findIndex(iface, uid, set, tag, ROAMING_DEFAULT);
+ final int i = stats.findIndex(iface, uid, set, tag, ROAMING_NO);
final NetworkStats.Entry entry = stats.getValues(i, null);
assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
new file mode 100644
index 0000000..b3897ce
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+public class ArrayUtilsTest extends TestCase {
+
+ @SmallTest
+ public void testUnstableRemoveIf() throws Exception {
+ java.util.function.Predicate<Object> isNull = new java.util.function.Predicate<Object>() {
+ @Override
+ public boolean test(Object o) {
+ return o == null;
+ }
+ };
+
+ final Object a = new Object();
+ final Object b = new Object();
+ final Object c = new Object();
+
+ ArrayList<Object> collection = null;
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+
+ collection = new ArrayList<>();
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+
+ collection = new ArrayList<>(Collections.singletonList(a));
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Collections.singletonList(null));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(0, collection.size());
+
+ collection = new ArrayList<>(Arrays.asList(a, b));
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(a, null));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, a));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, null));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(0, collection.size());
+
+ collection = new ArrayList<>(Arrays.asList(a, b, c));
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(3, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+ assertTrue(collection.contains(c));
+
+ collection = new ArrayList<>(Arrays.asList(a, b, null));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(a, null, b));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(null, a, b));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(a, null, null));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, null, a));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, a, null));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, null, null));
+ assertEquals(3, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(0, collection.size());
+ }
+}
diff --git a/core/tests/systemproperties/Android.mk b/core/tests/systemproperties/Android.mk
index 0c20876..b512396 100644
--- a/core/tests/systemproperties/Android.mk
+++ b/core/tests/systemproperties/Android.mk
@@ -9,7 +9,7 @@
$(call all-java-files-under, src)
LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib
+LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCoreSystemPropertiesTests
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index de741b3..dc85046 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -95,3 +95,14 @@
build-one-font-module :=
font_src_files :=
+
+
+# Run sanity tests on fonts on checkbuild
+checkbuild: fontchain_lint
+
+FONTCHAIN_LINTER := frameworks/base/tools/fonts/fontchain_lint.py
+
+.PHONY: fontchain_lint
+fontchain_lint: $(FONTCHAIN_LINTER) $(TARGET_OUT)/etc/fonts.xml
+ PYTHONPATH=$$PYTHONPATH:external/fonttools/Lib \
+ python $(FONTCHAIN_LINTER) $(TARGET_OUT)
\ No newline at end of file
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index dc302c7..bcac6a1 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -88,242 +88,246 @@
</family>
<!-- fallback fonts -->
- <family variant="elegant">
+ <family lang="und-Arab" variant="elegant">
<font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
<font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Arab" variant="compact">
<font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Ethi">
<font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
<font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Hebr">
<font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
<font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Thai" variant="elegant">
<font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
<font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Thai" variant="compact">
<font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Armn">
<font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
</family>
- <family>
+ <!-- TODO: add Geok -->
+ <family lang="und-Geor">
<font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Deva" variant="elegant">
<font weight="400" style="normal">NotoSansDevanagari-Regular.ttf</font>
<font weight="700" style="normal">NotoSansDevanagari-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Deva" variant="compact">
<font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansDevanagariUI-Bold.ttf</font>
</family>
- <!-- Gujarati should come after Devanagari -->
- <family variant="elegant">
+
+ <!-- All scripts of India should come after Devanagari, due to shared
+ danda characters.
+ -->
+ <family lang="und-Gujr" variant="elegant">
<font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Gujr" variant="compact">
<font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
</family>
- <!-- Gurmukhi should come after Devanagari -->
- <family variant="elegant">
+ <family lang="und-Guru" variant="elegant">
<font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGurmukhi-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Guru" variant="compact">
<font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansGurmukhiUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Taml" variant="elegant">
<font weight="400" style="normal">NotoSansTamil-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTamil-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Taml" variant="compact">
<font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTamilUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Mlym" variant="elegant">
<font weight="400" style="normal">NotoSansMalayalam-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMalayalam-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Mlym" variant="compact">
<font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMalayalamUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Beng" variant="elegant">
<font weight="400" style="normal">NotoSansBengali-Regular.ttf</font>
<font weight="700" style="normal">NotoSansBengali-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Beng" variant="compact">
<font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansBengaliUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Telu" variant="elegant">
<font weight="400" style="normal">NotoSansTelugu-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTelugu-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Telu" variant="compact">
<font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansTeluguUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Knda" variant="elegant">
<font weight="400" style="normal">NotoSansKannada-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKannada-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Knda" variant="compact">
<font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKannadaUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Orya" variant="elegant">
<font weight="400" style="normal">NotoSansOriya-Regular.ttf</font>
<font weight="700" style="normal">NotoSansOriya-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Orya" variant="compact">
<font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
</family>
- <family>
+
+ <family lang="und-Sinh">
<font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
<font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Khmr" variant="elegant">
<font weight="400" style="normal">NotoSansKhmer-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKhmer-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Khmr" variant="compact">
<font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Laoo" variant="elegant">
<font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
<font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Laoo" variant="compact">
<font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
</family>
- <family variant="elegant">
+ <family lang="und-Mymr" variant="elegant">
<font weight="400" style="normal">NotoSansMyanmar-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMyanmar-Bold.ttf</font>
</family>
- <family variant="compact">
+ <family lang="und-Mymr" variant="compact">
<font weight="400" style="normal">NotoSansMyanmarUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMyanmarUI-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Thaa">
<font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
<font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Cham">
<font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
<font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
</family>
- <family>
+ <family lang="und-Bali">
<font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Bamu">
<font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Batk">
<font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Bugi">
<font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Buhd">
<font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Cans">
<font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Cher">
<font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Copt">
<font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Glag">
<font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Hano">
<font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Java">
<font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Kali">
<font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Lepc">
<font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Limb">
<font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Lisu">
<font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Mand">
<font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Mtei">
<font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Talu">
<font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Nkoo">
<font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Olck">
<font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Rjng">
<font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Saur">
<font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Sund">
<font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Sylo">
<font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Syre">
<font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Tagb">
<font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Lana">
<font weight="400" style="normal">NotoSansTaiTham-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Tavt">
<font weight="400" style="normal">NotoSansTaiViet-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Tibt">
<font weight="400" style="normal">NotoSansTibetan-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Tfng">
<font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Vaii">
<font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Yiii">
<font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
</family>
<family>
@@ -332,6 +336,7 @@
<family lang="zh-Hans">
<font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
</family>
+ <!-- TODO: Add Bopo -->
<family lang="zh-Hant">
<font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
</family>
@@ -351,10 +356,10 @@
Tai Le and Mongolian are intentionally kept last, to make sure they don't override
the East Asian punctuation for Chinese.
-->
- <family>
+ <family lang="und-Tale">
<font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
</family>
- <family>
+ <family lang="und-Mong">
<font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
</family>
</familyset>
diff --git a/docs/docs-documentation-redirect.html b/docs/docs-documentation-redirect.html
index 98a265e..dbdf8b4 100644
--- a/docs/docs-documentation-redirect.html
+++ b/docs/docs-documentation-redirect.html
@@ -1,9 +1,9 @@
<html>
<head>
-<meta http-equiv="refresh" content="0;url=documentation.html">
+<meta http-equiv="refresh" content="0;url=reference/packages.html">
</head>
<body>
-<a href="documentation.html">click here if you are not redirected</a>
+<a href="reference/packages.html">click here if you are not redirected</a>
</body>
</html>
diff --git a/docs/docs-preview-index.html b/docs/docs-preview-index.html
new file mode 100644
index 0000000..e26b57c
--- /dev/null
+++ b/docs/docs-preview-index.html
@@ -0,0 +1,103 @@
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
+<meta content="IE=edge" http-equiv="X-UA-Compatible">
+<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
+
+<title>Android N Developer Preview</title>
+
+<!-- STYLESHEETS -->
+<link rel="stylesheet"
+href="http://fonts.googleapis.com/css?family=Roboto+Condensed">
+<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Roboto:light,regular,medium,thin,italic,mediumitalic,bold"
+ title="roboto">
+<link href="assets/css/default.css?v=17" rel="stylesheet" type="text/css">
+<!-- JAVASCRIPT -->
+<script src="https://www.google.com/jsapi" type="text/javascript"></script>
+<script src="assets/js/android_3p-bundle.js" type="text/javascript"></script>
+<script type="text/javascript">
+ var toRoot = "../";
+ var metaTags = ["develop, getstarted, sdk, appquality, landing"];
+ var devsite = false;
+</script>
+<script src="assets/js/docs.js?v=3" type="text/javascript"></script>
+</head>
+
+<body>
+<div id="header-wrapper">
+ <div class="dac-header" id="header">
+ <div class="dac-header-inner">
+ <a class="dac-nav-toggle" data-dac-toggle-nav="" href="javascript:;"
+ title="Open navigation">
+ <span class="dac-nav-hamburger">
+ <span class="dac-nav-hamburger-top"></span>
+ <span class="dac-nav-hamburger-mid"></span>
+ <span class="dac-nav-hamburger-bot"></span>
+ </span>
+ </a>
+ <a class="dac-header-logo" href="index.html">
+ <img class="dac-header-logo-image" src="assets/images/android_logo.png"
+ srcset="assets/images/android_logo@2x.png 2x" width="32" height="36"
+ alt="Android"> Developers
+ </a>
+ </div>
+ </div>
+</div>
+<nav class="dac-nav">
+ <div class="dac-nav-dimmer" data-dac-toggle-nav=""></div>
+ <ul class="dac-nav-list" data-dac-nav="">
+ <li class="dac-nav-item dac-nav-head">
+ <a class="dac-nav-link dac-nav-logo" data-dac-toggle-nav=""
+ href="javascript:;" title="Close navigation">
+ <img class="dac-logo-image" src="assets/images/android_logo.png"
+ srcset="assets/images/android_logo@2x.png 2x" width="32" height="36"
+ alt="Android"> Developers
+ </a>
+ </li>
+ <li class="dac-nav-item develop">
+ <a class="dac-nav-link" href="reference/packages.html"
+ >API Reference</a>
+ </li>
+ </ul>
+</nav>
+
+<section class="dac-expand" style="padding-top:40px;background-color:#eee">
+ <div class="wrap" style="max-width:1100px;margin-top:0;height:100%">
+ <div class="cols dac-hero-content" style="padding-bottom:1em;">
+ <div class="col-11of16">
+
+
+<h1>Android N Developer Preview</h1>
+<p>
+ Get ready for Android N!
+ <strong>Test your apps</strong> on Nexus devices. Support new system
+ behaviors to <strong>save power and memory</strong>.
+ Extend your apps with <strong>multi-window UI</strong>,
+ <strong>direct reply notifications</strong> and more.
+</p>
+
+<h2>Get Started</h2>
+<ul>
+ <li>View the <a href="reference/packages.html">API Reference</a></li>
+ <li>Read Diff Reports:</a>
+ <ul>
+ <li><a href="sdk/api_diff/n-preview-1/changes.html"
+ >API 23 --> Preview 1</a></li>
+ </ul>
+ </li>
+ <li>Downloads and additional documentation are available at the
+ <a href="http://developer.android.com/preview/index.html">
+ Android N Developer Preview site</a></li>
+ <li>For information about Developer Preview 1, visit the
+ <a href="http://developer.android.com/preview/support.html">Support</a>
+ page.</li>
+</ul>
+
+
+ </div>
+ </div>
+ </div>
+</section>
+</body>
+</html>
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 99fa9fe..3e86e6f 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -32,13 +32,15 @@
* @see Drawable#getOutline(Outline)
*/
public final class Outline {
+ private static final float RADIUS_UNDEFINED = Float.NEGATIVE_INFINITY;
+
/** @hide */
public Path mPath;
/** @hide */
public Rect mRect;
/** @hide */
- public float mRadius;
+ public float mRadius = RADIUS_UNDEFINED;
/** @hide */
public float mAlpha;
@@ -63,7 +65,7 @@
public void setEmpty() {
mPath = null;
mRect = null;
- mRadius = 0;
+ mRadius = RADIUS_UNDEFINED;
}
/**
@@ -194,11 +196,11 @@
}
/**
- * Returns the rounded rect radius, if set, or {@code -1} if a path has
+ * Returns the rounded rect radius, if set, or a value less than 0 if a path has
* been set via {@link #setConvexPath(Path)}. A return value of {@code 0}
* indicates a non-rounded rect.
*
- * @return the rounded rect radius or {@code -1}
+ * @return the rounded rect radius, or value < 0
*/
public float getRadius() {
return mRadius;
@@ -223,6 +225,7 @@
mPath.reset();
mPath.addOval(left, top, right, bottom, Path.Direction.CW);
mRect = null;
+ mRadius = RADIUS_UNDEFINED;
}
/**
@@ -249,7 +252,7 @@
mPath.set(convexPath);
mRect = null;
- mRadius = -1.0f;
+ mRadius = RADIUS_UNDEFINED;
}
/**
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index ca214ab..ae9ebc7 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.Application;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -35,6 +36,7 @@
import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.Outline;
+import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.os.Build;
@@ -155,7 +157,7 @@
private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
/** Local, mutable animator set. */
- private VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimatorRT(this);
+ private VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimatorUI(this);
/**
* The resources against which this drawable was created. Used to attempt
@@ -229,7 +231,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations() | mAnimatedVectorState.getChangingConfigurations();
}
@@ -259,6 +261,13 @@
return mAnimatedVectorState.mVectorDrawable.setLayoutDirection(layoutDirection);
}
+ /**
+ * AnimatedVectorDrawable is running on render thread now. Therefore, if the root alpha is being
+ * animated, then the root alpha value we get from this call could be out of sync with alpha
+ * value used in the render thread. Otherwise, the root alpha should be always the same value.
+ *
+ * @return the containing vector drawable's root alpha value.
+ */
@Override
public int getAlpha() {
return mAnimatedVectorState.mVectorDrawable.getAlpha();
@@ -275,6 +284,11 @@
}
@Override
+ public ColorFilter getColorFilter() {
+ return mAnimatedVectorState.mVectorDrawable.getColorFilter();
+ }
+
+ @Override
public void setTintList(ColorStateList tint) {
mAnimatedVectorState.mVectorDrawable.setTintList(tint);
}
@@ -307,7 +321,7 @@
@Override
public int getOpacity() {
- return mAnimatedVectorState.mVectorDrawable.getOpacity();
+ return PixelFormat.TRANSLUCENT;
}
@Override
@@ -436,7 +450,7 @@
}
private static class AnimatedVectorDrawableState extends ConstantState {
- int mChangingConfigurations;
+ @Config int mChangingConfigurations;
VectorDrawable mVectorDrawable;
/** Animators that require a theme before inflation. */
@@ -500,7 +514,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations;
}
@@ -702,17 +716,17 @@
private final Callback mCallback = new Callback() {
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
invalidateSelf();
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
scheduleSelf(what, when);
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
unscheduleSelf(what);
}
};
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index bffbc75..9d8ede0 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -17,6 +17,7 @@
package android.graphics.drawable;
import android.annotation.NonNull;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -454,7 +455,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations() | mBitmapState.getChangingConfigurations();
}
@@ -910,7 +911,7 @@
int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
boolean mAutoMirrored = false;
- int mChangingConfigurations;
+ @Config int mChangingConfigurations;
boolean mRebuildShader;
BitmapState(Bitmap bitmap) {
@@ -958,7 +959,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations
| (mTint != null ? mTint.getChangingConfigurations() : 0);
}
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index 5ad31f7..7524cac 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -18,6 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
+import android.content.pm.ActivityInfo.Config;
import android.graphics.*;
import android.graphics.PorterDuff.Mode;
import android.content.res.ColorStateList;
@@ -70,7 +71,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations() | mColorState.getChangingConfigurations();
}
@@ -292,7 +293,7 @@
int mBaseColor; // base color, independent of setAlpha()
@ViewDebug.ExportedProperty
int mUseColor; // basecolor modulated by setAlpha()
- int mChangingConfigurations;
+ @Config int mChangingConfigurations;
ColorStateList mTint = null;
Mode mTintMode = DEFAULT_TINT_MODE;
@@ -326,7 +327,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations
| (mTint != null ? mTint.getChangingConfigurations() : 0);
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 3d8437d..3915984 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -19,6 +19,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -134,7 +135,7 @@
private int[] mStateSet = StateSet.WILD_CARD;
private int mLevel = 0;
- private int mChangingConfigurations = 0;
+ private @Config int mChangingConfigurations = 0;
private Rect mBounds = ZERO_BOUNDS_RECT; // lazily becomes a new Rect()
private WeakReference<Callback> mCallback = null;
private boolean mVisible = true;
@@ -249,7 +250,7 @@
*
* @see android.content.pm.ActivityInfo
*/
- public void setChangingConfigurations(int configs) {
+ public void setChangingConfigurations(@Config int configs) {
mChangingConfigurations = configs;
}
@@ -266,7 +267,7 @@
*
* @see android.content.pm.ActivityInfo
*/
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations;
}
@@ -308,7 +309,7 @@
* to supply your implementation of the interface to the drawable; it uses
* this interface to schedule and execute animation changes.
*/
- public static interface Callback {
+ public interface Callback {
/**
* Called when the drawable needs to be redrawn. A view at this point
* should invalidate itself (or at least the part of itself where the
@@ -316,7 +317,7 @@
*
* @param who The drawable that is requesting the update.
*/
- public void invalidateDrawable(Drawable who);
+ void invalidateDrawable(@NonNull Drawable who);
/**
* A Drawable can call this to schedule the next frame of its
@@ -330,7 +331,7 @@
* @param when The time (in milliseconds) to run. The timebase is
* {@link android.os.SystemClock#uptimeMillis}
*/
- public void scheduleDrawable(Drawable who, Runnable what, long when);
+ void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when);
/**
* A Drawable can call this to unschedule an action previously
@@ -342,7 +343,7 @@
* @param who The drawable being unscheduled.
* @param what The action being unscheduled.
*/
- public void unscheduleDrawable(Drawable who, Runnable what);
+ void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what);
}
/**
@@ -1294,7 +1295,7 @@
* Return a bit mask of configuration changes that will impact
* this drawable (and thus require completely reloading it).
*/
- public abstract int getChangingConfigurations();
+ public abstract @Config int getChangingConfigurations();
/**
* @return Total pixel count
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 3b0e7e8..42f4863 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -17,6 +17,7 @@
package android.graphics.drawable;
import android.annotation.NonNull;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -87,7 +88,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations()
| mDrawableContainerState.getChangingConfigurations();
}
@@ -373,21 +374,21 @@
}
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().invalidateDrawable(this);
}
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().scheduleDrawable(this, what, when);
}
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
if (who == mCurrDrawable && getCallback() != null) {
getCallback().unscheduleDrawable(this, what);
}
@@ -649,8 +650,8 @@
Resources mSourceRes;
int mDensity = DisplayMetrics.DENSITY_DEFAULT;
- int mChangingConfigurations;
- int mChildrenChangingConfigurations;
+ @Config int mChangingConfigurations;
+ @Config int mChildrenChangingConfigurations;
SparseArray<ConstantState> mDrawableFutures;
Drawable[] mDrawables;
@@ -781,7 +782,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations | mChildrenChangingConfigurations;
}
@@ -804,6 +805,7 @@
mConstantPadding = null;
mCheckedPadding = false;
mCheckedConstantSize = false;
+ mCheckedConstantState = false;
return pos;
}
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index c427870..5abfc54 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -198,7 +199,7 @@
}
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
@@ -206,7 +207,7 @@
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
@@ -214,7 +215,7 @@
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
@@ -229,7 +230,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations()
| (mState != null ? mState.getChangingConfigurations() : 0)
| mDrawable.getChangingConfigurations();
@@ -444,7 +445,7 @@
abstract static class DrawableWrapperState extends Drawable.ConstantState {
private int[] mThemeAttrs;
- int mChangingConfigurations;
+ @Config int mChangingConfigurations;
int mDensity = DisplayMetrics.DENSITY_DEFAULT;
Drawable.ConstantState mDrawableState;
@@ -524,7 +525,7 @@
public abstract Drawable newDrawable(@Nullable Resources res);
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations
| (mDrawableState != null ? mDrawableState.getChangingConfigurations() : 0);
}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index f9208cd..bcc354c 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -17,8 +17,10 @@
package android.graphics.drawable;
import android.annotation.ColorInt;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -51,6 +53,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* A Drawable with a color gradient for buttons, backgrounds, etc.
@@ -108,6 +112,11 @@
*/
public static final int RING = 3;
+ /** @hide */
+ @IntDef({RECTANGLE, OVAL, LINE, RING})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Shape {}
+
/**
* Gradient is linear (default.)
*/
@@ -123,6 +132,11 @@
*/
public static final int SWEEP_GRADIENT = 2;
+ /** @hide */
+ @IntDef({LINEAR_GRADIENT, RADIAL_GRADIENT, SWEEP_GRADIENT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GradientType {}
+
/** Radius is in pixels. */
private static final int RADIUS_TYPE_PIXELS = 0;
@@ -132,6 +146,11 @@
/** Radius is a fraction of the bounds size. */
private static final int RADIUS_TYPE_FRACTION_PARENT = 2;
+ /** @hide */
+ @IntDef({RADIUS_TYPE_PIXELS, RADIUS_TYPE_FRACTION, RADIUS_TYPE_FRACTION_PARENT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RadiusType {}
+
private static final float DEFAULT_INNER_RADIUS_RATIO = 3.0f;
private static final float DEFAULT_THICKNESS_RATIO = 9.0f;
@@ -404,7 +423,7 @@
*
* @see #mutate()
*/
- public void setShape(int shape) {
+ public void setShape(@Shape int shape) {
mRingPath = null;
mPathIsDirty = true;
mGradientState.setShape(shape);
@@ -412,6 +431,18 @@
}
/**
+ * Returns the type of shape used by this drawable, one of {@link #LINE},
+ * {@link #OVAL}, {@link #RECTANGLE} or {@link #RING}.
+ *
+ * @return the type of shape used by this drawable
+ * @see #setShape(int)
+ */
+ @Shape
+ public int getShape() {
+ return mGradientState.mShape;
+ }
+
+ /**
* Sets the type of gradient used by this drawable.
* <p>
* <strong>Note</strong>: changing this property will affect all instances
@@ -424,7 +455,7 @@
* @see #mutate()
* @see #getGradientType()
*/
- public void setGradientType(int gradient) {
+ public void setGradientType(@GradientType int gradient) {
mGradientState.setGradientType(gradient);
mGradientIsDirty = true;
invalidateSelf();
@@ -438,6 +469,7 @@
* @return the type of gradient used by this drawable
* @see #setGradientType(int)
*/
+ @GradientType
public int getGradientType() {
return mGradientState.mGradient;
}
@@ -534,7 +566,7 @@
* @see #mutate()
* @see #setLevel(int)
* @see #getLevel()
- * @see #isUseLevel()
+ * @see #getUseLevel()
*/
public void setUseLevel(boolean useLevel) {
mGradientState.mUseLevel = useLevel;
@@ -550,7 +582,7 @@
* {@code false} otherwise
* @see #setUseLevel(boolean)
*/
- public boolean isUseLevel() {
+ public boolean getUseLevel() {
return mGradientState.mUseLevel;
}
@@ -616,7 +648,8 @@
*/
@Nullable
public int[] getColors() {
- return mGradientState.mGradientColors.clone();
+ return mGradientState.mGradientColors == null ?
+ null : mGradientState.mGradientColors.clone();
}
@Override
@@ -848,7 +881,7 @@
* @see #mutate()
* @see #getColor
*/
- public void setColor(ColorStateList colorStateList) {
+ public void setColor(@Nullable ColorStateList colorStateList) {
mGradientState.setSolidColors(colorStateList);
final int color;
if (colorStateList == null) {
@@ -870,6 +903,7 @@
* @see #setColor(int)
* @see #setColor(ColorStateList)
*/
+ @Nullable
public ColorStateList getColor() {
return mGradientState.mSolidColors;
}
@@ -925,7 +959,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations() | mGradientState.getChangingConfigurations();
}
@@ -951,12 +985,13 @@
}
@Override
+ @Nullable
public ColorFilter getColorFilter() {
return mColorFilter;
}
@Override
- public void setColorFilter(ColorFilter colorFilter) {
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
if (colorFilter != mColorFilter) {
mColorFilter = colorFilter;
invalidateSelf();
@@ -964,14 +999,14 @@
}
@Override
- public void setTintList(ColorStateList tint) {
+ public void setTintList(@Nullable ColorStateList tint) {
mGradientState.mTint = tint;
mTintFilter = updateTintFilter(mTintFilter, tint, mGradientState.mTintMode);
invalidateSelf();
}
@Override
- public void setTintMode(PorterDuff.Mode tintMode) {
+ public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
mGradientState.mTintMode = tintMode;
mTintFilter = updateTintFilter(mTintFilter, mGradientState.mTint, tintMode);
invalidateSelf();
@@ -1543,7 +1578,7 @@
final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
final float radius;
- final int radiusType;
+ final @RadiusType int radiusType;
if (tv.type == TypedValue.TYPE_FRACTION) {
radius = tv.getFraction(1.0f, 1.0f);
@@ -1624,7 +1659,9 @@
return false;
}
- if (!isOpaque(mFillPaint.getColor())) {
+ // Don't check opacity if we're using a gradient, as we've already
+ // checked the gradient opacity in mOpaqueOverShape.
+ if (mGradientState.mGradientColors == null && !isOpaque(mFillPaint.getColor())) {
return false;
}
@@ -1698,15 +1735,15 @@
}
final static class GradientState extends ConstantState {
- public int mChangingConfigurations;
- public int mShape = RECTANGLE;
- public int mGradient = LINEAR_GRADIENT;
+ public @Config int mChangingConfigurations;
+ public @Shape int mShape = RECTANGLE;
+ public @GradientType int mGradient = LINEAR_GRADIENT;
public int mAngle = 0;
public Orientation mOrientation;
public ColorStateList mSolidColors;
public ColorStateList mStrokeColors;
- public int[] mGradientColors;
- public int[] mTempColors; // no need to copy
+ public @ColorInt int[] mGradientColors;
+ public @ColorInt int[] mTempColors; // no need to copy
public float[] mTempPositions; // no need to copy
public float[] mPositions;
public int mStrokeWidth = -1; // if >= 0 use stroking.
@@ -1727,7 +1764,7 @@
float mCenterX = 0.5f;
float mCenterY = 0.5f;
float mGradientRadius = 0.5f;
- int mGradientRadiusType = RADIUS_TYPE_PIXELS;
+ @RadiusType int mGradientRadiusType = RADIUS_TYPE_PIXELS;
boolean mUseLevel = false;
boolean mUseLevelForShape = true;
@@ -1926,19 +1963,19 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations
| (mStrokeColors != null ? mStrokeColors.getChangingConfigurations() : 0)
| (mSolidColors != null ? mSolidColors.getChangingConfigurations() : 0)
| (mTint != null ? mTint.getChangingConfigurations() : 0);
}
- public void setShape(int shape) {
+ public void setShape(@Shape int shape) {
mShape = shape;
computeOpacity();
}
- public void setGradientType(int gradient) {
+ public void setGradientType(@GradientType int gradient) {
mGradient = gradient;
}
@@ -1947,13 +1984,13 @@
mCenterY = y;
}
- public void setGradientColors(int[] colors) {
+ public void setGradientColors(@Nullable int[] colors) {
mGradientColors = colors;
mSolidColors = null;
computeOpacity();
}
- public void setSolidColors(ColorStateList colors) {
+ public void setSolidColors(@Nullable ColorStateList colors) {
mGradientColors = null;
mSolidColors = colors;
computeOpacity();
@@ -1984,7 +2021,8 @@
&& mRadiusArray == null;
}
- public void setStroke(int width, ColorStateList colors, float dashWidth, float dashGap) {
+ public void setStroke(int width, @Nullable ColorStateList colors, float dashWidth,
+ float dashGap) {
mStrokeWidth = width;
mStrokeColors = colors;
mStrokeDashWidth = dashWidth;
@@ -2012,7 +2050,7 @@
mHeight = height;
}
- public void setGradientRadius(float gradientRadius, int type) {
+ public void setGradientRadius(float gradientRadius, @RadiusType int type) {
mGradientRadius = gradientRadius;
mGradientRadiusType = type;
}
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 0de4c2c..51221b4 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -627,6 +627,11 @@
return this;
}
+ /** @hide */
+ public boolean hasTint() {
+ return (mTintList != null) || (mTintMode != DEFAULT_TINT_MODE);
+ }
+
/**
* Create an Icon pointing to an image file specified by path.
*
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index e2150c0..d9c3a02 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -88,7 +89,7 @@
* @see #getLayerInsetStart(int)
* @see #getLayerInsetEnd(int)
*/
- public static final int UNDEFINED_INSET = Integer.MIN_VALUE;
+ public static final int INSET_UNDEFINED = Integer.MIN_VALUE;
LayerState mLayerState;
@@ -758,7 +759,7 @@
* @attr ref android.R.styleable#LayerDrawableItem_bottom
*/
public void setLayerInset(int index, int l, int t, int r, int b) {
- setLayerInsetInternal(index, l, t, r, b, UNDEFINED_INSET, UNDEFINED_INSET);
+ setLayerInsetInternal(index, l, t, r, b, INSET_UNDEFINED, INSET_UNDEFINED);
}
/**
@@ -873,7 +874,7 @@
/**
* @param index the index of the layer
* @return the number of pixels to inset from the start bound, or
- * {@link #UNDEFINED_INSET} if not specified
+ * {@link #INSET_UNDEFINED} if not specified
* @attr ref android.R.styleable#LayerDrawableItem_start
*/
public int getLayerInsetStart(int index) {
@@ -884,7 +885,7 @@
/**
* @param index the index of the layer to adjust
* @param e number of pixels to inset from the end bound, or
- * {@link #UNDEFINED_INSET} if not specified
+ * {@link #INSET_UNDEFINED} if not specified
* @attr ref android.R.styleable#LayerDrawableItem_end
*/
public void setLayerInsetEnd(int index, int e) {
@@ -944,17 +945,17 @@
}
@Override
- public void invalidateDrawable(Drawable who) {
+ public void invalidateDrawable(@NonNull Drawable who) {
invalidateSelf();
}
@Override
- public void scheduleDrawable(Drawable who, Runnable what, long when) {
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
scheduleSelf(what, when);
}
@Override
- public void unscheduleDrawable(Drawable who, Runnable what) {
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
unscheduleSelf(what);
}
@@ -971,7 +972,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations() | mLayerState.getChangingConfigurations();
}
@@ -1502,8 +1503,8 @@
// insets.
final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS;
final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE;
- final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL;
- final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR;
+ final int insetL = insetRtlL == INSET_UNDEFINED ? r.mInsetL : insetRtlL;
+ final int insetR = insetRtlR == INSET_UNDEFINED ? r.mInsetR : insetRtlR;
// Establish containing region based on aggregate padding and
// requested insets for the current layer.
@@ -1601,8 +1602,8 @@
// left / right ones.
final int insetRtlL = isLayoutRtl ? r.mInsetE : r.mInsetS;
final int insetRtlR = isLayoutRtl ? r.mInsetS : r.mInsetE;
- final int insetL = insetRtlL == UNDEFINED_INSET ? r.mInsetL : insetRtlL;
- final int insetR = insetRtlR == UNDEFINED_INSET ? r.mInsetR : insetRtlR;
+ final int insetL = insetRtlL == INSET_UNDEFINED ? r.mInsetL : insetRtlL;
+ final int insetR = insetRtlR == INSET_UNDEFINED ? r.mInsetR : insetRtlR;
// Don't apply padding and insets for children that don't have
// an intrinsic dimension.
@@ -1762,8 +1763,8 @@
public int[] mThemeAttrs;
public int mDensity = DisplayMetrics.DENSITY_DEFAULT;
public int mInsetL, mInsetT, mInsetR, mInsetB;
- public int mInsetS = UNDEFINED_INSET;
- public int mInsetE = UNDEFINED_INSET;
+ public int mInsetS = INSET_UNDEFINED;
+ public int mInsetE = INSET_UNDEFINED;
public int mWidth = -1;
public int mHeight = -1;
public int mGravity = Gravity.NO_GRAVITY;
@@ -1832,10 +1833,10 @@
mInsetT = Drawable.scaleFromDensity(mInsetT, sourceDensity, targetDensity, false);
mInsetR = Drawable.scaleFromDensity(mInsetR, sourceDensity, targetDensity, false);
mInsetB = Drawable.scaleFromDensity(mInsetB, sourceDensity, targetDensity, false);
- if (mInsetS != UNDEFINED_INSET) {
+ if (mInsetS != INSET_UNDEFINED) {
mInsetS = Drawable.scaleFromDensity(mInsetS, sourceDensity, targetDensity, false);
}
- if (mInsetE != UNDEFINED_INSET) {
+ if (mInsetE != INSET_UNDEFINED) {
mInsetE = Drawable.scaleFromDensity(mInsetE, sourceDensity, targetDensity, false);
}
if (mWidth > 0) {
@@ -1864,8 +1865,8 @@
int mPaddingEnd = -1;
int mOpacityOverride = PixelFormat.UNKNOWN;
- int mChangingConfigurations;
- int mChildrenChangingConfigurations;
+ @Config int mChangingConfigurations;
+ @Config int mChildrenChangingConfigurations;
private boolean mHaveOpacity;
private int mOpacity;
@@ -1989,7 +1990,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations
| mChildrenChangingConfigurations;
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 6816539..fd3b9b4 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -260,7 +261,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations() | mNinePatchState.getChangingConfigurations();
}
@@ -575,7 +576,7 @@
}
final static class NinePatchState extends ConstantState {
- int mChangingConfigurations;
+ @Config int mChangingConfigurations;
// Values loaded during inflation.
NinePatch mNinePatch = null;
@@ -651,7 +652,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations
| (mTint != null ? mTint.getChangingConfigurations() : 0);
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index ee0861a..caf2e7a 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -23,6 +23,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -1033,7 +1034,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations()
| (mColor != null ? mColor.getChangingConfigurations() : 0);
}
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 30b588e..fe82a93 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -16,6 +16,7 @@
package android.graphics.drawable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -260,7 +261,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations() | mShapeState.getChangingConfigurations();
}
@@ -526,7 +527,7 @@
*/
final static class ShapeState extends ConstantState {
int[] mThemeAttrs;
- int mChangingConfigurations;
+ @Config int mChangingConfigurations;
Paint mPaint;
Shape mShape;
ColorStateList mTint = null;
@@ -571,7 +572,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations
| (mTint != null ? mTint.getChangingConfigurations() : 0);
}
diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java
index e5c235e..0122338 100644
--- a/graphics/java/android/graphics/drawable/TransitionDrawable.java
+++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java
@@ -16,6 +16,7 @@
package android.graphics.drawable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.os.SystemClock;
@@ -259,7 +260,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations;
}
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 9e0f1b4..ae98c22 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -16,6 +16,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.ComplexColor;
import android.content.res.GradientColor;
@@ -364,7 +365,9 @@
@Override
public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
+ // We can't tell whether the drawable is fully opaque unless we examine all the pixels,
+ // but we could tell it is transparent if the root alpha is 0.
+ return getAlpha() == 0 ? PixelFormat.TRANSPARENT : PixelFormat.TRANSLUCENT;
}
@Override
@@ -684,7 +687,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
}
@@ -712,7 +715,7 @@
static class VectorDrawableState extends ConstantState {
// Variables below need to be copied (deep copy if applicable) for mutation.
int[] mThemeAttrs;
- int mChangingConfigurations;
+ @Config int mChangingConfigurations;
ColorStateList mTint = null;
Mode mTintMode = DEFAULT_TINT_MODE;
boolean mAutoMirrored;
@@ -821,7 +824,7 @@
}
@Override
- public int getChangingConfigurations() {
+ public @Config int getChangingConfigurations() {
return mChangingConfigurations
| (mTint != null ? mTint.getChangingConfigurations() : 0);
}
@@ -921,7 +924,7 @@
// mLocalMatrix is updated based on the update of transformation information,
// either parsed from the XML or by animation.
- private int mChangingConfigurations;
+ private @Config int mChangingConfigurations;
private int[] mThemeAttrs;
private String mGroupName = null;
@@ -1167,7 +1170,7 @@
protected PathParser.PathData mPathData = null;
String mPathName;
- int mChangingConfigurations;
+ @Config int mChangingConfigurations;
public VPath() {
// Empty constructor.
diff --git a/graphics/java/android/graphics/drawable/shapes/ArcShape.java b/graphics/java/android/graphics/drawable/shapes/ArcShape.java
index 84731b0..c4b239f 100644
--- a/graphics/java/android/graphics/drawable/shapes/ArcShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/ArcShape.java
@@ -17,6 +17,7 @@
package android.graphics.drawable.shapes;
import android.graphics.Canvas;
+import android.graphics.Outline;
import android.graphics.Paint;
/**
@@ -46,5 +47,11 @@
public void draw(Canvas canvas, Paint paint) {
canvas.drawArc(rect(), mStart, mSweep, true, paint);
}
+
+ @Override
+ public void getOutline(Outline outline) {
+ // Since we don't support concave outlines, arc shape does not attempt
+ // to provide an outline.
+ }
}
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 16bea79..05596f0 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -35,6 +35,8 @@
#include <android/configuration.h>
+#include <memory>
+
namespace android {
/**
@@ -1151,9 +1153,13 @@
uint32_t screenConfig2;
};
- // If true, it means that the script of the locale was explicitly provided.
- // If false, it means that the script was automatically computed.
- bool localeScriptWasProvided;
+ // If false and localeScript is set, it means that the script of the locale
+ // was explicitly provided.
+ //
+ // If true, it means that localeScript was automatically computed.
+ // localeScript may still not be set in this case, which means that we
+ // tried but could not compute a script.
+ bool localeScriptWasComputed;
void copyFromDeviceNoSwap(const ResTable_config& o);
@@ -1233,7 +1239,7 @@
inline void clearLocale() {
locale = 0;
- localeScriptWasProvided = false;
+ localeScriptWasComputed = false;
memset(localeScript, 0, sizeof(localeScript));
memset(localeVariant, 0, sizeof(localeVariant));
}
@@ -1872,9 +1878,30 @@
struct Entry;
struct Package;
struct PackageGroup;
- struct bag_set;
typedef Vector<Type*> TypeList;
+ struct bag_set {
+ size_t numAttrs; // number in array
+ size_t availAttrs; // total space in array
+ uint32_t typeSpecFlags;
+ // Followed by 'numAttr' bag_entry structures.
+ };
+
+ /**
+ * Configuration dependent cached data. This must be cleared when the configuration is
+ * changed (setParameters).
+ */
+ struct TypeCacheEntry {
+ TypeCacheEntry() : cachedBags(NULL) {}
+
+ // Computed attribute bags for this type.
+ bag_set** cachedBags;
+
+ // Pre-filtered list of configurations (per asset path) that match the parameters set on this
+ // ResTable.
+ Vector<std::shared_ptr<Vector<const ResTable_type*>>> filteredConfigs;
+ };
+
status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset=false);
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index c73bb584..52fe973 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1870,8 +1870,8 @@
// The language & region are equal, so compare the scripts and variants.
const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
- const char *lScript = l.localeScriptWasProvided ? l.localeScript : emptyScript;
- const char *rScript = r.localeScriptWasProvided ? r.localeScript : emptyScript;
+ const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript;
+ const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript;
int script = memcmp(lScript, rScript, sizeof(l.localeScript));
if (script) {
return script;
@@ -2016,11 +2016,11 @@
// scripts since it seems more useful to do so. We will consider
// "en-US-POSIX" to be more specific than "en-Latn-US".
- const int score = (localeScriptWasProvided ? 1 : 0) +
- ((localeVariant[0] != 0) ? 2 : 0);
+ const int score = ((localeScript[0] != '\0' && !localeScriptWasComputed) ? 1 : 0) +
+ ((localeVariant[0] != '\0') ? 2 : 0);
- const int oScore = (o.localeScriptWasProvided ? 1 : 0) +
- ((o.localeVariant[0] != 0) ? 2 : 0);
+ const int oScore = (o.localeScript[0] != '\0' && !o.localeScriptWasComputed ? 1 : 0) +
+ ((o.localeVariant[0] != '\0') ? 2 : 0);
return score - oScore;
@@ -2535,7 +2535,8 @@
if (settings.localeScript[0] == '\0') { // could not determine the request's script
countriesMustMatch = true;
} else {
- if (localeScript[0] == '\0') { // script was not provided, so we try to compute it
+ if (localeScript[0] == '\0' && !localeScriptWasComputed) {
+ // script was not provided or computed, so we try to compute it
localeDataComputeScript(computed_script, language, country);
if (computed_script[0] == '\0') { // we could not compute the script
countriesMustMatch = true;
@@ -2684,8 +2685,8 @@
if (!language[0]) {
return;
}
-
- if (!localeScriptWasProvided && !localeVariant[0]) {
+ const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed;
+ if (!scriptWasProvided && !localeVariant[0]) {
// Legacy format.
if (out.size() > 0) {
out.append("-");
@@ -2715,7 +2716,7 @@
size_t len = unpackLanguage(buf);
out.append(buf, len);
- if (localeScriptWasProvided) {
+ if (scriptWasProvided) {
out.append("+");
out.append(localeScript, sizeof(localeScript));
}
@@ -2746,7 +2747,7 @@
charsWritten += unpackLanguage(str);
}
- if (localeScriptWasProvided) {
+ if (localeScript[0] && !localeScriptWasComputed) {
if (charsWritten) {
str[charsWritten++] = '-';
}
@@ -2787,7 +2788,6 @@
for (size_t i = 1; i < 4; ++i) {
config->localeScript[i] = tolower(start[i]);
}
- config->localeScriptWasProvided = true;
break;
}
case 5:
@@ -2807,7 +2807,6 @@
void ResTable_config::setBcp47Locale(const char* in) {
locale = 0;
- localeScriptWasProvided = false;
memset(localeScript, 0, sizeof(localeScript));
memset(localeVariant, 0, sizeof(localeVariant));
@@ -2824,9 +2823,10 @@
const size_t size = in + strlen(in) - start;
assignLocaleComponent(this, start, size);
- if (localeScript[0] == '\0') {
+ localeScriptWasComputed = (localeScript[0] == '\0');
+ if (localeScriptWasComputed) {
computeScript();
- };
+ }
}
String8 ResTable_config::toString() const {
@@ -3147,9 +3147,6 @@
StringPoolRef keyStr;
};
-template <typename T>
-using SharedVector = std::shared_ptr<Vector<T>>;
-
struct ResTable::Type
{
Type(const Header* _header, const Package* _package, size_t count)
@@ -3162,10 +3159,6 @@
const uint32_t* typeSpecFlags;
IdmapEntries idmapEntries;
Vector<const ResTable_type*> configs;
-
- // The set of configurations that match the current parameters.
- // This will be swapped with a new set when the parameters change.
- SharedVector<const ResTable_type*> filteredConfigs;
};
struct ResTable::Package
@@ -3201,7 +3194,6 @@
, name(_name)
, id(_id)
, largestTypeId(0)
- , bags(NULL)
, dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
, isSystemAsset(_isSystemAsset)
{ }
@@ -3228,36 +3220,41 @@
}
}
+ /**
+ * Clear all cache related data that depends on parameters/configuration.
+ * This includes the bag caches and filtered types.
+ */
void clearBagCache() {
- if (bags) {
+ for (size_t i = 0; i < typeCacheEntries.size(); i++) {
if (kDebugTableNoisy) {
- printf("bags=%p\n", bags);
+ printf("type=%zu\n", i);
}
- for (size_t i = 0; i < bags->size(); i++) {
+ const TypeList& typeList = types[i];
+ if (!typeList.isEmpty()) {
+ TypeCacheEntry& cacheEntry = typeCacheEntries.editItemAt(i);
+
+ // Reset the filtered configurations.
+ cacheEntry.filteredConfigs.clear();
+
+ bag_set** typeBags = cacheEntry.cachedBags;
if (kDebugTableNoisy) {
- printf("type=%zu\n", i);
+ printf("typeBags=%p\n", typeBags);
}
- const TypeList& typeList = types[i];
- if (!typeList.isEmpty()) {
- bag_set** typeBags = bags->get(i);
+
+ if (typeBags) {
+ const size_t N = typeList[0]->entryCount;
if (kDebugTableNoisy) {
- printf("typeBags=%p\n", typeBags);
+ printf("type->entryCount=%zu\n", N);
}
- if (typeBags) {
- const size_t N = typeList[0]->entryCount;
- if (kDebugTableNoisy) {
- printf("type->entryCount=%zu\n", N);
+ for (size_t j = 0; j < N; j++) {
+ if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) {
+ free(typeBags[j]);
}
- for (size_t j = 0; j < N; j++) {
- if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
- free(typeBags[j]);
- }
- free(typeBags);
}
+ free(typeBags);
+ cacheEntry.cachedBags = NULL;
}
}
- delete bags;
- bags = NULL;
}
}
@@ -3285,9 +3282,11 @@
uint8_t largestTypeId;
- // Computed attribute bags, first indexed by the type and second
- // by the entry in that type.
- ByteBucketArray<bag_set**>* bags;
+ // Cached objects dependent on the parameters/configuration of this ResTable.
+ // Gets cleared whenever the parameters/configuration changes.
+ // These are stored here in a parallel structure because the data in `types` may
+ // be shared by other ResTable's (framework resources are shared this way).
+ ByteBucketArray<TypeCacheEntry> typeCacheEntries;
// The table mapping dynamic references to resolved references for
// this package group.
@@ -3301,14 +3300,6 @@
const bool isSystemAsset;
};
-struct ResTable::bag_set
-{
- size_t numAttrs; // number in array
- size_t availAttrs; // total space in array
- uint32_t typeSpecFlags;
- // Followed by 'numAttr' bag_entry structures.
-};
-
ResTable::Theme::Theme(const ResTable& table)
: mTable(table)
, mTypeSpecFlags(0)
@@ -4200,39 +4191,32 @@
}
// First see if we've already computed this bag...
- if (grp->bags) {
- bag_set** typeSet = grp->bags->get(t);
- if (typeSet) {
- bag_set* set = typeSet[e];
- if (set) {
- if (set != (bag_set*)0xFFFFFFFF) {
- if (outTypeSpecFlags != NULL) {
- *outTypeSpecFlags = set->typeSpecFlags;
- }
- *outBag = (bag_entry*)(set+1);
- if (kDebugTableSuperNoisy) {
- ALOGI("Found existing bag for: 0x%x\n", resID);
- }
- return set->numAttrs;
+ TypeCacheEntry& cacheEntry = grp->typeCacheEntries.editItemAt(t);
+ bag_set** typeSet = cacheEntry.cachedBags;
+ if (typeSet) {
+ bag_set* set = typeSet[e];
+ if (set) {
+ if (set != (bag_set*)0xFFFFFFFF) {
+ if (outTypeSpecFlags != NULL) {
+ *outTypeSpecFlags = set->typeSpecFlags;
}
- ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
- resID);
- return BAD_INDEX;
+ *outBag = (bag_entry*)(set+1);
+ if (kDebugTableSuperNoisy) {
+ ALOGI("Found existing bag for: 0x%x\n", resID);
+ }
+ return set->numAttrs;
}
+ ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.",
+ resID);
+ return BAD_INDEX;
}
}
// Bag not found, we need to compute it!
- if (!grp->bags) {
- grp->bags = new ByteBucketArray<bag_set**>();
- if (!grp->bags) return NO_MEMORY;
- }
-
- bag_set** typeSet = grp->bags->get(t);
if (!typeSet) {
typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
if (!typeSet) return NO_MEMORY;
- grp->bags->set(t, typeSet);
+ cacheEntry.cachedBags = typeSet;
}
// Mark that we are currently working on this one.
@@ -4452,13 +4436,24 @@
}
packageGroup->clearBagCache();
+ // Find which configurations match the set of parameters. This allows for a much
+ // faster lookup in getEntry() if the set of values is narrowed down.
for (size_t t = 0; t < packageGroup->types.size(); t++) {
+ if (packageGroup->types[t].isEmpty()) {
+ continue;
+ }
+
TypeList& typeList = packageGroup->types.editItemAt(t);
+
+ // Retrieve the cache entry for this type.
+ TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries.editItemAt(t);
+
for (size_t ts = 0; ts < typeList.size(); ts++) {
Type* type = typeList.editItemAt(ts);
- SharedVector<const ResTable_type*> newFilteredConfigs =
+ std::shared_ptr<Vector<const ResTable_type*>> newFilteredConfigs =
std::make_shared<Vector<const ResTable_type*>>();
+
for (size_t ti = 0; ti < type->configs.size(); ti++) {
ResTable_config config;
config.copyFromDtoH(type->configs[ti]->config);
@@ -4472,7 +4467,8 @@
ALOGD("Updating pkg=%zu type=%zu with %zu filtered configs",
p, t, newFilteredConfigs->size());
}
- type->filteredConfigs = newFilteredConfigs;
+
+ cacheEntry.filteredConfigs.add(newFilteredConfigs);
}
}
}
@@ -6010,7 +6006,7 @@
const Vector<const ResTable_type*>* candidateConfigs = &typeSpec->configs;
- SharedVector<const ResTable_type*> filteredConfigs;
+ std::shared_ptr<Vector<const ResTable_type*>> filteredConfigs;
if (config && memcmp(&mParams, config, sizeof(mParams)) == 0) {
// Grab the lock first so we can safely get the current filtered list.
AutoMutex _lock(mFilteredConfigLock);
@@ -6018,13 +6014,16 @@
// This configuration is equal to the one we have previously cached for,
// so use the filtered configs.
- if (typeSpec->filteredConfigs) {
- // Grab a reference to the shared_ptr so it doesn't get destroyed while
- // going through this list.
- filteredConfigs = typeSpec->filteredConfigs;
+ const TypeCacheEntry& cacheEntry = packageGroup->typeCacheEntries[typeIndex];
+ if (i < cacheEntry.filteredConfigs.size()) {
+ if (cacheEntry.filteredConfigs[i]) {
+ // Grab a reference to the shared_ptr so it doesn't get destroyed while
+ // going through this list.
+ filteredConfigs = cacheEntry.filteredConfigs[i];
- // Use this filtered list.
- candidateConfigs = filteredConfigs.get();
+ // Use this filtered list.
+ candidateConfigs = filteredConfigs.get();
+ }
}
}
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 4b8d65c..2bf9b12 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -125,10 +125,10 @@
if (script != NULL) {
memcpy(out->localeScript, script, 4);
- out->localeScriptWasProvided = true;
+ out->localeScriptWasComputed = false;
} else {
out->computeScript();
- out->localeScriptWasProvided = false;
+ out->localeScriptWasComputed = true;
}
if (variant != NULL) {
@@ -182,7 +182,7 @@
EXPECT_EQ('n', test.language[1]);
EXPECT_EQ('U', test.country[0]);
EXPECT_EQ('S', test.country[1]);
- EXPECT_FALSE(test.localeScriptWasProvided);
+ EXPECT_TRUE(test.localeScriptWasComputed);
EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
EXPECT_EQ(0, test.localeVariant[0]);
@@ -203,7 +203,7 @@
EXPECT_EQ('e', test.language[0]);
EXPECT_EQ('n', test.language[1]);
EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
- EXPECT_TRUE(test.localeScriptWasProvided);
+ EXPECT_FALSE(test.localeScriptWasComputed);
memset(out, 1, 4);
test.unpackRegion(out);
EXPECT_EQ('4', out[0]);
@@ -216,7 +216,7 @@
EXPECT_EQ('d', out[0]);
EXPECT_EQ('e', out[1]);
EXPECT_EQ('\0', out[2]);
- EXPECT_FALSE(test.localeScriptWasProvided);
+ EXPECT_TRUE(test.localeScriptWasComputed);
EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
memset(out, 1, 4);
test.unpackRegion(out);
@@ -229,7 +229,7 @@
EXPECT_EQ('d', out[0]);
EXPECT_EQ('e', out[1]);
EXPECT_EQ('\0', out[2]);
- EXPECT_TRUE(test.localeScriptWasProvided);
+ EXPECT_FALSE(test.localeScriptWasComputed);
EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
memset(out, 1, 4);
test.unpackRegion(out);
@@ -270,11 +270,11 @@
fillIn("en", NULL, "Latn", NULL, &config);
char out[RESTABLE_MAX_LOCALE_LEN];
- config.localeScriptWasProvided = true;
+ config.localeScriptWasComputed = false;
config.getBcp47Locale(out);
EXPECT_EQ(0, strcmp("en-Latn", out));
- config.localeScriptWasProvided = false;
+ config.localeScriptWasComputed = true;
config.getBcp47Locale(out);
EXPECT_EQ(0, strcmp("en", out));
}
@@ -379,7 +379,7 @@
// emulate packages built with older AAPT
memset(supported.localeScript, '\0', 4);
- supported.localeScriptWasProvided = false;
+ supported.localeScriptWasComputed = false;
EXPECT_TRUE(supported.match(requested));
}
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index dcfe91e..7cd7fb5 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -282,4 +282,46 @@
testU16StringToInt(u"0x1ffffffff", 0U, false, true);
}
+TEST(ResTableTest, ShareButDontModifyResTable) {
+ ResTable sharedTable;
+ ASSERT_EQ(NO_ERROR, sharedTable.add(basic_arsc, basic_arsc_len));
+
+ ResTable_config param;
+ memset(¶m, 0, sizeof(param));
+ param.language[0] = 'v';
+ param.language[1] = 's';
+ sharedTable.setParameters(¶m);
+
+ // Check that we get the default value for @integer:number1
+ Res_value val;
+ ssize_t block = sharedTable.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(600), val.data);
+
+ // Create a new table that shares the entries of the shared table.
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(&sharedTable, false));
+
+ // Set a new configuration on the new table.
+ memset(¶m, 0, sizeof(param));
+ param.language[0] = 's';
+ param.language[1] = 'v';
+ param.country[0] = 'S';
+ param.country[1] = 'E';
+ table.setParameters(¶m);
+
+ // Check that we get a new value in the new table.
+ block = table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(400), val.data);
+
+ // Check that we still get the old value in the shared table.
+ block = sharedTable.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(600), val.data);
}
+
+} // namespace
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index aaac740..6694dd0 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -46,7 +46,7 @@
namespace integer {
enum {
- number1 = 0x7f040000, // default, sv
+ number1 = 0x7f040000, // default, sv, vs
number2 = 0x7f040001, // default
test3 = 0x7f090000, // default (in feature)
diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h
index 13ab4fa..e497401 100644
--- a/libs/androidfw/tests/data/basic/basic_arsc.h
+++ b/libs/androidfw/tests/data/basic/basic_arsc.h
@@ -1,5 +1,5 @@
unsigned char basic_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x68, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
@@ -16,7 +16,7 @@
0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01,
- 0xa0, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+ 0x44, 0x07, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00,
0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00,
@@ -73,68 +73,81 @@
0x41, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x8c, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x24, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x24, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x4c, 0x00, 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x4c, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x06, 0x7f, 0x01, 0x02, 0x44, 0x00, 0x5c, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x7f, 0x01, 0x02, 0x4c, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x76, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x90, 0x01, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x90, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x58, 0x02, 0x00, 0x00,
+ 0x01, 0x02, 0x4c, 0x00, 0x64, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x98, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -146,16 +159,17 @@
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x7f, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x2c, 0x01, 0x00, 0x00,
0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
- 0x7c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x84, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02,
+ 0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02,
+ 0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00
};
-unsigned int basic_arsc_len = 1896;
+unsigned int basic_arsc_len = 2060;
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/libs/androidfw/tests/data/basic/res/values-vs/values.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to libs/androidfw/tests/data/basic/res/values-vs/values.xml
index b541d13..4a5a640 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/libs/androidfw/tests/data/basic/res/values-vs/values.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 The Android Open Source Project
+<?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.
@@ -13,9 +14,6 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<resources>
+ <integer name="number1">600</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
index b742d28..a2aa598 100644
--- a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
+++ b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
@@ -1,5 +1,5 @@
unsigned char split_de_fr_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0xe4, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0xf4, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
@@ -10,7 +10,7 @@
0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00,
0x61, 0x00, 0x69, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00,
0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x61, 0x00, 0x69, 0x00, 0x20, 0x00,
- 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x5c, 0x03, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x6c, 0x03, 0x00, 0x00,
0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
@@ -57,30 +57,32 @@
0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
- 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00,
+ 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x4c, 0x61, 0x74, 0x6e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
};
-unsigned int split_de_fr_arsc_len = 996;
+unsigned int split_de_fr_arsc_len = 1012;
diff --git a/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
index e9fb7ea..0cc3915 100644
--- a/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
+++ b/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
@@ -1,10 +1,10 @@
unsigned char split_hdpi_v4_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x08, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x68, 0x00,
0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01,
- 0xd0, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+ 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00,
0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00,
@@ -50,19 +50,20 @@
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
};
-unsigned int split_hdpi_v4_arsc_len = 776;
+unsigned int split_hdpi_v4_arsc_len = 784;
diff --git a/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
index 7835f71..d44ba96 100644
--- a/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
+++ b/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
@@ -1,10 +1,10 @@
unsigned char split_xhdpi_v4_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x14, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x78, 0x00,
0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x20, 0x01, 0xd0, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x20, 0x01, 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00,
0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00,
0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00,
@@ -50,19 +50,20 @@
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x60, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-unsigned int split_xhdpi_v4_arsc_len = 780;
+unsigned int split_xhdpi_v4_arsc_len = 788;
diff --git a/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
index f805db1..2f3f682 100644
--- a/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
+++ b/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
@@ -1,10 +1,10 @@
unsigned char split_xxhdpi_v4_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x14, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x78, 0x00,
0x78, 0x00, 0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0x20, 0x01, 0xd0, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x20, 0x01, 0xd8, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00,
0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00,
0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00,
@@ -50,19 +50,20 @@
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x60, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x4c, 0x00, 0x68, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-unsigned int split_xxhdpi_v4_arsc_len = 780;
+unsigned int split_xxhdpi_v4_arsc_len = 788;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 54b453d..ca07738 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,7 +2,7 @@
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-HWUI_NEW_OPS := false
+HWUI_NEW_OPS := true
# Enables fine-grained GLES error checking
# If set to true, every GLES call is wrapped & error checked
@@ -227,18 +227,20 @@
LOCAL_MODULE := hwui_unit_tests
LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu
+LOCAL_SHARED_LIBRARIES := libmemunreachable
LOCAL_CFLAGS := \
$(hwui_cflags) \
-DHWUI_NULL_GPU
LOCAL_SRC_FILES += \
$(hwui_test_common_src_files) \
+ tests/unit/main.cpp \
tests/unit/CanvasStateTests.cpp \
tests/unit/ClipAreaTests.cpp \
- tests/unit/CrashHandlerInjector.cpp \
tests/unit/DamageAccumulatorTests.cpp \
tests/unit/DeviceInfoTests.cpp \
tests/unit/FatVectorTests.cpp \
+ tests/unit/GlopBuilderTests.cpp \
tests/unit/GpuMemoryTrackerTests.cpp \
tests/unit/LayerUpdateQueueTests.cpp \
tests/unit/LinearAllocatorTests.cpp \
@@ -252,6 +254,8 @@
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
+ tests/unit/BakedOpDispatcherTests.cpp \
+ tests/unit/BakedOpRendererTests.cpp \
tests/unit/BakedOpStateTests.cpp \
tests/unit/FrameBuilderTests.cpp \
tests/unit/LeakCheckTests.cpp \
@@ -303,13 +307,13 @@
LOCAL_CFLAGS := \
$(hwui_cflags) \
-DHWUI_NULL_GPU
-LOCAL_C_INCLUDES += bionic/benchmarks/
LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_null_gpu
-LOCAL_STATIC_LIBRARIES := libbenchmark libbase
+LOCAL_STATIC_LIBRARIES := libgoogle-benchmark
LOCAL_SRC_FILES += \
$(hwui_test_common_src_files) \
+ tests/microbench/main.cpp \
tests/microbench/DisplayListCanvasBench.cpp \
tests/microbench/LinearAllocatorBench.cpp \
tests/microbench/PathParserBench.cpp \
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index c147384..bb3ea3f 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -40,6 +40,16 @@
void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
+ // subtract repaintRect from region, since it will be regenerated
+ if (repaintRect.contains(0, 0,
+ offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight)) {
+ // repaint full layer, so throw away entire region
+ offscreenBuffer->region.clear();
+ } else {
+ offscreenBuffer->region.subtractSelf(android::Rect(repaintRect.left, repaintRect.top,
+ repaintRect.right, repaintRect.bottom));
+ }
+
mRenderTarget.offscreenBuffer = offscreenBuffer;
// create and bind framebuffer
@@ -135,17 +145,7 @@
mRenderState.stencil().disable();
}
- mCaches.clearGarbage();
- mCaches.pathCache.trim();
- mCaches.tessellationCache.trim();
-
-#if DEBUG_MEMORY_USAGE
- mCaches.dumpMemoryUsage();
-#else
- if (Properties::debugLevel & kDebugMemory) {
- mCaches.dumpMemoryUsage();
- }
-#endif
+ // Note: we leave FBO 0 renderable here, for post-frame-content decoration
}
void BakedOpRenderer::setViewport(uint32_t width, uint32_t height) {
@@ -179,6 +179,38 @@
return texture;
}
+void BakedOpRenderer::drawRects(const float* rects, int count, const SkPaint* paint) {
+ std::vector<Vertex> vertices;
+ vertices.reserve(count);
+ Vertex* vertex = vertices.data();
+
+ for (int index = 0; index < count; index += 4) {
+ float l = rects[index + 0];
+ float t = rects[index + 1];
+ float r = rects[index + 2];
+ float b = rects[index + 3];
+
+ Vertex::set(vertex++, l, t);
+ Vertex::set(vertex++, r, t);
+ Vertex::set(vertex++, l, b);
+ Vertex::set(vertex++, r, b);
+ }
+
+ LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "decoration only supported for FBO 0");
+ // TODO: Currently assume full FBO damage, due to FrameInfoVisualizer::unionDirty.
+ // Should should scissor safely.
+ mRenderState.scissor().setEnabled(false);
+ Glop glop;
+ GlopBuilder(mRenderState, mCaches, &glop)
+ .setRoundRectClipState(nullptr)
+ .setMeshIndexedQuads(vertices.data(), count / 4)
+ .setFillPaint(*paint, 1.0f)
+ .setTransform(Matrix4::identity(), TransformFlags::None)
+ .setModelViewIdentityEmptyBounds()
+ .build();
+ mRenderState.render(glop, mRenderTarget.orthoMatrix);
+}
+
// clears and re-fills stencil with provided rendertarget space quads,
// and then put stencil into test mode
void BakedOpRenderer::setupStencilQuads(std::vector<Vertex>& quadVertices,
@@ -302,7 +334,7 @@
}
}
-void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const ClipBase* clip,
+void BakedOpRenderer::renderGlopImpl(const Rect* dirtyBounds, const ClipBase* clip,
const Glop& glop) {
prepareRender(dirtyBounds, clip);
mRenderState.render(glop, mRenderTarget.orthoMatrix);
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 55ea935..1b4065a 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_BAKED_OP_RENDERER_H
-#define ANDROID_HWUI_BAKED_OP_RENDERER_H
+#pragma once
#include "BakedOpState.h"
#include "Matrix.h"
@@ -41,6 +40,7 @@
*/
class BakedOpRenderer {
public:
+ typedef void (*GlopReceiver)(BakedOpRenderer&, const Rect*, const ClipBase*, const Glop&);
/**
* Position agnostic shadow lighting info. Used with all shadow ops in scene.
*/
@@ -54,8 +54,10 @@
uint8_t spotShadowAlpha;
};
- BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, const LightInfo& lightInfo)
- : mRenderState(renderState)
+ BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque,
+ const LightInfo& lightInfo)
+ : mGlopReceiver(DefaultGlopReceiver)
+ , mRenderState(renderState)
, mCaches(caches)
, mOpaque(opaque)
, mLightInfo(lightInfo) {
@@ -81,11 +83,30 @@
}
void renderFunctor(const FunctorOp& op, const BakedOpState& state);
- void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop);
+ void renderGlop(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop) {
+ mGlopReceiver(*this, dirtyBounds, clip, glop);
+ }
bool offscreenRenderTarget() { return mRenderTarget.offscreenBuffer != nullptr; }
void dirtyRenderTarget(const Rect& dirtyRect);
bool didDraw() const { return mHasDrawn; }
+
+ uint32_t getViewportWidth() const { return mRenderTarget.viewportWidth; }
+ uint32_t getViewportHeight() const { return mRenderTarget.viewportHeight; }
+
+ // simple draw methods, to be used for end frame decoration
+ void drawRect(float left, float top, float right, float bottom, const SkPaint* paint) {
+ float ltrb[4] = { left, top, right, bottom };
+ drawRects(ltrb, 4, paint);
+ }
+ void drawRects(const float* rects, int count, const SkPaint* paint);
+protected:
+ GlopReceiver mGlopReceiver;
private:
+ static void DefaultGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds,
+ const ClipBase* clip, const Glop& glop) {
+ renderer.renderGlopImpl(dirtyBounds, clip, glop);
+ }
+ void renderGlopImpl(const Rect* dirtyBounds, const ClipBase* clip, const Glop& glop);
void setViewport(uint32_t width, uint32_t height);
void clearColorBuffer(const Rect& clearRect);
void prepareRender(const Rect* dirtyBounds, const ClipBase* clip);
@@ -126,5 +147,3 @@
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_BAKED_OP_RENDERER_H
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 501cbe5..f886dda 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -41,6 +41,10 @@
return transformedBounds;
}
+void ClipBase::dump() const {
+ ALOGD("mode %d" RECT_STRING, mode, RECT_ARGS(rect));
+}
+
/*
* TransformedRectangle
*/
@@ -375,15 +379,13 @@
serialization->rect.set(mClipRegion.getBounds());
break;
}
+ // TODO: this is only done for draw time, should eventually avoid for record time
+ serialization->rect.snapToPixelBoundaries();
mLastSerialization = serialization;
}
return mLastSerialization;
}
-inline static const Rect& getRect(const ClipBase* scb) {
- return reinterpret_cast<const ClipRect*>(scb)->rect;
-}
-
inline static const RectangleList& getRectList(const ClipBase* scb) {
return reinterpret_cast<const ClipRectList*>(scb)->rectList;
}
@@ -425,9 +427,10 @@
&& recordedClip->mode == ClipMode::Rectangle
&& recordedClipTransform.rectToRect())) {
// common case - result is a single rectangle
- auto rectClip = allocator.create<ClipRect>(getRect(recordedClip));
+ auto rectClip = allocator.create<ClipRect>(recordedClip->rect);
recordedClipTransform.mapRect(rectClip->rect);
rectClip->rect.doIntersect(mClipRect);
+ rectClip->rect.snapToPixelBoundaries();
mLastResolutionResult = rectClip;
} else if (CC_UNLIKELY(mMode == ClipMode::Region
|| recordedClip->mode == ClipMode::Region
@@ -438,11 +441,11 @@
case ClipMode::Rectangle:
if (CC_LIKELY(recordedClipTransform.rectToRect())) {
// simple transform, skip creating SkPath
- Rect resultClip(getRect(recordedClip));
+ Rect resultClip(recordedClip->rect);
recordedClipTransform.mapRect(resultClip);
other.setRect(resultClip.toSkIRect());
} else {
- SkPath transformedRect = pathFromTransformedRectangle(getRect(recordedClip),
+ SkPath transformedRect = pathFromTransformedRectangle(recordedClip->rect,
recordedClipTransform);
other.setPath(transformedRect, createViewportRegion());
}
@@ -474,6 +477,7 @@
regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op);
break;
}
+ // Don't need to snap, since region's in int bounds
regionClip->rect.set(regionClip->region.getBounds());
mLastResolutionResult = regionClip;
} else {
@@ -484,7 +488,7 @@
}
if (recordedClip->mode == ClipMode::Rectangle) {
- rectList.intersectWith(getRect(recordedClip), recordedClipTransform);
+ rectList.intersectWith(recordedClip->rect, recordedClipTransform);
} else {
const RectangleList& other = getRectList(recordedClip);
for (int i = 0; i < other.getTransformedRectanglesCount(); i++) {
@@ -495,6 +499,7 @@
}
}
rectListClip->rect = rectList.calculateBounds();
+ rectListClip->rect.snapToPixelBoundaries();
mLastResolutionResult = rectListClip;
}
}
@@ -505,7 +510,7 @@
if (!clip) return; // nothing to do
if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) {
- clipRectWithTransform(getRect(clip), &transform, SkRegion::kIntersect_Op);
+ clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op);
} else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
auto&& rectList = getRectList(clip);
for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) {
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 479796d..1654eb8 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -106,6 +106,8 @@
// Bounds of the clipping area, used to define the scissor, and define which
// portion of the stencil is updated/used
Rect rect;
+
+ void dump() const;
};
struct ClipRect : ClipBase {
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
index 97dec88..9a39ec2 100644
--- a/libs/hwui/FloatColor.h
+++ b/libs/hwui/FloatColor.h
@@ -17,6 +17,7 @@
#define FLOATCOLOR_H
#include "utils/Macros.h"
+#include "utils/MathUtils.h"
#include <stdint.h>
@@ -38,6 +39,17 @@
|| b > 0.0f;
}
+ bool operator==(const FloatColor& other) const {
+ return MathUtils::areEqual(r, other.r)
+ && MathUtils::areEqual(g, other.g)
+ && MathUtils::areEqual(b, other.b)
+ && MathUtils::areEqual(a, other.a);
+ }
+
+ bool operator!=(const FloatColor& other) const {
+ return !(*this == other);
+ }
+
float r;
float g;
float b;
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 5642170..fd5856a 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -203,8 +203,9 @@
mCanvasState.setClippingOutline(mAllocator, &(properties.getOutline()));
}
- bool quickRejected = properties.getClipToBounds()
- && mCanvasState.quickRejectConservative(0, 0, width, height);
+ bool quickRejected = mCanvasState.currentSnapshot()->getRenderTargetClip().isEmpty()
+ || (properties.getClipToBounds()
+ && mCanvasState.quickRejectConservative(0, 0, width, height));
if (!quickRejected) {
// not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer)
if (node.getLayer()) {
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index b7dd3b7..adadd32 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -15,7 +15,11 @@
*/
#include "FrameInfoVisualizer.h"
+#if HWUI_NEW_OPS
+#include "BakedOpRenderer.h"
+#else
#include "OpenGLRenderer.h"
+#endif
#include "utils/Color.h"
#include <cutils/compiler.h>
@@ -88,7 +92,7 @@
}
}
-void FrameInfoVisualizer::draw(OpenGLRenderer* canvas) {
+void FrameInfoVisualizer::draw(ContentRenderer* renderer) {
RETURN_IF_DISABLED();
if (mShowDirtyRegions) {
@@ -96,7 +100,7 @@
if (mFlashToggle) {
SkPaint paint;
paint.setColor(0x7fff0000);
- canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
+ renderer->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
}
}
@@ -111,9 +115,9 @@
info.markSwapBuffers();
info.markFrameCompleted();
- initializeRects(canvas->getViewportHeight(), canvas->getViewportWidth());
- drawGraph(canvas);
- drawThreshold(canvas);
+ initializeRects(renderer->getViewportHeight(), renderer->getViewportWidth());
+ drawGraph(renderer);
+ drawThreshold(renderer);
}
}
@@ -194,27 +198,26 @@
}
}
-void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) {
+void FrameInfoVisualizer::drawGraph(ContentRenderer* renderer) {
SkPaint paint;
for (size_t i = 0; i < Bar.size(); i++) {
nextBarSegment(Bar[i].start, Bar[i].end);
paint.setColor(Bar[i].color & BAR_FAST_MASK);
- canvas->drawRects(mFastRects.get(), mNumFastRects * 4, &paint);
+ renderer->drawRects(mFastRects.get(), mNumFastRects * 4, &paint);
paint.setColor(Bar[i].color & BAR_JANKY_MASK);
- canvas->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint);
+ renderer->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint);
}
}
-void FrameInfoVisualizer::drawThreshold(OpenGLRenderer* canvas) {
+void FrameInfoVisualizer::drawThreshold(ContentRenderer* renderer) {
SkPaint paint;
paint.setColor(THRESHOLD_COLOR);
- paint.setStrokeWidth(mThresholdStroke);
-
- float pts[4];
- pts[0] = 0.0f;
- pts[1] = pts[3] = canvas->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
- pts[2] = canvas->getViewportWidth();
- canvas->drawLines(pts, 4, &paint);
+ float yLocation = renderer->getViewportHeight() - (FRAME_THRESHOLD * mVerticalUnit);
+ renderer->drawRect(0.0f,
+ yLocation - mThresholdStroke/2,
+ renderer->getViewportWidth(),
+ yLocation + mThresholdStroke/2,
+ &paint);
}
bool FrameInfoVisualizer::consumeProperties() {
diff --git a/libs/hwui/FrameInfoVisualizer.h b/libs/hwui/FrameInfoVisualizer.h
index cf877c4..83adf19 100644
--- a/libs/hwui/FrameInfoVisualizer.h
+++ b/libs/hwui/FrameInfoVisualizer.h
@@ -28,7 +28,13 @@
namespace android {
namespace uirenderer {
+#if HWUI_NEW_OPS
+class BakedOpRenderer;
+typedef BakedOpRenderer ContentRenderer;
+#else
class OpenGLRenderer;
+typedef OpenGLRenderer ContentRenderer;
+#endif
// TODO: This is a bit awkward as it needs to match the thing in CanvasContext
// A better abstraction here would be nice but iterators are painful
@@ -46,7 +52,7 @@
void setDensity(float density);
void unionDirty(SkRect* dirty);
- void draw(OpenGLRenderer* canvas);
+ void draw(ContentRenderer* renderer);
void dumpData(int fd);
@@ -56,8 +62,8 @@
void initializeRects(const int baseline, const int width);
void nextBarSegment(FrameInfoIndex start, FrameInfoIndex end);
- void drawGraph(OpenGLRenderer* canvas);
- void drawThreshold(OpenGLRenderer* canvas);
+ void drawGraph(ContentRenderer* renderer);
+ void drawThreshold(ContentRenderer* renderer);
inline float durationMS(size_t index, FrameInfoIndex start, FrameInfoIndex end) {
float duration = mFrameSource[index].duration(start, end) * 0.000001f;
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index e72f396..704bd69 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -81,8 +81,10 @@
* vertex/index/Texture/RoundRectClipState pointers prevent this from
* being safe.
*/
-// TODO: PREVENT_COPY_AND_ASSIGN(...) or similar
struct Glop {
+ PREVENT_COPY_AND_ASSIGN(Glop);
+public:
+ Glop() { }
struct Mesh {
GLuint primitiveMode; // GL_TRIANGLES and GL_TRIANGLE_STRIP supported
@@ -149,7 +151,7 @@
}
} transform;
- const RoundRectClipState* roundRectClipState;
+ const RoundRectClipState* roundRectClipState = nullptr;
/**
* Blending to be used by this draw - both GL_NONE if blending is disabled.
@@ -165,7 +167,7 @@
* Bounds of the drawing command in layer space. Only mapped into layer
* space once GlopBuilder::build() is called.
*/
- Rect bounds;
+ Rect bounds; // TODO: remove for HWUI_NEW_OPS
/**
* Additional render state to enumerate:
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 45fc16c..2799def 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -676,8 +676,11 @@
fill.skiaShaderData.skiaShaderType);
ALOGD("Glop transform");
- glop.transform.modelView.dump("model view");
- glop.transform.canvas.dump("canvas");
+ glop.transform.modelView.dump(" model view");
+ glop.transform.canvas.dump(" canvas");
+ ALOGD_IF(glop.transform.transformFlags, " transformFlags 0x%x", glop.transform.transformFlags);
+
+ ALOGD_IF(glop.roundRectClipState, "Glop RRCS %p", glop.roundRectClipState);
ALOGD("Glop blend %d %d", glop.blend.src, glop.blend.dst);
ALOGD("Glop bounds " RECT_STRING, RECT_ARGS(glop.bounds));
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index c5af279..e6a95ff 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -349,8 +349,9 @@
}
void LayerBuilder::dump() const {
- ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p",
- this, width, height, offscreenBuffer, beginLayerOp, renderNode);
+ ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)",
+ this, width, height, offscreenBuffer, beginLayerOp,
+ renderNode, renderNode ? renderNode->getName() : "-");
for (const BatchBase* batch : mBatches) {
batch->dump();
}
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index deab956..709156c 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -437,8 +437,16 @@
y = dy * dz;
}
+/**
+ * Set the contents of the rect to be the bounding rect around each of the corners, mapped by the
+ * matrix.
+ *
+ * NOTE: an empty rect to an arbitrary matrix isn't guaranteed to have an empty output, since that's
+ * important for conservative bounds estimation (e.g. rotate45Matrix.mapRect of Rect(0, 10) should
+ * result in non-empty.
+ */
void Matrix4::mapRect(Rect& r) const {
- if (isIdentity() || r.isEmpty()) return;
+ if (isIdentity()) return;
if (isSimple()) {
MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 9ae2212..11eb825 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -24,6 +24,14 @@
namespace android {
namespace uirenderer {
+#define MIL_PIX 1000000
+static Rect sUnreasonablyLargeBounds(-MIL_PIX, -MIL_PIX, MIL_PIX, MIL_PIX);
+
+static const Rect& getConservativeOpBounds(const ClipBase* clip) {
+ // if op is clipped, that rect can be used, but otherwise just use a conservatively large rect
+ return clip ? clip->rect : sUnreasonablyLargeBounds;
+}
+
RecordingCanvas::RecordingCanvas(size_t width, size_t height)
: mState(*this)
, mResourceCache(ResourceCache::getInstance()) {
@@ -242,10 +250,8 @@
void RecordingCanvas::drawPaint(const SkPaint& paint) {
const ClipBase* clip = getRecordedClip();
- // if there's no current clip, draw a big rect and hope we cover the eventual clip bounds
- Rect bounds = clip ? clip->rect : Rect(-10000, -10000, 10000, 10000);
addOp(alloc().create_trivial<RectOp>(
- bounds,
+ getConservativeOpBounds(clip),
Matrix4::identity(),
clip,
refPaint(&paint)));
@@ -350,11 +356,15 @@
}
void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, const SkPaint& paint) {
- addOp(alloc().create_trivial<RoundRectOp>(
- Rect(left, top, right, bottom),
- *(mState.currentSnapshot()->transform),
- getRecordedClip(),
- refPaint(&paint), rx, ry));
+ if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) {
+ addOp(alloc().create_trivial<RoundRectOp>(
+ Rect(left, top, right, bottom),
+ *(mState.currentSnapshot()->transform),
+ getRecordedClip(),
+ refPaint(&paint), rx, ry));
+ } else {
+ drawRect(left, top, right, bottom, paint);
+ }
}
void RecordingCanvas::drawRoundRect(
@@ -534,10 +544,11 @@
float hOffset, float vOffset, const SkPaint& paint) {
if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
+ auto clip = getRecordedClip();
addOp(alloc().create_trivial<TextOnPathOp>(
- mState.getLocalClipBounds(), // TODO: explicitly define bounds
+ getConservativeOpBounds(clip), // TODO: explicitly define bounds
*(mState.currentSnapshot()->transform),
- getRecordedClip(),
+ clip,
refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset));
}
@@ -557,15 +568,17 @@
getRecordedClip(),
renderNode);
int opIndex = addOp(op);
- int childIndex = mDisplayList->addChild(op);
+ if (CC_LIKELY(opIndex >= 0)) {
+ int childIndex = mDisplayList->addChild(op);
- // update the chunk's child indices
- DisplayList::Chunk& chunk = mDisplayList->chunks.back();
- chunk.endChildIndex = childIndex + 1;
+ // update the chunk's child indices
+ DisplayList::Chunk& chunk = mDisplayList->chunks.back();
+ chunk.endChildIndex = childIndex + 1;
- if (renderNode->stagingProperties().isProjectionReceiver()) {
- // use staging property, since recording on UI thread
- mDisplayList->projectionReceiveIndex = opIndex;
+ if (renderNode->stagingProperties().isProjectionReceiver()) {
+ // use staging property, since recording on UI thread
+ mDisplayList->projectionReceiveIndex = opIndex;
+ }
}
}
@@ -586,10 +599,11 @@
void RecordingCanvas::callDrawGLFunction(Functor* functor) {
mDisplayList->functors.push_back(functor);
+ auto clip = getRecordedClip();
addOp(alloc().create_trivial<FunctorOp>(
- mState.getLocalClipBounds(), // TODO: explicitly define bounds
+ getConservativeOpBounds(clip),
*(mState.currentSnapshot()->transform),
- getRecordedClip(),
+ clip,
functor));
}
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index d9fce9b..de4fa55 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -286,7 +286,8 @@
friend std::ostream& operator<<(std::ostream& os, const Rect& rect) {
if (rect.isEmpty()) {
- return os << "empty";
+ // Print empty, but continue, since empty rects may still have useful coordinate info
+ os << "(empty)";
}
if (rect.left == 0 && rect.top == 0) {
diff --git a/libs/hwui/debug/nullgles.cpp b/libs/hwui/debug/nullgles.cpp
index ffb0649..8689f98 100644
--- a/libs/hwui/debug/nullgles.cpp
+++ b/libs/hwui/debug/nullgles.cpp
@@ -133,6 +133,15 @@
}
}
+GLenum glCheckFramebufferStatus(GLenum target) {
+ switch (target) {
+ case GL_FRAMEBUFFER:
+ return GL_FRAMEBUFFER_COMPLETE;
+ default:
+ return 0; // error case
+ }
+}
+
const char* getString(GLenum name) {
switch (name) {
case GL_VENDOR:
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
index 5f984b5..bb1a044 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -164,6 +164,9 @@
// resize in place
layer->viewportWidth = width;
layer->viewportHeight = height;
+
+ // entire area will be repainted (and may be smaller) so clear usage region
+ layer->region.clear();
return layer;
}
putOrDelete(layer);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 249d83f..a496b49 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -348,12 +348,26 @@
FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
mRenderNodes, mLightGeometry, mContentDrawBounds, &Caches::getInstance());
mLayerUpdateQueue.clear();
- BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
+ auto&& caches = Caches::getInstance();
+ BakedOpRenderer renderer(caches, mRenderThread.renderState(),
mOpaque, mLightInfo);
- // TODO: profiler().draw(mCanvas);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
+ profiler().draw(&renderer);
bool drew = renderer.didDraw();
+ // post frame cleanup
+ caches.clearGarbage();
+ caches.pathCache.trim();
+ caches.tessellationCache.trim();
+
+#if DEBUG_MEMORY_USAGE
+ mCaches.dumpMemoryUsage();
+#else
+ if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) {
+ caches.dumpMemoryUsage();
+ }
+#endif
+
#else
mCanvas->prepareDirty(frame.width(), frame.height(),
dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque);
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 9595a85..c809ff4 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -19,9 +19,6 @@
#include "DeferredLayerUpdater.h"
#include "LayerRenderer.h"
-#include <unistd.h>
-#include <signal.h>
-
namespace android {
namespace uirenderer {
@@ -136,27 +133,7 @@
canvas->drawTextOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
}
-static void defaultCrashHandler() {
- fprintf(stderr, "RenderThread crashed!");
-}
-
-static std::function<void()> gCrashHandler = defaultCrashHandler;
-static sighandler_t gPreviousSignalHandler;
-
-static void signalHandler(int sig) {
- gCrashHandler();
- if (gPreviousSignalHandler) {
- gPreviousSignalHandler(sig);
- }
-}
-
-void TestUtils::setRenderThreadCrashHandler(std::function<void()> crashHandler) {
- gCrashHandler = crashHandler;
-}
-
void TestUtils::TestTask::run() {
- gPreviousSignalHandler = signal(SIGABRT, signalHandler);
-
// RenderState only valid once RenderThread is running, so queried here
RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
@@ -164,9 +141,6 @@
rtCallback(renderthread::RenderThread::getInstance());
renderState.flush(Caches::FlushMode::Full);
renderState.onGLContextDestroyed();
-
- // Restore the previous signal handler
- signal(SIGABRT, gPreviousSignalHandler);
}
} /* namespace uirenderer */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 6f23705..28ac116 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -180,8 +180,6 @@
typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
- static void setRenderThreadCrashHandler(std::function<void()> crashHandler);
-
class TestTask : public renderthread::RenderTask {
public:
TestTask(RtCallback rtCallback)
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index b317c12..06b68d1 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <benchmark/Benchmark.h>
+#include <benchmark/benchmark.h>
#include "DisplayList.h"
#if HWUI_NEW_OPS
@@ -23,7 +23,6 @@
#include "DisplayListCanvas.h"
#endif
#include "tests/common/TestUtils.h"
-#include "tests/microbench/MicroBench.h"
using namespace android;
using namespace android::uirenderer;
@@ -34,74 +33,64 @@
typedef DisplayListCanvas TestCanvas;
#endif
-BENCHMARK_NO_ARG(BM_DisplayList_alloc);
-void BM_DisplayList_alloc::Run(int iters) {
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+void BM_DisplayList_alloc(benchmark::State& benchState) {
+ while (benchState.KeepRunning()) {
auto displayList = new DisplayList();
- MicroBench::DoNotOptimize(displayList);
+ benchmark::DoNotOptimize(displayList);
delete displayList;
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_DisplayList_alloc);
-BENCHMARK_NO_ARG(BM_DisplayList_alloc_theoretical);
-void BM_DisplayList_alloc_theoretical::Run(int iters) {
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+void BM_DisplayList_alloc_theoretical(benchmark::State& benchState) {
+ while (benchState.KeepRunning()) {
auto displayList = new char[sizeof(DisplayList)];
- MicroBench::DoNotOptimize(displayList);
+ benchmark::DoNotOptimize(displayList);
delete[] displayList;
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_DisplayList_alloc_theoretical);
-BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_empty);
-void BM_DisplayListCanvas_record_empty::Run(int iters) {
+void BM_DisplayListCanvas_record_empty(benchmark::State& benchState) {
TestCanvas canvas(100, 100);
delete canvas.finishRecording();
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (benchState.KeepRunning()) {
canvas.resetRecording(100, 100);
- MicroBench::DoNotOptimize(&canvas);
+ benchmark::DoNotOptimize(&canvas);
delete canvas.finishRecording();
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_DisplayListCanvas_record_empty);
-BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_saverestore);
-void BM_DisplayListCanvas_record_saverestore::Run(int iters) {
+void BM_DisplayListCanvas_record_saverestore(benchmark::State& benchState) {
TestCanvas canvas(100, 100);
delete canvas.finishRecording();
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (benchState.KeepRunning()) {
canvas.resetRecording(100, 100);
canvas.save(SaveFlags::MatrixClip);
canvas.save(SaveFlags::MatrixClip);
- MicroBench::DoNotOptimize(&canvas);
+ benchmark::DoNotOptimize(&canvas);
canvas.restore();
canvas.restore();
delete canvas.finishRecording();
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_DisplayListCanvas_record_saverestore);
-BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_translate);
-void BM_DisplayListCanvas_record_translate::Run(int iters) {
+void BM_DisplayListCanvas_record_translate(benchmark::State& benchState) {
TestCanvas canvas(100, 100);
delete canvas.finishRecording();
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (benchState.KeepRunning()) {
canvas.resetRecording(100, 100);
canvas.scale(10, 10);
- MicroBench::DoNotOptimize(&canvas);
+ benchmark::DoNotOptimize(&canvas);
delete canvas.finishRecording();
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_DisplayListCanvas_record_translate);
/**
* Simulate a simple view drawing a background, overlapped by an image.
@@ -109,16 +98,14 @@
* Note that the recording commands are intentionally not perfectly efficient, as the
* View system frequently produces unneeded save/restores.
*/
-BENCHMARK_NO_ARG(BM_DisplayListCanvas_record_simpleBitmapView);
-void BM_DisplayListCanvas_record_simpleBitmapView::Run(int iters) {
+void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) {
TestCanvas canvas(100, 100);
delete canvas.finishRecording();
SkPaint rectPaint;
SkBitmap iconBitmap = TestUtils::createSkBitmap(80, 80);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (benchState.KeepRunning()) {
canvas.resetRecording(100, 100);
{
canvas.save(SaveFlags::MatrixClip);
@@ -131,11 +118,11 @@
canvas.drawBitmap(iconBitmap, 0, 0, nullptr);
canvas.restore();
}
- MicroBench::DoNotOptimize(&canvas);
+ benchmark::DoNotOptimize(&canvas);
delete canvas.finishRecording();
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_DisplayListCanvas_record_simpleBitmapView);
class NullClient: public CanvasStateClient {
void onViewportInitialized() override {}
@@ -143,48 +130,42 @@
GLuint getTargetFbo() const override { return 0; }
};
-BENCHMARK_NO_ARG(BM_CanvasState_saverestore);
-void BM_CanvasState_saverestore::Run(int iters) {
+void BM_CanvasState_saverestore(benchmark::State& benchState) {
NullClient client;
CanvasState state(client);
state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (benchState.KeepRunning()) {
state.save(SaveFlags::MatrixClip);
state.save(SaveFlags::MatrixClip);
- MicroBench::DoNotOptimize(&state);
+ benchmark::DoNotOptimize(&state);
state.restore();
state.restore();
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_CanvasState_saverestore);
-BENCHMARK_NO_ARG(BM_CanvasState_init);
-void BM_CanvasState_init::Run(int iters) {
+void BM_CanvasState_init(benchmark::State& benchState) {
NullClient client;
CanvasState state(client);
state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (benchState.KeepRunning()) {
state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
- MicroBench::DoNotOptimize(&state);
+ benchmark::DoNotOptimize(&state);
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_CanvasState_init);
-BENCHMARK_NO_ARG(BM_CanvasState_translate);
-void BM_CanvasState_translate::Run(int iters) {
+void BM_CanvasState_translate(benchmark::State& benchState) {
NullClient client;
CanvasState state(client);
state.initializeSaveStack(100, 100, 0, 0, 100, 100, Vector3());
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (benchState.KeepRunning()) {
state.translate(5, 5, 0);
- MicroBench::DoNotOptimize(&state);
+ benchmark::DoNotOptimize(&state);
state.translate(-5, -5, 0);
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_CanvasState_translate);
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 7845eb4..7816f0f 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <benchmark/Benchmark.h>
+#include <benchmark/benchmark.h>
#include "BakedOpState.h"
#include "BakedOpDispatcher.h"
@@ -27,7 +27,6 @@
#include "tests/common/TestScene.h"
#include "tests/common/TestUtils.h"
#include "Vector.h"
-#include "tests/microbench/MicroBench.h"
#include <vector>
@@ -62,38 +61,34 @@
return vec;
}
-BENCHMARK_NO_ARG(BM_FrameBuilder_defer);
-void BM_FrameBuilder_defer::Run(int iters) {
+void BM_FrameBuilder_defer(benchmark::State& state) {
auto nodes = createTestNodeList();
- StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+ while (state.KeepRunning()) {
FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
nodes, sLightGeometry, nullptr);
- MicroBench::DoNotOptimize(&frameBuilder);
+ benchmark::DoNotOptimize(&frameBuilder);
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_FrameBuilder_defer);
-BENCHMARK_NO_ARG(BM_FrameBuilder_deferAndRender);
-void BM_FrameBuilder_deferAndRender::Run(int iters) {
- TestUtils::runOnRenderThread([this, iters](RenderThread& thread) {
+void BM_FrameBuilder_deferAndRender(benchmark::State& state) {
+ TestUtils::runOnRenderThread([&state](RenderThread& thread) {
auto nodes = createTestNodeList();
RenderState& renderState = thread.renderState();
Caches& caches = Caches::getInstance();
- StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+ while (state.KeepRunning()) {
FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
nodes, sLightGeometry, nullptr);
BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
- MicroBench::DoNotOptimize(&renderer);
+ benchmark::DoNotOptimize(&renderer);
}
- StopBenchmarkTiming();
});
}
+BENCHMARK(BM_FrameBuilder_deferAndRender);
static std::vector<sp<RenderNode>> getSyncedSceneNodes(const char* sceneName) {
gDisplay = getBuiltInDisplay(); // switch to real display if present
@@ -113,47 +108,41 @@
return nodes;
}
-static void benchDeferScene(testing::Benchmark& benchmark, int iters, const char* sceneName) {
+static auto SCENES = {
+ "listview",
+};
+
+void BM_FrameBuilder_defer_scene(benchmark::State& state) {
+ const char* sceneName = *(SCENES.begin() + state.range_x());
+ state.SetLabel(sceneName);
auto nodes = getSyncedSceneNodes(sceneName);
- benchmark.StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+ while (state.KeepRunning()) {
FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
nodes, sLightGeometry, nullptr);
- MicroBench::DoNotOptimize(&frameBuilder);
+ benchmark::DoNotOptimize(&frameBuilder);
}
- benchmark.StopBenchmarkTiming();
}
+BENCHMARK(BM_FrameBuilder_defer_scene)->DenseRange(0, SCENES.size() - 1);
-static void benchDeferAndRenderScene(testing::Benchmark& benchmark,
- int iters, const char* sceneName) {
- TestUtils::runOnRenderThread([&benchmark, iters, sceneName](RenderThread& thread) {
+void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) {
+ TestUtils::runOnRenderThread([&state](RenderThread& thread) {
+ const char* sceneName = *(SCENES.begin() + state.range_x());
+ state.SetLabel(sceneName);
auto nodes = getSyncedSceneNodes(sceneName);
RenderState& renderState = thread.renderState();
Caches& caches = Caches::getInstance();
- benchmark.StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+ while (state.KeepRunning()) {
FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
nodes, sLightGeometry, nullptr);
BakedOpRenderer renderer(caches, renderState, true, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
- MicroBench::DoNotOptimize(&renderer);
+ benchmark::DoNotOptimize(&renderer);
}
- benchmark.StopBenchmarkTiming();
});
}
-
-BENCHMARK_NO_ARG(BM_FrameBuilder_listview_defer);
-void BM_FrameBuilder_listview_defer::Run(int iters) {
- benchDeferScene(*this, iters, "listview");
-}
-
-BENCHMARK_NO_ARG(BM_FrameBuilder_listview_deferAndRender);
-void BM_FrameBuilder_listview_deferAndRender::Run(int iters) {
- benchDeferAndRenderScene(*this, iters, "listview");
-}
-
+BENCHMARK(BM_FrameBuilder_deferAndRender_scene)->DenseRange(0, SCENES.size() - 1);
diff --git a/libs/hwui/tests/microbench/LinearAllocatorBench.cpp b/libs/hwui/tests/microbench/LinearAllocatorBench.cpp
index 28513e4..3c0a6c5 100644
--- a/libs/hwui/tests/microbench/LinearAllocatorBench.cpp
+++ b/libs/hwui/tests/microbench/LinearAllocatorBench.cpp
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-#include <benchmark/Benchmark.h>
+#include <benchmark/benchmark.h>
-#include "tests/microbench/MicroBench.h"
#include "utils/LinearAllocator.h"
#include <vector>
@@ -24,30 +23,26 @@
using namespace android;
using namespace android::uirenderer;
-BENCHMARK_NO_ARG(BM_LinearStdAllocator_vectorBaseline);
-void BM_LinearStdAllocator_vectorBaseline::Run(int iters) {
- StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+static void BM_LinearStdAllocator_vectorBaseline(benchmark::State& state) {
+ while (state.KeepRunning()) {
std::vector<char> v;
for (int j = 0; j < 200; j++) {
v.push_back(j);
}
- MicroBench::DoNotOptimize(&v);
+ benchmark::DoNotOptimize(&v);
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_LinearStdAllocator_vectorBaseline);
-BENCHMARK_NO_ARG(BM_LinearStdAllocator_vector);
-void BM_LinearStdAllocator_vector::Run(int iters) {
- StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+static void BM_LinearStdAllocator_vector(benchmark::State& state) {
+ while (state.KeepRunning()) {
LinearAllocator la;
LinearStdAllocator<void*> stdAllocator(la);
std::vector<char, LinearStdAllocator<char> > v(stdAllocator);
for (int j = 0; j < 200; j++) {
v.push_back(j);
}
- MicroBench::DoNotOptimize(&v);
+ benchmark::DoNotOptimize(&v);
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_LinearStdAllocator_vector);
diff --git a/libs/hwui/tests/microbench/MicroBench.h b/libs/hwui/tests/microbench/MicroBench.h
deleted file mode 100644
index f05e92c..0000000
--- a/libs/hwui/tests/microbench/MicroBench.h
+++ /dev/null
@@ -1,35 +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.
- */
-#ifndef MICROBENCH_MICROBENCH_H
-#define MICROBENCH_MICROBENCH_H
-
-namespace android {
-namespace uirenderer {
-
-#define NO_INLINE __attribute__ ((noinline))
-
-class MicroBench {
-public:
- template <class Tp>
- static inline void DoNotOptimize(Tp const& value) {
- asm volatile("" : "+rm" (const_cast<Tp&>(value)));
- }
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* MICROBENCH_MICROBENCH_H */
diff --git a/libs/hwui/tests/microbench/PathParserBench.cpp b/libs/hwui/tests/microbench/PathParserBench.cpp
index bd742c6..4186539 100644
--- a/libs/hwui/tests/microbench/PathParserBench.cpp
+++ b/libs/hwui/tests/microbench/PathParserBench.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <benchmark/Benchmark.h>
+#include <benchmark/benchmark.h>
#include "PathParser.h"
#include "VectorDrawable.h"
@@ -26,26 +26,26 @@
static const char* sPathString = "M 1 1 m 2 2, l 3 3 L 3 3 H 4 h4 V5 v5, Q6 6 6 6 q 6 6 6 6t 7 7 T 7 7 C 8 8 8 8 8 8 c 8 8 8 8 8 8 S 9 9 9 9 s 9 9 9 9 A 10 10 0 1 1 10 10 a 10 10 0 1 1 10 10";
-BENCHMARK_NO_ARG(BM_PathParser_parseStringPathForSkPath);
-void BM_PathParser_parseStringPathForSkPath::Run(int iter) {
+void BM_PathParser_parseStringPathForSkPath(benchmark::State& state) {
SkPath skPath;
size_t length = strlen(sPathString);
PathParser::ParseResult result;
- StartBenchmarkTiming();
- for (int i = 0; i < iter; i++) {
+ while (state.KeepRunning()) {
PathParser::parseStringForSkPath(&skPath, &result, sPathString, length);
+ benchmark::DoNotOptimize(&result);
+ benchmark::DoNotOptimize(&skPath);
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_PathParser_parseStringPathForSkPath);
-BENCHMARK_NO_ARG(BM_PathParser_parseStringPathForPathData);
-void BM_PathParser_parseStringPathForPathData::Run(int iter) {
+void BM_PathParser_parseStringPathForPathData(benchmark::State& state) {
size_t length = strlen(sPathString);
PathData outData;
PathParser::ParseResult result;
- StartBenchmarkTiming();
- for (int i = 0; i < iter; i++) {
+ while (state.KeepRunning()) {
PathParser::getPathDataFromString(&outData, &result, sPathString, length);
+ benchmark::DoNotOptimize(&result);
+ benchmark::DoNotOptimize(&outData);
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_PathParser_parseStringPathForPathData);
diff --git a/libs/hwui/tests/microbench/ShadowBench.cpp b/libs/hwui/tests/microbench/ShadowBench.cpp
index 98ec4d9..a0fc6e8 100644
--- a/libs/hwui/tests/microbench/ShadowBench.cpp
+++ b/libs/hwui/tests/microbench/ShadowBench.cpp
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-#include <benchmark/Benchmark.h>
+#include <benchmark/benchmark.h>
#include "Matrix.h"
#include "Rect.h"
#include "Vector.h"
#include "VertexBuffer.h"
#include "TessellationCache.h"
-#include "tests/microbench/MicroBench.h"
#include <SkPath.h>
@@ -78,39 +77,35 @@
testData.lightRadius, *ambient, *spot);
}
-BENCHMARK_NO_ARG(BM_TessellateShadows_roundrect_opaque);
-void BM_TessellateShadows_roundrect_opaque::Run(int iters) {
+void BM_TessellateShadows_roundrect_opaque(benchmark::State& state) {
ShadowTestData shadowData;
createShadowTestData(&shadowData);
SkPath path;
path.addRoundRect(SkRect::MakeWH(100, 100), 5, 5);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+ while (state.KeepRunning()) {
VertexBuffer ambient;
VertexBuffer spot;
tessellateShadows(shadowData, true, path, &ambient, &spot);
- MicroBench::DoNotOptimize(&ambient);
- MicroBench::DoNotOptimize(&spot);
+ benchmark::DoNotOptimize(&ambient);
+ benchmark::DoNotOptimize(&spot);
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_TessellateShadows_roundrect_opaque);
-BENCHMARK_NO_ARG(BM_TessellateShadows_roundrect_translucent);
-void BM_TessellateShadows_roundrect_translucent::Run(int iters) {
+void BM_TessellateShadows_roundrect_translucent(benchmark::State& state) {
ShadowTestData shadowData;
createShadowTestData(&shadowData);
SkPath path;
path.reset();
path.addRoundRect(SkRect::MakeLTRB(0, 0, 100, 100), 5, 5);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+ while (state.KeepRunning()) {
std::unique_ptr<VertexBuffer> ambient(new VertexBuffer);
std::unique_ptr<VertexBuffer> spot(new VertexBuffer);
tessellateShadows(shadowData, false, path, ambient.get(), spot.get());
- MicroBench::DoNotOptimize(ambient.get());
- MicroBench::DoNotOptimize(spot.get());
+ benchmark::DoNotOptimize(ambient.get());
+ benchmark::DoNotOptimize(spot.get());
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_TessellateShadows_roundrect_translucent);
diff --git a/libs/hwui/tests/microbench/TaskManagerBench.cpp b/libs/hwui/tests/microbench/TaskManagerBench.cpp
index 0ea30e47..c6b9f3b 100644
--- a/libs/hwui/tests/microbench/TaskManagerBench.cpp
+++ b/libs/hwui/tests/microbench/TaskManagerBench.cpp
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-#include <benchmark/Benchmark.h>
+#include <benchmark/benchmark.h>
#include "thread/Task.h"
#include "thread/TaskManager.h"
#include "thread/TaskProcessor.h"
-#include "tests/microbench/MicroBench.h"
#include <vector>
@@ -39,55 +38,51 @@
}
};
-BENCHMARK_NO_ARG(BM_TaskManager_allocateTask);
-void BM_TaskManager_allocateTask::Run(int iters) {
+void BM_TaskManager_allocateTask(benchmark::State& state) {
std::vector<sp<TrivialTask> > tasks;
- tasks.reserve(iters);
+ tasks.reserve(state.max_iterations);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+ while (state.KeepRunning()) {
tasks.emplace_back(new TrivialTask);
- MicroBench::DoNotOptimize(tasks.back());
+ benchmark::DoNotOptimize(tasks.back());
}
- StopBenchmarkTiming();
}
+BENCHMARK(BM_TaskManager_allocateTask);
-BENCHMARK_NO_ARG(BM_TaskManager_enqueueTask);
-void BM_TaskManager_enqueueTask::Run(int iters) {
+void BM_TaskManager_enqueueTask(benchmark::State& state) {
TaskManager taskManager;
sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager));
std::vector<sp<TrivialTask> > tasks;
- tasks.reserve(iters);
+ tasks.reserve(state.max_iterations);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+ while (state.KeepRunning()) {
tasks.emplace_back(new TrivialTask);
- MicroBench::DoNotOptimize(tasks.back());
+ benchmark::DoNotOptimize(tasks.back());
processor->add(tasks.back());
}
- StopBenchmarkTiming();
for (sp<TrivialTask>& task : tasks) {
task->getResult();
}
}
+BENCHMARK(BM_TaskManager_enqueueTask);
-BENCHMARK_NO_ARG(BM_TaskManager_enqueueRunDeleteTask);
-void BM_TaskManager_enqueueRunDeleteTask::Run(int iters) {
+void BM_TaskManager_enqueueRunDeleteTask(benchmark::State& state) {
TaskManager taskManager;
sp<TrivialProcessor> processor(new TrivialProcessor(&taskManager));
std::vector<sp<TrivialTask> > tasks;
- tasks.reserve(iters);
+ tasks.reserve(state.max_iterations);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; i++) {
+ while (state.KeepRunning()) {
tasks.emplace_back(new TrivialTask);
- MicroBench::DoNotOptimize(tasks.back());
+ benchmark::DoNotOptimize(tasks.back());
processor->add(tasks.back());
}
+ state.ResumeTiming();
for (sp<TrivialTask>& task : tasks) {
- MicroBench::DoNotOptimize(task->getResult());
+ benchmark::DoNotOptimize(task->getResult());
}
tasks.clear();
- StopBenchmarkTiming();
+ state.PauseTiming();
}
+BENCHMARK(BM_TaskManager_enqueueRunDeleteTask);
diff --git a/libs/hwui/tests/microbench/main.cpp b/libs/hwui/tests/microbench/main.cpp
new file mode 100644
index 0000000..a0157bc
--- /dev/null
+++ b/libs/hwui/tests/microbench/main.cpp
@@ -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.
+ */
+
+#include <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
new file mode 100644
index 0000000..654ddc6
--- /dev/null
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <RecordedOp.h>
+#include <BakedOpDispatcher.h>
+#include <BakedOpRenderer.h>
+#include <tests/common/TestUtils.h>
+
+using namespace android::uirenderer;
+
+static BakedOpRenderer::LightInfo sLightInfo;
+static Rect sBaseClip(100, 100);
+
+class ValidatingBakedOpRenderer : public BakedOpRenderer {
+public:
+ ValidatingBakedOpRenderer(RenderState& renderState, std::function<void(const Glop& glop)> validator)
+ : BakedOpRenderer(Caches::getInstance(), renderState, true, sLightInfo)
+ , mValidator(validator) {
+ mGlopReceiver = ValidatingGlopReceiver;
+ }
+private:
+ static void ValidatingGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds,
+ const ClipBase* clip, const Glop& glop) {
+
+ auto vbor = reinterpret_cast<ValidatingBakedOpRenderer*>(&renderer);
+ vbor->mValidator(glop);
+ }
+ std::function<void(const Glop& glop)> mValidator;
+};
+
+typedef void (*BakedOpReceiver)(BakedOpRenderer&, const BakedOpState&);
+
+static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, RecordedOp* op,
+ std::function<void(const Glop& glop)> glopVerifier) {
+ // Create op, and wrap with basic state.
+ LinearAllocator allocator;
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), sBaseClip);
+ auto state = BakedOpState::tryConstruct(allocator, *snapshot, *op);
+ ASSERT_NE(nullptr, state);
+
+ int glopCount = 0;
+ auto glopReceiver = [&glopVerifier, &glopCount] (const Glop& glop) {
+ ASSERT_EQ(glopCount++, 0) << "Only one Glop expected";
+ glopVerifier(glop);
+ };
+ ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
+
+ // Dispatch based on op type created, similar to Frame/LayerBuilder dispatch behavior
+#define X(Type) \
+ [](BakedOpRenderer& renderer, const BakedOpState& state) { \
+ BakedOpDispatcher::on##Type(renderer, static_cast<const Type&>(*(state.op)), state); \
+ },
+ static BakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X);
+#undef X
+ unmergedReceivers[op->opId](renderer, *state);
+ ASSERT_EQ(1, glopCount) << "Exactly one Glop expected";
+}
+
+RENDERTHREAD_TEST(BakedOpDispatcher, onArc_position) {
+ SkPaint strokePaint;
+ strokePaint.setStyle(SkPaint::kStroke_Style);
+ strokePaint.setStrokeWidth(4);
+ ArcOp op(Rect(10, 15, 20, 25), Matrix4::identity(), nullptr, &strokePaint, 0, 270, true);
+ testUnmergedGlopDispatch(renderThread, &op, [] (const Glop& glop) {
+ // validate glop produced by renderPathTexture (so texture, unit quad)
+ auto texture = glop.fill.texture.texture;
+ ASSERT_NE(nullptr, texture);
+ float expectedOffset = floor(4 * 1.5f + 0.5f);
+ EXPECT_EQ(expectedOffset, reinterpret_cast<PathTexture*>(texture)->offset)
+ << "Should see conservative offset from PathCache::computeBounds";
+ Rect expectedBounds(10, 15, 20, 25);
+ expectedBounds.outset(expectedOffset);
+ EXPECT_EQ(expectedBounds, glop.bounds) << "bounds outset by stroke 'offset'";
+ Matrix4 expectedModelView;
+ expectedModelView.loadTranslate(10 - expectedOffset, 15 - expectedOffset, 0);
+ expectedModelView.scale(10 + 2 * expectedOffset, 10 + 2 * expectedOffset, 1);
+ EXPECT_EQ(expectedModelView, glop.transform.modelView)
+ << "X and Y offsets, and scale both applied to model view";
+ });
+}
+
+RENDERTHREAD_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
+ SkPaint layerPaint;
+ layerPaint.setAlpha(128);
+ OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case
+ LayerOp op(Rect(10, 10), Matrix4::identity(), nullptr, &layerPaint, &buffer);
+ testUnmergedGlopDispatch(renderThread, &op, [&renderThread] (const Glop& glop) {
+ // rect glop is dispatched with paint props applied
+ EXPECT_EQ(renderThread.renderState().meshState().getUnitQuadVBO(),
+ glop.mesh.vertices.bufferObject) << "Unit quad should be drawn";
+ EXPECT_EQ(nullptr, glop.fill.texture.texture) << "Should be no texture when layer is null";
+ EXPECT_FLOAT_EQ(128 / 255.0f, glop.fill.color.a) << "Rect quad should use op alpha";
+ });
+}
diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
new file mode 100644
index 0000000..59bd75e
--- /dev/null
+++ b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <BakedOpRenderer.h>
+#include <tests/common/TestUtils.h>
+
+using namespace android::uirenderer;
+
+const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
+
+RENDERTHREAD_TEST(BakedOpRenderer, startRepaintLayer_clear) {
+ BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, sLightInfo);
+ OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u);
+
+ layer.dirty(Rect(200, 200));
+ {
+ renderer.startRepaintLayer(&layer, Rect(200, 200));
+ EXPECT_TRUE(layer.region.isEmpty()) << "Repaint full layer should clear region";
+ renderer.endLayer();
+ }
+
+ layer.dirty(Rect(200, 200));
+ {
+ renderer.startRepaintLayer(&layer, Rect(100, 200)); // repainting left side
+ EXPECT_TRUE(layer.region.isRect());
+ //ALOGD("bounds %d %d %d %d", RECT_ARGS(layer.region.getBounds()));
+ EXPECT_EQ(android::Rect(100, 0, 200, 200), layer.region.getBounds())
+ << "Left side being repainted, so right side should be clear";
+ renderer.endLayer();
+ }
+
+ // right side is now only dirty portion
+ {
+ renderer.startRepaintLayer(&layer, Rect(100, 0, 200, 200)); // repainting right side
+ EXPECT_TRUE(layer.region.isEmpty())
+ << "Now right side being repainted, so region should be entirely clear";
+ renderer.endLayer();
+ }
+}
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
index dc2ea07..822d04f 100644
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ b/libs/hwui/tests/unit/ClipAreaTests.cpp
@@ -27,7 +27,7 @@
namespace android {
namespace uirenderer {
-static Rect kViewportBounds(0, 0, 2048, 2048);
+static Rect kViewportBounds(2048, 2048);
static ClipArea createClipArea() {
ClipArea area;
@@ -140,17 +140,15 @@
// rect list
Matrix4 rotate;
- rotate.loadRotate(2.0f);
- area.clipRectWithTransform(Rect(200, 200), &rotate, SkRegion::kIntersect_Op);
+ rotate.loadRotate(5.0f);
+ area.clipRectWithTransform(Rect(50, 50, 150, 150), &rotate, SkRegion::kIntersect_Op);
{
auto serializedClip = area.serializeClip(allocator);
ASSERT_NE(nullptr, serializedClip);
ASSERT_EQ(ClipMode::RectangleList, serializedClip->mode);
auto clipRectList = reinterpret_cast<const ClipRectList*>(serializedClip);
EXPECT_EQ(2, clipRectList->rectList.getTransformedRectanglesCount());
- EXPECT_FALSE(clipRectList->rect.isEmpty());
- EXPECT_FLOAT_EQ(199.87817f, clipRectList->rect.right)
- << "Right side should be clipped by rotated rect";
+ EXPECT_EQ(Rect(37, 54, 145, 163), clipRectList->rect);
EXPECT_EQ(serializedClip, area.serializeClip(allocator))
<< "Requery of clip on unmodified ClipArea must return same pointer.";
}
@@ -241,5 +239,28 @@
}
}
+TEST(ClipArea, serializeIntersectedClip_snap) {
+ ClipArea area(createClipArea());
+ area.setClip(100.2, 100.4, 500.6, 500.8);
+ LinearAllocator allocator;
+
+ {
+ // no recorded clip case
+ auto resolvedClip = area.serializeIntersectedClip(allocator, nullptr, Matrix4::identity());
+ EXPECT_EQ(Rect(100, 100, 501, 501), resolvedClip->rect);
+ }
+ {
+ // recorded clip case
+ ClipRect recordedClip(Rect(100.12, 100.74));
+ Matrix4 translateScale;
+ translateScale.loadTranslate(100, 100, 0);
+ translateScale.scale(2, 3, 1); // recorded clip will have non-int coords, even after transform
+ auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
+ ASSERT_NE(nullptr, resolvedClip);
+ EXPECT_EQ(ClipMode::Rectangle, resolvedClip->mode);
+ EXPECT_EQ(Rect(100, 100, 300, 402), resolvedClip->rect);
+ }
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/tests/unit/CrashHandlerInjector.cpp b/libs/hwui/tests/unit/CrashHandlerInjector.cpp
deleted file mode 100644
index b1c678d..0000000
--- a/libs/hwui/tests/unit/CrashHandlerInjector.cpp
+++ /dev/null
@@ -1,41 +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.
- */
-
-#include "tests/common/TestUtils.h"
-
-#include <gtest/gtest.h>
-#include <cstdio>
-
-using namespace android::uirenderer;
-
-static void gunitCrashHandler() {
- auto testinfo = ::testing::UnitTest::GetInstance()->current_test_info();
- printf("[ FAILED ] %s.%s\n", testinfo->test_case_name(),
- testinfo->name());
- printf("[ FATAL! ] RenderThread crashed, aborting tests!\n");
- fflush(stdout);
-}
-
-static void hookError() {
- TestUtils::setRenderThreadCrashHandler(gunitCrashHandler);
-}
-
-class HookErrorInit {
-public:
- HookErrorInit() { hookError(); }
-};
-
-static HookErrorInit sInit;
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 4c56a22..f147fd4 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -349,6 +349,29 @@
EXPECT_EQ(1, renderer.getIndex());
}
+TEST(FrameBuilder, functor_reject) {
+ class FunctorTestRenderer : public TestRendererBase {
+ public:
+ void onFunctorOp(const FunctorOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ }
+ };
+ Functor noopFunctor;
+
+ // 1 million pixel tall view, scrolled down 80%
+ auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000,
+ [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.translate(0, -800000);
+ canvas.callDrawGLFunction(&noopFunctor);
+ });
+
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ TestUtils::createSyncedNodeList(scrolledFunctorView), sLightGeometry, nullptr);
+ FunctorTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected";
+}
+
TEST(FrameBuilder, renderNode) {
class RenderNodeTestRenderer : public TestRendererBase {
public:
@@ -391,6 +414,7 @@
TestUtils::createSyncedNodeList(parent), sLightGeometry, nullptr);
RenderNodeTestRenderer renderer;
frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex());
}
TEST(FrameBuilder, clipped) {
diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp
new file mode 100644
index 0000000..949c541
--- /dev/null
+++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "BakedOpRenderer.h"
+#include "Glop.h"
+#include "GlopBuilder.h"
+#include "Rect.h"
+#include "tests/common/TestUtils.h"
+#include "utils/Color.h"
+
+#include <SkPaint.h>
+
+using namespace android::uirenderer;
+
+static void expectFillEq(Glop::Fill& expectedFill, Glop::Fill& builtFill) {
+ EXPECT_EQ(expectedFill.colorEnabled, builtFill.colorEnabled);
+ if (expectedFill.colorEnabled)
+ EXPECT_EQ(expectedFill.color, builtFill.color);
+
+ EXPECT_EQ(expectedFill.filterMode, builtFill.filterMode);
+ if (expectedFill.filterMode == ProgramDescription::ColorFilterMode::Blend) {
+ EXPECT_EQ(expectedFill.filter.color, builtFill.filter.color);
+ } else if (expectedFill.filterMode == ProgramDescription::ColorFilterMode::Matrix) {
+ Glop::Fill::Filter::Matrix& expectedMatrix = expectedFill.filter.matrix;
+ Glop::Fill::Filter::Matrix& builtMatrix = expectedFill.filter.matrix;
+ EXPECT_TRUE(std::memcmp(expectedMatrix.matrix, builtMatrix.matrix,
+ sizeof(Glop::Fill::Filter::Matrix::matrix)));
+ EXPECT_TRUE(std::memcmp(expectedMatrix.vector, builtMatrix.vector,
+ sizeof(Glop::Fill::Filter::Matrix::vector)));
+ }
+ EXPECT_EQ(expectedFill.skiaShaderData.skiaShaderType, builtFill.skiaShaderData.skiaShaderType);
+ EXPECT_EQ(expectedFill.texture.clamp, builtFill.texture.clamp);
+ EXPECT_EQ(expectedFill.texture.filter, builtFill.texture.filter);
+ EXPECT_EQ(expectedFill.texture.target, builtFill.texture.target);
+ EXPECT_EQ(expectedFill.texture.textureTransform, builtFill.texture.textureTransform);
+}
+
+static void expectBlendEq(Glop::Blend& expectedBlend, Glop::Blend& builtBlend) {
+ EXPECT_EQ(expectedBlend.src, builtBlend.src);
+ EXPECT_EQ(expectedBlend.dst, builtBlend.dst);
+}
+
+static void expectMeshEq(Glop::Mesh& expectedMesh, Glop::Mesh& builtMesh) {
+ EXPECT_EQ(expectedMesh.elementCount, builtMesh.elementCount);
+ EXPECT_EQ(expectedMesh.primitiveMode, builtMesh.primitiveMode);
+ EXPECT_EQ(expectedMesh.indices.indices, builtMesh.indices.indices);
+ EXPECT_EQ(expectedMesh.indices.bufferObject, builtMesh.indices.bufferObject);
+ EXPECT_EQ(expectedMesh.vertices.attribFlags, builtMesh.vertices.attribFlags);
+ EXPECT_EQ(expectedMesh.vertices.bufferObject, builtMesh.vertices.bufferObject);
+ EXPECT_EQ(expectedMesh.vertices.color, builtMesh.vertices.color);
+ EXPECT_EQ(expectedMesh.vertices.position, builtMesh.vertices.position);
+ EXPECT_EQ(expectedMesh.vertices.stride, builtMesh.vertices.stride);
+ EXPECT_EQ(expectedMesh.vertices.texCoord, builtMesh.vertices.texCoord);
+
+ if (builtMesh.vertices.position) {
+ for (int i = 0; i < 4; i++) {
+ TextureVertex& expectedVertex = expectedMesh.mappedVertices[i];
+ TextureVertex& builtVertex = builtMesh.mappedVertices[i];
+ EXPECT_EQ(expectedVertex.u, builtVertex.u);
+ EXPECT_EQ(expectedVertex.v, builtVertex.v);
+ EXPECT_EQ(expectedVertex.x, builtVertex.x);
+ EXPECT_EQ(expectedVertex.y, builtVertex.y);
+ }
+ }
+}
+
+static void expectTransformEq(Glop::Transform& expectedTransform, Glop::Transform& builtTransform) {
+ EXPECT_EQ(expectedTransform.canvas, builtTransform.canvas);
+ EXPECT_EQ(expectedTransform.modelView, builtTransform.modelView);
+ EXPECT_EQ(expectedTransform.transformFlags, expectedTransform.transformFlags);
+}
+
+static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) {
+ EXPECT_EQ(expectedGlop.bounds, builtGlop.bounds);
+ expectBlendEq(expectedGlop.blend, builtGlop.blend);
+ expectFillEq(expectedGlop.fill, builtGlop.fill);
+ expectMeshEq(expectedGlop.mesh, builtGlop.mesh);
+ expectTransformEq(expectedGlop.transform, builtGlop.transform);
+}
+
+static std::unique_ptr<Glop> blackUnitQuadGlop(RenderState& renderState) {
+ std::unique_ptr<Glop> glop(new Glop());
+ glop->blend = { GL_ZERO, GL_ZERO };
+ glop->mesh.elementCount = 4;
+ glop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
+ glop->mesh.indices.indices = nullptr;
+ glop->mesh.indices.bufferObject = GL_ZERO;
+ glop->mesh.vertices = {
+ renderState.meshState().getUnitQuadVBO(),
+ VertexAttribFlags::None,
+ nullptr, nullptr, nullptr,
+ kTextureVertexStride };
+ glop->transform.modelView.loadIdentity();
+ glop->fill.colorEnabled = true;
+ glop->fill.color.set(Color::Black);
+ glop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType;
+ glop->fill.filterMode = ProgramDescription::ColorFilterMode::None;
+ glop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
+ return glop;
+}
+
+RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) {
+ RenderState& renderState = renderThread.renderState();
+ Caches& caches = Caches::getInstance();
+ SkPaint paint;
+ Rect dest(1, 1, 100, 100);
+ Matrix4 simpleTranslate;
+ simpleTranslate.loadTranslate(0.7, 0.7, 0);
+ Glop glop;
+ GlopBuilder(renderState, caches, &glop)
+ .setRoundRectClipState(nullptr)
+ .setMeshUnitQuad()
+ .setFillPaint(paint, 1.0f)
+ .setTransform(simpleTranslate, TransformFlags::None)
+ .setModelViewMapUnitToRectSnap(dest)
+ .build();
+
+ std::unique_ptr<Glop> goldenGlop(blackUnitQuadGlop(renderState));
+ // Rect(1,1,100,100) is the set destination,
+ // so unit quad should be translated by (1,1) and scaled by (99, 99)
+ // Tricky part: because translate (0.7, 0.7) and snapping were set in glopBuilder,
+ // unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels.
+ goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0);
+ goldenGlop->transform.modelView.scale(99, 99, 1);
+ goldenGlop->bounds = android::uirenderer::Rect(1.70, 1.70, 100.70, 100.70);
+ goldenGlop->transform.canvas = simpleTranslate;
+ goldenGlop->fill.texture.filter = GL_NEAREST;
+ expectGlopEq(*goldenGlop, glop);
+}
diff --git a/libs/hwui/tests/unit/LinearAllocatorTests.cpp b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
index 402a09c..ffcbf12 100644
--- a/libs/hwui/tests/unit/LinearAllocatorTests.cpp
+++ b/libs/hwui/tests/unit/LinearAllocatorTests.cpp
@@ -113,3 +113,21 @@
EXPECT_GT(lastLocation + 20, &v[0]);
}
+
+TEST(LsaVector, dtorCheck) {
+ LinearAllocator allocator;
+ LinearStdAllocator<void*> stdAllocator(allocator);
+
+ for (int size : {1, 2, 3, 500}) {
+ int destroyed = 0;
+ {
+ LsaVector<std::unique_ptr<TestUtils::SignalingDtor> > vector(stdAllocator);
+ for (int i = 0; i < size; i++) {
+ vector.emplace_back(new TestUtils::SignalingDtor(&destroyed));
+ }
+ EXPECT_EQ(0, destroyed);
+ EXPECT_EQ(size, (int) vector.size());
+ }
+ EXPECT_EQ(size, destroyed);
+ }
+}
diff --git a/libs/hwui/tests/unit/MatrixTests.cpp b/libs/hwui/tests/unit/MatrixTests.cpp
index da22637..eddab87 100644
--- a/libs/hwui/tests/unit/MatrixTests.cpp
+++ b/libs/hwui/tests/unit/MatrixTests.cpp
@@ -21,15 +21,30 @@
using namespace android::uirenderer;
-TEST(Matrix, mapRect) {
+TEST(Matrix, mapRect_emptyScaleSkew) {
// Skew, so we don't hit identity/translate/simple fast paths
- Matrix4 matrix;
- matrix.skew(0.1f, 0.1f);
+ Matrix4 scaleMatrix;
+ scaleMatrix.loadScale(10, 10, 1);
+ scaleMatrix.skew(0.1f, 0.1f);
// non-zero empty rect, so sorting x/y would make rect non-empty
- Rect empty(100, 100, -100, -100);
+ Rect empty(15, 20, 15, 100);
ASSERT_TRUE(empty.isEmpty());
- matrix.mapRect(empty);
- EXPECT_TRUE(empty.isEmpty())
- << "Empty rect should always remain empty, regardless of mapping.";
+ scaleMatrix.mapRect(empty);
+ EXPECT_EQ(Rect(170, 215, 250, 1015), empty);
+ EXPECT_FALSE(empty.isEmpty())
+ << "Empty 'line' rect doesn't remain empty when skewed.";
+}
+
+TEST(Matrix, mapRect_emptyRotate) {
+ // Skew, so we don't hit identity/translate/simple fast paths
+ Matrix4 skewMatrix;
+ skewMatrix.loadRotate(45);
+
+ // non-zero empty rect, so sorting x/y would make rect non-empty
+ Rect lineRect(0, 100);
+ ASSERT_TRUE(lineRect.isEmpty());
+ skewMatrix.mapRect(lineRect);
+ EXPECT_FALSE(lineRect.isEmpty())
+ << "Empty 'line' rect doesn't remain empty when rotated.";
}
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index 0c6eb57..37a485e 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -103,9 +103,11 @@
OffscreenBufferPool pool;
auto layer = pool.get(thread.renderState(), 64u, 64u);
+ layer->dirty(Rect(64, 64));
// resize in place
ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
+ EXPECT_TRUE(layer->region.isEmpty()) << "In place resize should clear usage region";
EXPECT_EQ(60u, layer->viewportWidth);
EXPECT_EQ(55u, layer->viewportHeight);
EXPECT_EQ(64u, layer->texture.width());
@@ -113,9 +115,13 @@
// resized to use different object in pool
auto layer2 = pool.get(thread.renderState(), 128u, 128u);
+ layer2->dirty(Rect(128, 128));
+ EXPECT_FALSE(layer2->region.isEmpty());
pool.putOrDelete(layer2);
ASSERT_EQ(1u, pool.getCount());
+
ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
+ EXPECT_TRUE(layer2->region.isEmpty()) << "Swap resize should clear usage region";
EXPECT_EQ(120u, layer2->viewportWidth);
EXPECT_EQ(125u, layer2->viewportHeight);
EXPECT_EQ(128u, layer2->texture.width());
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index c39047c..5e613fd 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -114,6 +114,23 @@
EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
}
+TEST(RecordingCanvas, drawRoundRect) {
+ // Round case - stays rounded
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
+ });
+ ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
+ ASSERT_EQ(RecordedOpId::RoundRectOp, dl->getOps()[0]->opId);
+
+ // Non-rounded case - turned into drawRect
+ dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
+ canvas.drawRoundRect(0, 0, 100, 100, 0, -1, SkPaint());
+ });
+ ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
+ ASSERT_EQ(RecordedOpId::RectOp, dl->getOps()[0]->opId)
+ << "Non-rounded rects should be converted";
+}
+
TEST(RecordingCanvas, drawText) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
@@ -442,9 +459,7 @@
// since the same clip will be computed at draw time. If such a change is made, this
// check could be done at record time by querying the clip, or the clip could be altered
// slightly so that it is serialized.
- EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136),
- (reinterpret_cast<const ClipRect*>(op.localClip))->rect);
-
+ EXPECT_EQ(Rect(59, 59, 341, 341), op.localClip->rect);
EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
expectedMatrix.loadIdentity();
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
@@ -453,6 +468,21 @@
EXPECT_EQ(3, count);
}
+TEST(RecordingCanvas, drawRenderNode_rejection) {
+ auto child = TestUtils::createNode(50, 50, 150, 150,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ });
+
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) {
+ canvas.clipRect(0, 0, 0, 0, SkRegion::kIntersect_Op); // empty clip, reject node
+ canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
+ });
+ ASSERT_TRUE(dl->isEmpty());
+}
+
TEST(RecordingCanvas, drawRenderNode_projection) {
sp<RenderNode> background = TestUtils::createNode(50, 50, 150, 150,
[](RenderProperties& props, RecordingCanvas& canvas) {
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
new file mode 100644
index 0000000..409a12d
--- /dev/null
+++ b/libs/hwui/tests/unit/main.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 "gtest/gtest.h"
+
+#include "Caches.h"
+#include "thread/TaskManager.h"
+#include "tests/common/TestUtils.h"
+
+#include <memunreachable/memunreachable.h>
+
+#include <cstdio>
+#include <iostream>
+#include <map>
+#include <unordered_set>
+#include <signal.h>
+#include <unistd.h>
+
+using namespace std;
+using namespace android;
+using namespace android::uirenderer;
+
+static auto CRASH_SIGNALS = {
+ SIGABRT,
+ SIGSEGV,
+ SIGBUS,
+};
+
+static map<int, struct sigaction> gSigChain;
+
+static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) {
+ auto testinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+ printf("[ FAILED ] %s.%s\n", testinfo->test_case_name(),
+ testinfo->name());
+ printf("[ FATAL! ] Process crashed, aborting tests!\n");
+ fflush(stdout);
+
+ // restore the default sighandler and re-raise
+ struct sigaction sa = gSigChain[sig];
+ sigaction(sig, &sa, nullptr);
+ raise(sig);
+}
+
+static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) {
+ // merge them all
+ UnreachableMemoryInfo merged;
+ unordered_set<uintptr_t> addrs;
+ merged.allocation_bytes = 0;
+ merged.leak_bytes = 0;
+ merged.num_allocations = 0;
+ merged.num_leaks = 0;
+ for (auto& info : infolist) {
+ // We'll be a little hazzy about these ones and just hope the biggest
+ // is the most accurate
+ merged.allocation_bytes = max(merged.allocation_bytes, info.allocation_bytes);
+ merged.num_allocations = max(merged.num_allocations, info.num_allocations);
+ for (auto& leak : info.leaks) {
+ if (addrs.find(leak.begin) == addrs.end()) {
+ merged.leaks.push_back(leak);
+ merged.num_leaks++;
+ merged.leak_bytes += leak.size;
+ addrs.insert(leak.begin);
+ }
+ }
+ }
+
+ // Now log the result
+ if (merged.num_leaks) {
+ cout << endl << "Leaked memory!" << endl;
+ if (!merged.leaks[0].backtrace.num_frames) {
+ cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'"
+ << endl << "and 'setprop libc.debug.malloc.options backtrace=8'"
+ << " to get backtraces" << endl;
+ }
+ cout << merged.ToString(false);
+ }
+}
+
+static void checkForLeaks() {
+ // TODO: Until we can shutdown the RT thread we need to do this in
+ // two passes as GetUnreachableMemory has limited insight into
+ // thread-local caches so some leaks will not be properly tagged as leaks
+ nsecs_t before = systemTime();
+ UnreachableMemoryInfo rtMemInfo;
+ TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) {
+ if (Caches::hasInstance()) {
+ Caches::getInstance().tasks.stop();
+ }
+ // Check for leaks
+ if (!GetUnreachableMemory(rtMemInfo)) {
+ cerr << "Failed to get unreachable memory!" << endl;
+ return;
+ }
+ });
+ UnreachableMemoryInfo uiMemInfo;
+ if (!GetUnreachableMemory(uiMemInfo)) {
+ cerr << "Failed to get unreachable memory!" << endl;
+ return;
+ }
+ logUnreachable({rtMemInfo, uiMemInfo});
+ nsecs_t after = systemTime();
+ cout << "Leak check took " << ns2ms(after - before) << "ms" << endl;
+}
+
+int main(int argc, char* argv[]) {
+ // Register a crash handler
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = >estSigHandler;
+ sa.sa_flags = SA_SIGINFO;
+ for (auto sig : CRASH_SIGNALS) {
+ struct sigaction old_sa;
+ sigaction(sig, &sa, &old_sa);
+ gSigChain.insert(pair<int, struct sigaction>(sig, old_sa));
+ }
+
+ // Run the tests
+ testing::InitGoogleTest(&argc, argv);
+ int ret = RUN_ALL_TESTS();
+ checkForLeaks();
+ return ret;
+}
+
diff --git a/location/java/android/location/GnssClock.java b/location/java/android/location/GnssClock.java
index 6a7801e..2af4790 100644
--- a/location/java/android/location/GnssClock.java
+++ b/location/java/android/location/GnssClock.java
@@ -16,6 +16,7 @@
package android.location;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -48,13 +49,19 @@
private double mDriftUncertaintyNanosPerSecond;
private int mHardwareClockDiscontinuityCount;
- GnssClock() {
+ /**
+ * @hide
+ */
+ @TestApi
+ public GnssClock() {
initialize();
}
/**
* Sets all contents to the values stored in the provided object.
+ * @hide
*/
+ @TestApi
public void set(GnssClock clock) {
mFlags = clock.mFlags;
mLeapSecond = clock.mLeapSecond;
@@ -70,7 +77,9 @@
/**
* Resets all the contents to its original state.
+ * @hide
*/
+ @TestApi
public void reset() {
initialize();
}
@@ -95,7 +104,9 @@
/**
* Sets the leap second associated with the clock's time.
+ * @hide
*/
+ @TestApi
public void setLeapSecond(int leapSecond) {
setFlag(HAS_LEAP_SECOND);
mLeapSecond = leapSecond;
@@ -103,7 +114,9 @@
/**
* Resets the leap second associated with the clock's time.
+ * @hide
*/
+ @TestApi
public void resetLeapSecond() {
resetFlag(HAS_LEAP_SECOND);
mLeapSecond = Integer.MIN_VALUE;
@@ -129,7 +142,9 @@
/**
* Sets the GNSS receiver internal clock in nanoseconds.
+ * @hide
*/
+ @TestApi
public void setTimeNanos(long timeNanos) {
mTimeNanos = timeNanos;
}
@@ -153,7 +168,9 @@
/**
* Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
*/
+ @TestApi
public void setTimeUncertaintyNanos(double timeUncertaintyNanos) {
setFlag(HAS_TIME_UNCERTAINTY);
mTimeUncertaintyNanos = timeUncertaintyNanos;
@@ -161,7 +178,9 @@
/**
* Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
*/
+ @TestApi
public void resetTimeUncertaintyNanos() {
resetFlag(HAS_TIME_UNCERTAINTY);
mTimeUncertaintyNanos = Double.NaN;
@@ -193,7 +212,9 @@
/**
* Sets the full bias in nanoseconds.
+ * @hide
*/
+ @TestApi
public void setFullBiasNanos(long value) {
setFlag(HAS_FULL_BIAS);
mFullBiasNanos = value;
@@ -201,7 +222,9 @@
/**
* Resets the full bias in nanoseconds.
+ * @hide
*/
+ @TestApi
public void resetFullBiasNanos() {
resetFlag(HAS_FULL_BIAS);
mFullBiasNanos = Long.MIN_VALUE;
@@ -226,7 +249,9 @@
/**
* Sets the sub-nanosecond bias.
+ * @hide
*/
+ @TestApi
public void setBiasNanos(double biasNanos) {
setFlag(HAS_BIAS);
mBiasNanos = biasNanos;
@@ -234,7 +259,9 @@
/**
* Resets the clock's Bias in nanoseconds.
+ * @hide
*/
+ @TestApi
public void resetBiasNanos() {
resetFlag(HAS_BIAS);
mBiasNanos = Double.NaN;
@@ -258,7 +285,9 @@
/**
* Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
*/
+ @TestApi
public void setBiasUncertaintyNanos(double biasUncertaintyNanos) {
setFlag(HAS_BIAS_UNCERTAINTY);
mBiasUncertaintyNanos = biasUncertaintyNanos;
@@ -266,7 +295,9 @@
/**
* Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
*/
+ @TestApi
public void resetBiasUncertaintyNanos() {
resetFlag(HAS_BIAS_UNCERTAINTY);
mBiasUncertaintyNanos = Double.NaN;
@@ -292,7 +323,9 @@
/**
* Sets the clock's Drift in nanoseconds per second.
+ * @hide
*/
+ @TestApi
public void setDriftNanosPerSecond(double driftNanosPerSecond) {
setFlag(HAS_DRIFT);
mDriftNanosPerSecond = driftNanosPerSecond;
@@ -300,7 +333,9 @@
/**
* Resets the clock's Drift in nanoseconds per second.
+ * @hide
*/
+ @TestApi
public void resetDriftNanosPerSecond() {
resetFlag(HAS_DRIFT);
mDriftNanosPerSecond = Double.NaN;
@@ -324,7 +359,9 @@
/**
* Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+ * @hide
*/
+ @TestApi
public void setDriftUncertaintyNanosPerSecond(double driftUncertaintyNanosPerSecond) {
setFlag(HAS_DRIFT_UNCERTAINTY);
mDriftUncertaintyNanosPerSecond = driftUncertaintyNanosPerSecond;
@@ -339,14 +376,18 @@
/**
* Sets count of last hardware clock discontinuity.
+ * @hide
*/
+ @TestApi
public void setHardwareClockDiscontinuityCount(int value) {
mHardwareClockDiscontinuityCount = value;
}
/**
* Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+ * @hide
*/
+ @TestApi
public void resetDriftUncertaintyNanosPerSecond() {
resetFlag(HAS_DRIFT_UNCERTAINTY);
mDriftUncertaintyNanosPerSecond = Double.NaN;
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 367c52f..11fecfb 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -16,6 +16,7 @@
package android.location;
+import android.annotation.TestApi;
import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
@@ -46,6 +47,7 @@
private double mCarrierPhaseUncertainty;
private int mMultipathIndicator;
private double mSnrInDb;
+ private boolean mPseudorangeRateCorrected;
// The following enumerations must be in sync with the values declared in gps.h
@@ -55,7 +57,6 @@
private static final int HAS_CARRIER_CYCLES = (1<<10);
private static final int HAS_CARRIER_PHASE = (1<<11);
private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
- private static final int HAS_UNCORRECTED_PSEUDORANGE_RATE = (1<<18);
/** The status of multipath. */
@Retention(RetentionPolicy.SOURCE)
@@ -141,13 +142,19 @@
// End enumerations in sync with gps.h
- GnssMeasurement() {
+ /**
+ * @hide
+ */
+ @TestApi
+ public GnssMeasurement() {
initialize();
}
/**
* Sets all contents to the values stored in the provided object.
+ * @hide
*/
+ @TestApi
public void set(GnssMeasurement measurement) {
mFlags = measurement.mFlags;
mSvid = measurement.mSvid;
@@ -174,7 +181,9 @@
/**
* Resets all the contents to its original state.
+ * @hide
*/
+ @TestApi
public void reset() {
initialize();
}
@@ -189,7 +198,9 @@
/**
* Sets the Pseud-random number (PRN).
+ * @hide
*/
+ @TestApi
public void setSvid(int value) {
mSvid = value;
}
@@ -204,7 +215,9 @@
/**
* Sets the constellation type.
+ * @hide
*/
+ @TestApi
public void setConstellationType(@GnssStatus.ConstellationType int value) {
mConstellationType = value;
}
@@ -227,7 +240,9 @@
/**
* Sets the time offset at which the measurement was taken in nanoseconds.
+ * @hide
*/
+ @TestApi
public void setTimeOffsetNanos(double value) {
mTimeOffsetNanos = value;
}
@@ -244,7 +259,9 @@
/**
* Sets the sync state.
+ * @hide
*/
+ @TestApi
public void setState(int value) {
mState = value;
}
@@ -353,7 +370,9 @@
/**
* Sets the received GNSS time in nanoseconds.
+ * @hide
*/
+ @TestApi
public void setReceivedSvTimeNanos(long value) {
mReceivedSvTimeNanos = value;
}
@@ -367,7 +386,9 @@
/**
* Sets the received GNSS time uncertainty (1-Sigma) in nanoseconds.
+ * @hide
*/
+ @TestApi
public void setReceivedSvTimeUncertaintyNanos(long value) {
mReceivedSvTimeUncertaintyNanos = value;
}
@@ -384,7 +405,9 @@
/**
* Sets the carrier-to-noise density in dB-Hz.
+ * @hide
*/
+ @TestApi
public void setCn0DbHz(double value) {
mCn0DbHz = value;
}
@@ -409,7 +432,9 @@
/**
* Sets the pseudorange rate at the timestamp in m/s.
+ * @hide
*/
+ @TestApi
public void setPseudorangeRateMetersPerSecond(double value) {
mPseudorangeRateMetersPerSecond = value;
}
@@ -421,7 +446,16 @@
* value, {@code false} if it contains an uncorrected value.
*/
public boolean isPseudorangeRateCorrected() {
- return !isFlagSet(HAS_UNCORRECTED_PSEUDORANGE_RATE);
+ return mPseudorangeRateCorrected;
+ }
+
+ /**
+ * Sets whether the pseudorange corrected.
+ * @hide
+ */
+ @TestApi
+ public void setPseudorangeRateCorrected(boolean value) {
+ mPseudorangeRateCorrected = value;
}
/**
@@ -434,7 +468,9 @@
/**
* Sets the pseudorange's rate uncertainty (1-Sigma) in m/s.
+ * @hide
*/
+ @TestApi
public void setPseudorangeRateUncertaintyMetersPerSecond(double value) {
mPseudorangeRateUncertaintyMetersPerSecond = value;
}
@@ -450,7 +486,9 @@
/**
* Sets the 'Accumulated Delta Range' state.
+ * @hide
*/
+ @TestApi
public void setAccumulatedDeltaRangeState(int value) {
mAccumulatedDeltaRangeState = value;
}
@@ -500,7 +538,9 @@
/**
* Sets the accumulated delta range in meters.
+ * @hide
*/
+ @TestApi
public void setAccumulatedDeltaRangeMeters(double value) {
mAccumulatedDeltaRangeMeters = value;
}
@@ -519,7 +559,10 @@
* Sets the accumulated delta range's uncertainty (1-sigma) in meters.
*
* The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+ *
+ * @hide
*/
+ @TestApi
public void setAccumulatedDeltaRangeUncertaintyMeters(double value) {
mAccumulatedDeltaRangeUncertaintyMeters = value;
}
@@ -543,7 +586,9 @@
/**
* Sets the Carrier frequency (L1 or L2) in Hz.
+ * @hide
*/
+ @TestApi
public void setCarrierFrequencyHz(float carrierFrequencyHz) {
setFlag(HAS_CARRIER_FREQUENCY);
mCarrierFrequencyHz = carrierFrequencyHz;
@@ -551,7 +596,9 @@
/**
* Resets the Carrier frequency (L1 or L2) in Hz.
+ * @hide
*/
+ @TestApi
public void resetCarrierFrequencyHz() {
resetFlag(HAS_CARRIER_FREQUENCY);
mCarrierFrequencyHz = Float.NaN;
@@ -576,7 +623,9 @@
/**
* Sets the number of full carrier cycles between the satellite and the receiver.
+ * @hide
*/
+ @TestApi
public void setCarrierCycles(long value) {
setFlag(HAS_CARRIER_CYCLES);
mCarrierCycles = value;
@@ -584,7 +633,9 @@
/**
* Resets the number of full carrier cycles between the satellite and the receiver.
+ * @hide
*/
+ @TestApi
public void resetCarrierCycles() {
resetFlag(HAS_CARRIER_CYCLES);
mCarrierCycles = Long.MIN_VALUE;
@@ -613,7 +664,9 @@
/**
* Sets the RF phase detected by the receiver.
+ * @hide
*/
+ @TestApi
public void setCarrierPhase(double value) {
setFlag(HAS_CARRIER_PHASE);
mCarrierPhase = value;
@@ -621,7 +674,9 @@
/**
* Resets the RF phase detected by the receiver.
+ * @hide
*/
+ @TestApi
public void resetCarrierPhase() {
resetFlag(HAS_CARRIER_PHASE);
mCarrierPhase = Double.NaN;
@@ -646,7 +701,9 @@
/**
* Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ * @hide
*/
+ @TestApi
public void setCarrierPhaseUncertainty(double value) {
setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
mCarrierPhaseUncertainty = value;
@@ -654,7 +711,9 @@
/**
* Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ * @hide
*/
+ @TestApi
public void resetCarrierPhaseUncertainty() {
resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
mCarrierPhaseUncertainty = Double.NaN;
@@ -670,7 +729,9 @@
/**
* Sets the 'multi-path' indicator.
+ * @hide
*/
+ @TestApi
public void setMultipathIndicator(@MultipathIndicator int value) {
mMultipathIndicator = value;
}
@@ -710,7 +771,9 @@
/**
* Sets the Signal-to-noise ratio (SNR) in dB.
+ * @hide
*/
+ @TestApi
public void setSnrInDb(double snrInDb) {
setFlag(HAS_SNR);
mSnrInDb = snrInDb;
@@ -718,7 +781,9 @@
/**
* Resets the Signal-to-noise ratio (SNR) in dB.
+ * @hide
*/
+ @TestApi
public void resetSnrInDb() {
resetFlag(HAS_SNR);
mSnrInDb = Double.NaN;
@@ -748,6 +813,7 @@
gnssMeasurement.mCarrierPhaseUncertainty = parcel.readDouble();
gnssMeasurement.mMultipathIndicator = parcel.readInt();
gnssMeasurement.mSnrInDb = parcel.readDouble();
+ gnssMeasurement.mPseudorangeRateCorrected = (parcel.readByte() != 0);
return gnssMeasurement;
}
@@ -779,6 +845,7 @@
parcel.writeDouble(mCarrierPhaseUncertainty);
parcel.writeInt(mMultipathIndicator);
parcel.writeDouble(mSnrInDb);
+ parcel.writeByte((byte) (mPseudorangeRateCorrected ? 1 : 0));
}
@Override
@@ -876,6 +943,7 @@
resetCarrierPhaseUncertainty();
setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
resetSnrInDb();
+ setPseudorangeRateCorrected(false);
}
private void setFlag(int flag) {
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index c0608e0..ac255c8 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -16,6 +16,7 @@
package android.location;
+import android.annotation.TestApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
@@ -86,13 +87,19 @@
private byte[] mData;
private int mStatus;
- GnssNavigationMessage() {
+ /**
+ * @hide
+ */
+ @TestApi
+ public GnssNavigationMessage() {
initialize();
}
/**
* Sets all contents to the values stored in the provided object.
+ * @hide
*/
+ @TestApi
public void set(GnssNavigationMessage navigationMessage) {
mType = navigationMessage.mType;
mSvid = navigationMessage.mSvid;
@@ -104,7 +111,9 @@
/**
* Resets all the contents to its original state.
+ * @hide
*/
+ @TestApi
public void reset() {
initialize();
}
@@ -119,7 +128,9 @@
/**
* Sets the type of the navigation message.
+ * @hide
*/
+ @TestApi
public void setType(@GnssNavigationMessageType int value) {
mType = value;
}
@@ -165,7 +176,9 @@
/**
* Sets the Pseud-random number.
+ * @hide
*/
+ @TestApi
public void setSvid(int value) {
mSvid = value;
}
@@ -182,7 +195,9 @@
/**
* Sets the Message Identifier.
+ * @hide
*/
+ @TestApi
public void setMessageId(int value) {
mMessageId = value;
}
@@ -199,7 +214,9 @@
/**
* Sets the Sub-message identifier.
+ * @hide
*/
+ @TestApi
public void setSubmessageId(int value) {
mSubmessageId = value;
}
@@ -215,7 +232,9 @@
/**
* Sets the data associated with the Navigation Message.
+ * @hide
*/
+ @TestApi
public void setData(byte[] value) {
if (value == null) {
throw new InvalidParameterException("Data must be a non-null array");
@@ -233,7 +252,9 @@
/**
* Sets the status of the navigation message.
+ * @hide
*/
+ @TestApi
public void setStatus(int value) {
mStatus = value;
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index c7e96cf..69d4487 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2147,9 +2147,10 @@
}
break;
case MSSG_RECORDING_CONFIG_CHANGE:
- final AudioRecordingCallback cb = (AudioRecordingCallback) msg.obj;
- if (cb != null) {
- cb.onRecordConfigChanged();
+ final RecordConfigChangeCallbackData cbData =
+ (RecordConfigChangeCallbackData) msg.obj;
+ if (cbData.mCb != null) {
+ cbData.mCb.onRecordConfigChanged(cbData.mConfigs);
}
break;
default:
@@ -2729,13 +2730,16 @@
* this abstract class and register it with
* {@link AudioManager#registerAudioRecordingCallback(AudioRecordingCallback, Handler)}
* to be notified.
- * Use {@link AudioManager#getActiveRecordConfigurations()} to query the current configuration.
+ * Use {@link AudioManager#getActiveRecordingConfigurations()} to query the current
+ * configuration.
*/
public static abstract class AudioRecordingCallback {
/**
* Called whenever the device recording configuration has changed.
+ * @param configs array containing the results of
+ * {@link AudioManager#getActiveRecordingConfigurations()}.
*/
- public void onRecordConfigChanged() {}
+ public void onRecordConfigChanged(AudioRecordingConfiguration[] configs) {}
}
private static class AudioRecordingCallbackInfo {
@@ -2747,6 +2751,17 @@
}
}
+ private final static class RecordConfigChangeCallbackData {
+ final AudioRecordingCallback mCb;
+ final AudioRecordingConfiguration[] mConfigs;
+
+ RecordConfigChangeCallbackData(AudioRecordingCallback cb,
+ AudioRecordingConfiguration[] configs) {
+ mCb = cb;
+ mConfigs = configs;
+ }
+ }
+
/**
* Register a callback to be notified of audio recording changes through
* {@link AudioRecordingCallback}
@@ -2824,10 +2839,10 @@
* @return a non-null array of recording configurations. An array of length 0 indicates there is
* no recording active when queried.
*/
- public @NonNull AudioRecordConfiguration[] getActiveRecordConfigurations() {
+ public @NonNull AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
final IAudioService service = getService();
try {
- return service.getActiveRecordConfigurations();
+ return service.getActiveRecordingConfigurations();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2882,14 +2897,15 @@
private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
- public void dispatchRecordingConfigChange() {
+ public void dispatchRecordingConfigChange(AudioRecordingConfiguration[] configs) {
synchronized(mRecordCallbackLock) {
if (mRecordCallbackList != null) {
for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
final AudioRecordingCallbackInfo arci = mRecordCallbackList.get(i);
if (arci.mHandler != null) {
final Message m = arci.mHandler.obtainMessage(
- MSSG_RECORDING_CONFIG_CHANGE/*what*/, arci.mCb/*obj*/);
+ MSSG_RECORDING_CONFIG_CHANGE/*what*/,
+ new RecordConfigChangeCallbackData(arci.mCb, configs)/*obj*/);
arci.mHandler.sendMessage(m);
}
}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index b8f0717..0f82cfc 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -394,30 +394,48 @@
* value here as no error checking is or can be done.
*/
/*package*/ AudioRecord(long nativeRecordInJavaObj) {
- int[] session = { 0 };
- int[] rates = { 0 };
- //TODO: update native initialization when information about hardware init failure
- // due to capture device already open is available.
- // Note that for this native_setup, we are providing an already created/initialized
- // *Native* AudioRecord, so the attributes parameters to native_setup() are ignored.
- int initResult = native_setup(new WeakReference<AudioRecord>(this),
- null /*mAudioAttributes*/,
- rates /*mSampleRates*/,
- 0 /*mChannelMask*/,
- 0 /*mChannelIndexMask*/,
- 0 /*mAudioFormat*/,
- 0 /*mNativeBufferSizeInBytes*/,
- session,
- ActivityThread.currentOpPackageName(),
- nativeRecordInJavaObj);
- if (initResult != SUCCESS) {
- loge("Error code "+initResult+" when initializing native AudioRecord object.");
- return; // with mState == STATE_UNINITIALIZED
+ mNativeRecorderInJavaObj = 0;
+ mNativeCallbackCookie = 0;
+ mNativeDeviceCallback = 0;
+
+ // other initialization...
+ if (nativeRecordInJavaObj != 0) {
+ deferred_connect(nativeRecordInJavaObj);
+ } else {
+ mState = STATE_UNINITIALIZED;
}
+ }
- mSessionId = session[0];
+ /**
+ * @hide
+ */
+ /* package */ void deferred_connect(long nativeRecordInJavaObj) {
+ if (mState != STATE_INITIALIZED) {
+ int[] session = { 0 };
+ int[] rates = { 0 };
+ //TODO: update native initialization when information about hardware init failure
+ // due to capture device already open is available.
+ // Note that for this native_setup, we are providing an already created/initialized
+ // *Native* AudioRecord, so the attributes parameters to native_setup() are ignored.
+ int initResult = native_setup(new WeakReference<AudioRecord>(this),
+ null /*mAudioAttributes*/,
+ rates /*mSampleRates*/,
+ 0 /*mChannelMask*/,
+ 0 /*mChannelIndexMask*/,
+ 0 /*mAudioFormat*/,
+ 0 /*mNativeBufferSizeInBytes*/,
+ session,
+ ActivityThread.currentOpPackageName(),
+ nativeRecordInJavaObj);
+ if (initResult != SUCCESS) {
+ loge("Error code "+initResult+" when initializing native AudioRecord object.");
+ return; // with mState == STATE_UNINITIALIZED
+ }
- mState = STATE_INITIALIZED;
+ mSessionId = session[0];
+
+ mState = STATE_INITIALIZED;
+ }
}
/**
@@ -1738,7 +1756,10 @@
// TODO remove: implementation calls directly into implementation of native_release()
private native final void native_finalize();
- private native final void native_release();
+ /**
+ * @hide
+ */
+ public native final void native_release();
private native final int native_start(int syncEvent, int sessionId);
diff --git a/media/java/android/media/AudioRecordConfiguration.aidl b/media/java/android/media/AudioRecordingConfiguration.aidl
similarity index 93%
rename from media/java/android/media/AudioRecordConfiguration.aidl
rename to media/java/android/media/AudioRecordingConfiguration.aidl
index afe912b..c63d30b 100644
--- a/media/java/android/media/AudioRecordConfiguration.aidl
+++ b/media/java/android/media/AudioRecordingConfiguration.aidl
@@ -15,4 +15,4 @@
package android.media;
-parcelable AudioRecordConfiguration;
+parcelable AudioRecordingConfiguration;
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
similarity index 73%
rename from media/java/android/media/AudioRecordConfiguration.java
rename to media/java/android/media/AudioRecordingConfiguration.java
index 2fc8ee8..cd6f95a 100644
--- a/media/java/android/media/AudioRecordConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -16,21 +16,24 @@
package android.media;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Objects;
/**
- * The AudioRecordConfiguration class collects the information describing an audio recording
+ * The AudioRecordingConfiguration class collects the information describing an audio recording
* session. This information is returned through the
- * {@link AudioManager#getActiveRecordConfigurations()} method.
+ * {@link AudioManager#getActiveRecordingConfigurations()} method.
*
*/
-public final class AudioRecordConfiguration implements Parcelable {
- private final static String TAG = new String("AudioRecordConfiguration");
+public final class AudioRecordingConfiguration implements Parcelable {
+ private final static String TAG = new String("AudioRecordingConfiguration");
private final int mSessionId;
@@ -44,7 +47,7 @@
/**
* @hide
*/
- public AudioRecordConfiguration(int session, int source, AudioFormat devFormat,
+ public AudioRecordingConfiguration(int session, int source, AudioFormat devFormat,
AudioFormat clientFormat, int patchHandle) {
mSessionId = session;
mClientSource = source;
@@ -53,6 +56,19 @@
mPatchHandle = patchHandle;
}
+ /** @hide */
+ @IntDef({
+ MediaRecorder.AudioSource.DEFAULT,
+ MediaRecorder.AudioSource.VOICE_UPLINK,
+ MediaRecorder.AudioSource.VOICE_DOWNLINK,
+ MediaRecorder.AudioSource.VOICE_CALL,
+ MediaRecorder.AudioSource.CAMCORDER,
+ MediaRecorder.AudioSource.VOICE_RECOGNITION,
+ MediaRecorder.AudioSource.VOICE_COMMUNICATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioSource {}
+
/**
* Returns the audio source being used for the recording.
* @return one of {@link MediaRecorder.AudioSource#MIC},
@@ -63,7 +79,7 @@
* {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
* {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION}.
*/
- public int getClientAudioSource() { return mClientSource; }
+ public @AudioSource int getClientAudioSource() { return mClientSource; }
/**
* Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
@@ -120,18 +136,18 @@
return null;
}
- public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR
- = new Parcelable.Creator<AudioRecordConfiguration>() {
+ public static final Parcelable.Creator<AudioRecordingConfiguration> CREATOR
+ = new Parcelable.Creator<AudioRecordingConfiguration>() {
/**
- * Rebuilds an AudioRecordConfiguration previously stored with writeToParcel().
- * @param p Parcel object to read the AudioRecordConfiguration from
- * @return a new AudioRecordConfiguration created from the data in the parcel
+ * Rebuilds an AudioRecordingConfiguration previously stored with writeToParcel().
+ * @param p Parcel object to read the AudioRecordingConfiguration from
+ * @return a new AudioRecordingConfiguration created from the data in the parcel
*/
- public AudioRecordConfiguration createFromParcel(Parcel p) {
- return new AudioRecordConfiguration(p);
+ public AudioRecordingConfiguration createFromParcel(Parcel p) {
+ return new AudioRecordingConfiguration(p);
}
- public AudioRecordConfiguration[] newArray(int size) {
- return new AudioRecordConfiguration[size];
+ public AudioRecordingConfiguration[] newArray(int size) {
+ return new AudioRecordingConfiguration[size];
}
};
@@ -154,7 +170,7 @@
dest.writeInt(mPatchHandle);
}
- private AudioRecordConfiguration(Parcel in) {
+ private AudioRecordingConfiguration(Parcel in) {
mSessionId = in.readInt();
mClientSource = in.readInt();
mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
@@ -165,9 +181,9 @@
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || !(o instanceof AudioRecordConfiguration)) return false;
+ if (o == null || !(o instanceof AudioRecordingConfiguration)) return false;
- AudioRecordConfiguration that = (AudioRecordConfiguration) o;
+ AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
return ((mSessionId == that.mSessionId)
&& (mClientSource == that.mClientSource)
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index c48bfc5..c5d1120 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -526,11 +526,18 @@
* the AudioTrackRoutingProxy subclass.
* @param nativeTrackInJavaObj a C/C++ pointer to a native AudioTrack
* (associated with an OpenSL ES player).
+ * IMPORTANT: For "N", this method is ONLY called to setup a Java routing proxy,
+ * i.e. IAndroidConfiguration::AcquireJavaProxy(). If we call with a 0 in nativeTrackInJavaObj
+ * it means that the OpenSL player interface hasn't been realized, so there is no native
+ * Audiotrack to connect to. In this case wait to call deferred_connect() until the
+ * OpenSLES interface is realized.
*/
/*package*/ AudioTrack(long nativeTrackInJavaObj) {
// "final"s
mAttributes = null;
mAppOps = null;
+ mNativeTrackInJavaObj = 0;
+ mJniData = 0;
// remember which looper is associated with the AudioTrack instantiation
Looper looper;
@@ -540,28 +547,41 @@
mInitializationLooper = looper;
// other initialization...
- // Note that for this native_setup, we are providing an already created/initialized
- // *Native* AudioTrack, so the attributes parameters to native_setup() are ignored.
- int[] session = { 0 };
- int[] rates = { 0 };
- int initResult = native_setup(new WeakReference<AudioTrack>(this),
- null /*mAttributes - NA*/,
- rates /*sampleRate - NA*/,
- 0 /*mChannelMask - NA*/,
- 0 /*mChannelIndexMask - NA*/,
- 0 /*mAudioFormat - NA*/,
- 0 /*mNativeBufferSizeInBytes - NA*/,
- 0 /*mDataLoadMode - NA*/,
- session,
- nativeTrackInJavaObj);
- if (initResult != SUCCESS) {
- loge("Error code "+initResult+" when initializing AudioTrack.");
- return; // with mState == STATE_UNINITIALIZED
+ if (nativeTrackInJavaObj != 0) {
+ deferred_connect(nativeTrackInJavaObj);
+ } else {
+ mState = STATE_UNINITIALIZED;
}
+ }
- mSessionId = session[0];
+ /**
+ * @hide
+ */
+ /* package */ void deferred_connect(long nativeTrackInJavaObj) {
+ if (mState != STATE_INITIALIZED) {
+ // Note that for this native_setup, we are providing an already created/initialized
+ // *Native* AudioTrack, so the attributes parameters to native_setup() are ignored.
+ int[] session = { 0 };
+ int[] rates = { 0 };
+ int initResult = native_setup(new WeakReference<AudioTrack>(this),
+ null /*mAttributes - NA*/,
+ rates /*sampleRate - NA*/,
+ 0 /*mChannelMask - NA*/,
+ 0 /*mChannelIndexMask - NA*/,
+ 0 /*mAudioFormat - NA*/,
+ 0 /*mNativeBufferSizeInBytes - NA*/,
+ 0 /*mDataLoadMode - NA*/,
+ session,
+ nativeTrackInJavaObj);
+ if (initResult != SUCCESS) {
+ loge("Error code "+initResult+" when initializing AudioTrack.");
+ return; // with mState == STATE_UNINITIALIZED
+ }
- mState = STATE_INITIALIZED;
+ mSessionId = session[0];
+
+ mState = STATE_INITIALIZED;
+ }
}
/**
@@ -2794,7 +2814,10 @@
private native final void native_finalize();
- private native final void native_release();
+ /**
+ * @hide
+ */
+ public native final void native_release();
private native final void native_start();
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 1a387be..e35fcf6 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -16,14 +16,16 @@
package android.media;
+import android.annotation.NonNull;
+import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
-import android.util.Pair;
+import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -44,6 +46,7 @@
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.TimeZone;
import java.util.regex.Pattern;
@@ -62,89 +65,273 @@
private static final boolean DEBUG = false;
// The Exif tag names
+ /** Type is String. */
+ public static final String TAG_ARTIST = "Artist";
/** Type is int. */
- public static final String TAG_ORIENTATION = "Orientation";
+ public static final String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+ /** Type is int. */
+ public static final String TAG_COMPRESSION = "Compression";
+ /** Type is String. */
+ public static final String TAG_COPYRIGHT = "Copyright";
/** Type is String. */
public static final String TAG_DATETIME = "DateTime";
/** Type is String. */
+ public static final String TAG_IMAGE_DESCRIPTION = "ImageDescription";
+ /** Type is int. */
+ public static final String TAG_IMAGE_LENGTH = "ImageLength";
+ /** Type is int. */
+ public static final String TAG_IMAGE_WIDTH = "ImageWidth";
+ /** Type is int. */
+ public static final String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+ /** Type is int. */
+ public static final String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
+ /** Type is String. */
public static final String TAG_MAKE = "Make";
/** Type is String. */
public static final String TAG_MODEL = "Model";
/** Type is int. */
- public static final String TAG_FLASH = "Flash";
+ public static final String TAG_ORIENTATION = "Orientation";
/** Type is int. */
- public static final String TAG_IMAGE_WIDTH = "ImageWidth";
+ public static final String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
/** Type is int. */
- public static final String TAG_IMAGE_LENGTH = "ImageLength";
- /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
- public static final String TAG_GPS_LATITUDE = "GPSLatitude";
- /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
- public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
+ public static final String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+ /** Type is rational. */
+ public static final String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+ /** Type is rational. */
+ public static final String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+ /** Type is int. */
+ public static final String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+ /** Type is int. */
+ public static final String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+ /** Type is int. */
+ public static final String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
/** Type is String. */
- public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+ public static final String TAG_SOFTWARE = "Software";
+ /** Type is int. */
+ public static final String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+ /** Type is int. */
+ public static final String TAG_STRIP_OFFSETS = "StripOffsets";
+ /** Type is int. */
+ public static final String TAG_TRANSFER_FUNCTION = "TransferFunction";
+ /** Type is rational. */
+ public static final String TAG_WHITE_POINT = "WhitePoint";
+ /** Type is rational. */
+ public static final String TAG_X_RESOLUTION = "XResolution";
+ /** Type is rational. */
+ public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+ /** Type is int. */
+ public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+ /** Type is int. */
+ public static final String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+ /** Type is rational. */
+ public static final String TAG_Y_RESOLUTION = "YResolution";
+ /** Type is rational. */
+ public static final String TAG_APERTURE_VALUE = "ApertureValue";
+ /** Type is rational. */
+ public static final String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
/** Type is String. */
- public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ public static final String TAG_CFA_PATTERN = "CFAPattern";
+ /** Type is int. */
+ public static final String TAG_COLOR_SPACE = "ColorSpace";
/** Type is String. */
+ public static final String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+ /** Type is rational. */
+ public static final String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+ /** Type is int. */
+ public static final String TAG_CONTRAST = "Contrast";
+ /** Type is int. */
+ public static final String TAG_CUSTOM_RENDERED = "CustomRendered";
+ /** Type is String. */
+ public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ /** Type is String. */
+ public static final String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+ /** Type is String. */
+ public static final String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
+ /** Type is double. */
+ public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+ /** Type is String. */
+ public static final String TAG_EXIF_VERSION = "ExifVersion";
+ /** Type is double. */
+ public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ /** Type is rational. */
+ public static final String TAG_EXPOSURE_INDEX = "ExposureIndex";
+ /** Type is int. */
+ public static final String TAG_EXPOSURE_MODE = "ExposureMode";
+ /** Type is int. */
+ public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
+ /** Type is double. */
public static final String TAG_EXPOSURE_TIME = "ExposureTime";
/** Type is String. */
+ public static final String TAG_F_NUMBER = "FNumber";
+ /** Type is String. */
public static final String TAG_APERTURE = "FNumber";
/** Type is String. */
+ public static final String TAG_FILE_SOURCE = "FileSource";
+ /** Type is int. */
+ public static final String TAG_FLASH = "Flash";
+ /** Type is rational. */
+ public static final String TAG_FLASH_ENERGY = "FlashEnergy";
+ /** Type is String. */
+ public static final String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+ /** Type is rational. */
+ public static final String TAG_FOCAL_LENGTH = "FocalLength";
+ /** Type is int. */
+ public static final String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+ /** Type is int. */
+ public static final String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+ /** Type is rational. */
+ public static final String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+ /** Type is rational. */
+ public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+ /** Type is rational. */
+ public static final String TAG_GAIN_CONTROL = "GainControl";
+ /** Type is String. */
+ public static final String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+ /** Type is String. */
public static final String TAG_ISO = "ISOSpeedRatings";
/** Type is String. */
- public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+ public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
+ /** Type is int. */
+ public static final String TAG_LIGHT_SOURCE = "LightSource";
+ /** Type is String. */
+ public static final String TAG_MAKER_NOTE = "MakerNote";
+ /** Type is rational. */
+ public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
+ /** Type is int. */
+ public static final String TAG_METERING_MODE = "MeteringMode";
+ /** Type is String. */
+ public static final String TAG_OECF = "OECF";
+ /** Type is int. */
+ public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+ /** Type is int. */
+ public static final String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+ /** Type is String. */
+ public static final String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+ /** Type is int. */
+ public static final String TAG_SATURATION = "Saturation";
+ /** Type is int. */
+ public static final String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+ /** Type is String. */
+ public static final String TAG_SCENE_TYPE = "SceneType";
+ /** Type is int. */
+ public static final String TAG_SENSING_METHOD = "SensingMethod";
+ /** Type is int. */
+ public static final String TAG_SHARPNESS = "Sharpness";
+ /** Type is rational. */
+ public static final String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+ /** Type is String. */
+ public static final String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+ /** Type is String. */
+ public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
/** Type is int. */
public static final String TAG_SUBSEC_TIME = "SubSecTime";
+ /** Type is int. @hide */
+ public static final String TAG_SUBSECTIME = "SubSecTime";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
+ /** Type is int. */
+ public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
/** Type is int. */
public static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
/** Type is int. */
- public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
-
- /**
- * @hide
- */
- public static final String TAG_SUBSECTIME = "SubSecTime";
-
+ public static final String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+ /** Type is int. */
+ public static final String TAG_SUBJECT_AREA = "SubjectArea";
+ /** Type is double. */
+ public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+ /** Type is int. */
+ public static final String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+ /** Type is int. */
+ public static final String TAG_SUBJECT_LOCATION = "SubjectLocation";
+ /** Type is String. */
+ public static final String TAG_USER_COMMENT = "UserComment";
+ /** Type is int. */
+ public static final String TAG_WHITE_BALANCE = "WhiteBalance";
/**
* The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF.
* Type is rational.
*/
public static final String TAG_GPS_ALTITUDE = "GPSAltitude";
-
/**
* 0 if the altitude is above sea level. 1 if the altitude is below sea
* level. Type is int.
*/
public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
-
/** Type is String. */
- public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ public static final String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
+ /** Type is rational. */
+ public static final String TAG_GPS_DOP = "GPSDOP";
/** Type is String. */
public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
- /** Type is int. */
- public static final String TAG_WHITE_BALANCE = "WhiteBalance";
/** Type is rational. */
- public static final String TAG_FOCAL_LENGTH = "FocalLength";
+ public static final String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+ /** Type is String. */
+ public static final String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+ /** Type is rational. */
+ public static final String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+ /** Type is String. */
+ public static final String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+ /** Type is rational. */
+ public static final String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+ /** Type is String. */
+ public static final String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+ /** Type is rational. */
+ public static final String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+ /** Type is String. */
+ public static final String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+ /** Type is int. */
+ public static final String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+ /** Type is rational. */
+ public static final String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+ /** Type is String. */
+ public static final String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
+ /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
+ public static final String TAG_GPS_LATITUDE = "GPSLatitude";
+ /** Type is String. */
+ public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+ /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
+ public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
+ /** Type is String. */
+ public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ /** Type is String. */
+ public static final String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+ /** Type is String. */
+ public static final String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
/** Type is String. Name of GPS processing method used for location finding. */
public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
- /** Type is double. */
- public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
- /** Type is double. */
- public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
- /** Type is double. */
- public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+ /** Type is String. */
+ public static final String TAG_GPS_SATELLITES = "GPSSatellites";
+ /** Type is rational. */
+ public static final String TAG_GPS_SPEED = "GPSSpeed";
+ /** Type is String. */
+ public static final String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+ /** Type is String. */
+ public static final String TAG_GPS_STATUS = "GPSStatus";
+ /** Type is String. */
+ public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ /** Type is rational. */
+ public static final String TAG_GPS_TRACK = "GPSTrack";
+ /** Type is String. */
+ public static final String TAG_GPS_TRACK_REF = "GPSTrackRef";
+ /** Type is String. */
+ public static final String TAG_GPS_VERSION_ID = "GPSVersionID";
+ /** Type is String. */
+ public static final String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
/** Type is int. */
- public static final String TAG_LIGHT_SOURCE = "LightSource";
+ public static final String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
/** Type is int. */
- public static final String TAG_METERING_MODE = "MeteringMode";
- /** Type is int. */
- public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
- /** Type is int. */
- public static final String TAG_EXPOSURE_MODE = "ExposureMode";
+ public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+
+ // Private tags used for pointing the other IFD offset. The types of the following tags are int.
+ private static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer";
+ private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
+ private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
// Private tags used for thumbnail information.
private static final String TAG_HAS_THUMBNAIL = "hasThumbnail";
private static final String TAG_THUMBNAIL_OFFSET = "thumbnailOffset";
private static final String TAG_THUMBNAIL_LENGTH = "thumbnailLength";
+ private static final String TAG_THUMBNAIL_DATA = "thumbnailData";
// Constants used for the Orientation Exif tag.
public static final int ORIENTATION_UNDEFINED = 0;
@@ -163,6 +350,9 @@
public static final int WHITEBALANCE_AUTO = 0;
public static final int WHITEBALANCE_MANUAL = 1;
+ private static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff};
+ private static final int JPEG_SIGNATURE_SIZE = 3;
+
private static SimpleDateFormat sFormatter;
// See Exchangeable image file format for digital still cameras: Exif version 2.2.
@@ -208,171 +398,171 @@
// Primary image IFD TIFF tags (See JEITA CP-3451 Table 14. page 54).
private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[] {
- new ExifTag("ImageWidth", 256),
- new ExifTag("ImageLength", 257),
- new ExifTag("BitsPerSample", 258),
- new ExifTag("Compression", 259),
- new ExifTag("PhotometricInterpretation", 262),
- new ExifTag("ImageDescription", 270),
- new ExifTag("Make", 271),
- new ExifTag("Model", 272),
- new ExifTag("StripOffsets", 273),
- new ExifTag("Orientation", 274),
- new ExifTag("SamplesPerPixel", 277),
- new ExifTag("RowsPerStrip", 278),
- new ExifTag("StripByteCounts", 279),
- new ExifTag("XResolution", 282),
- new ExifTag("YResolution", 283),
- new ExifTag("PlanarConfiguration", 284),
- new ExifTag("ResolutionUnit", 296),
- new ExifTag("TransferFunction", 301),
- new ExifTag("Software", 305),
- new ExifTag("DateTime", 306),
- new ExifTag("Artist", 315),
- new ExifTag("WhitePoint", 318),
- new ExifTag("PrimaryChromaticities", 319),
- new ExifTag("JPEGInterchangeFormat", 513),
- new ExifTag("JPEGInterchangeFormatLength", 514),
- new ExifTag("YCbCrCoefficients", 529),
- new ExifTag("YCbCrSubSampling", 530),
- new ExifTag("YCbCrPositioning", 531),
- new ExifTag("ReferenceBlackWhite", 532),
- new ExifTag("Copyright", 33432),
- new ExifTag("ExifIFDPointer", 34665),
- new ExifTag("GPSInfoIFDPointer", 34853),
+ new ExifTag(TAG_IMAGE_WIDTH, 256),
+ new ExifTag(TAG_IMAGE_LENGTH, 257),
+ new ExifTag(TAG_BITS_PER_SAMPLE, 258),
+ new ExifTag(TAG_COMPRESSION, 259),
+ new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262),
+ new ExifTag(TAG_IMAGE_DESCRIPTION, 270),
+ new ExifTag(TAG_MAKE, 271),
+ new ExifTag(TAG_MODEL, 272),
+ new ExifTag(TAG_STRIP_OFFSETS, 273),
+ new ExifTag(TAG_ORIENTATION, 274),
+ new ExifTag(TAG_SAMPLES_PER_PIXEL, 277),
+ new ExifTag(TAG_ROWS_PER_STRIP, 278),
+ new ExifTag(TAG_STRIP_BYTE_COUNTS, 279),
+ new ExifTag(TAG_X_RESOLUTION, 282),
+ new ExifTag(TAG_Y_RESOLUTION, 283),
+ new ExifTag(TAG_PLANAR_CONFIGURATION, 284),
+ new ExifTag(TAG_RESOLUTION_UNIT, 296),
+ new ExifTag(TAG_TRANSFER_FUNCTION, 301),
+ new ExifTag(TAG_SOFTWARE, 305),
+ new ExifTag(TAG_DATETIME, 306),
+ new ExifTag(TAG_ARTIST, 315),
+ new ExifTag(TAG_WHITE_POINT, 318),
+ new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319),
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513),
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514),
+ new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529),
+ new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530),
+ new ExifTag(TAG_Y_CB_CR_POSITIONING, 531),
+ new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532),
+ new ExifTag(TAG_COPYRIGHT, 33432),
+ new ExifTag(TAG_EXIF_IFD_POINTER, 34665),
+ new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853),
};
// Primary image IFD Exif Private tags (See JEITA CP-3451 Table 15. page 55).
private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] {
- new ExifTag("ExposureTime", 33434),
- new ExifTag("FNumber", 33437),
- new ExifTag("ExposureProgram", 34850),
- new ExifTag("SpectralSensitivity", 34852),
- new ExifTag("ISOSpeedRatings", 34855),
- new ExifTag("OECF", 34856),
- new ExifTag("ExifVersion", 36864),
- new ExifTag("DateTimeOriginal", 36867),
- new ExifTag("DateTimeDigitized", 36868),
- new ExifTag("ComponentsConfiguration", 37121),
- new ExifTag("CompressedBitsPerPixel", 37122),
- new ExifTag("ShutterSpeedValue", 37377),
- new ExifTag("ApertureValue", 37378),
- new ExifTag("BrightnessValue", 37379),
- new ExifTag("ExposureBiasValue", 37380),
- new ExifTag("MaxApertureValue", 37381),
- new ExifTag("SubjectDistance", 37382),
- new ExifTag("MeteringMode", 37383),
- new ExifTag("LightSource", 37384),
- new ExifTag("Flash", 37385),
- new ExifTag("FocalLength", 37386),
- new ExifTag("SubjectArea", 37396),
- new ExifTag("MakerNote", 37500),
- new ExifTag("UserComment", 37510),
- new ExifTag("SubSecTime", 37520),
- new ExifTag("SubSecTimeOriginal", 37521),
- new ExifTag("SubSecTimeDigitized", 37522),
- new ExifTag("FlashpixVersion", 40960),
- new ExifTag("ColorSpace", 40961),
- new ExifTag("PixelXDimension", 40962),
- new ExifTag("PixelYDimension", 40963),
- new ExifTag("RelatedSoundFile", 40964),
- new ExifTag("InteroperabilityIFDPointer", 40965),
- new ExifTag("FlashEnergy", 41483),
- new ExifTag("SpatialFrequencyResponse", 41484),
- new ExifTag("FocalPlaneXResolution", 41486),
- new ExifTag("FocalPlaneYResolution", 41487),
- new ExifTag("FocalPlaneResolutionUnit", 41488),
- new ExifTag("SubjectLocation", 41492),
- new ExifTag("ExposureIndex", 41493),
- new ExifTag("SensingMethod", 41495),
- new ExifTag("FileSource", 41728),
- new ExifTag("SceneType", 41729),
- new ExifTag("CFAPattern", 41730),
- new ExifTag("CustomRendered", 41985),
- new ExifTag("ExposureMode", 41986),
- new ExifTag("WhiteBalance", 41987),
- new ExifTag("DigitalZoomRatio", 41988),
- new ExifTag("FocalLengthIn35mmFilm", 41989),
- new ExifTag("SceneCaptureType", 41990),
- new ExifTag("GainControl", 41991),
- new ExifTag("Contrast", 41992),
- new ExifTag("Saturation", 41993),
- new ExifTag("Sharpness", 41994),
- new ExifTag("DeviceSettingDescription", 41995),
- new ExifTag("SubjectDistanceRange", 41996),
- new ExifTag("ImageUniqueID", 42016),
+ new ExifTag(TAG_EXPOSURE_TIME, 33434),
+ new ExifTag(TAG_F_NUMBER, 33437),
+ new ExifTag(TAG_EXPOSURE_PROGRAM, 34850),
+ new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852),
+ new ExifTag(TAG_ISO, 34855),
+ new ExifTag(TAG_OECF, 34856),
+ new ExifTag(TAG_EXIF_VERSION, 36864),
+ new ExifTag(TAG_DATETIME_ORIGINAL, 36867),
+ new ExifTag(TAG_DATETIME_DIGITIZED, 36868),
+ new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121),
+ new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122),
+ new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377),
+ new ExifTag(TAG_APERTURE_VALUE, 37378),
+ new ExifTag(TAG_BRIGHTNESS_VALUE, 37379),
+ new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380),
+ new ExifTag(TAG_MAX_APERTURE_VALUE, 37381),
+ new ExifTag(TAG_SUBJECT_DISTANCE, 37382),
+ new ExifTag(TAG_METERING_MODE, 37383),
+ new ExifTag(TAG_LIGHT_SOURCE, 37384),
+ new ExifTag(TAG_FLASH, 37385),
+ new ExifTag(TAG_FOCAL_LENGTH, 37386),
+ new ExifTag(TAG_SUBJECT_AREA, 37396),
+ new ExifTag(TAG_MAKER_NOTE, 37500),
+ new ExifTag(TAG_USER_COMMENT, 37510),
+ new ExifTag(TAG_SUBSEC_TIME, 37520),
+ new ExifTag(TAG_SUBSEC_TIME_ORIG, 37521),
+ new ExifTag(TAG_SUBSEC_TIME_DIG, 37522),
+ new ExifTag(TAG_FLASHPIX_VERSION, 40960),
+ new ExifTag(TAG_COLOR_SPACE, 40961),
+ new ExifTag(TAG_PIXEL_X_DIMENSION, 40962),
+ new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963),
+ new ExifTag(TAG_RELATED_SOUND_FILE, 40964),
+ new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965),
+ new ExifTag(TAG_FLASH_ENERGY, 41483),
+ new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484),
+ new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486),
+ new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487),
+ new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488),
+ new ExifTag(TAG_SUBJECT_LOCATION, 41492),
+ new ExifTag(TAG_EXPOSURE_INDEX, 41493),
+ new ExifTag(TAG_SENSING_METHOD, 41495),
+ new ExifTag(TAG_FILE_SOURCE, 41728),
+ new ExifTag(TAG_SCENE_TYPE, 41729),
+ new ExifTag(TAG_CFA_PATTERN, 41730),
+ new ExifTag(TAG_CUSTOM_RENDERED, 41985),
+ new ExifTag(TAG_EXPOSURE_MODE, 41986),
+ new ExifTag(TAG_WHITE_BALANCE, 41987),
+ new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988),
+ new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989),
+ new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990),
+ new ExifTag(TAG_GAIN_CONTROL, 41991),
+ new ExifTag(TAG_CONTRAST, 41992),
+ new ExifTag(TAG_SATURATION, 41993),
+ new ExifTag(TAG_SHARPNESS, 41994),
+ new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995),
+ new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996),
+ new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016),
};
// Primary image IFD GPS Info tags (See JEITA CP-3451 Table 16. page 56).
private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[] {
- new ExifTag("GPSVersionID", 0),
- new ExifTag("GPSLatitudeRef", 1),
- new ExifTag("GPSLatitude", 2),
- new ExifTag("GPSLongitudeRef", 3),
- new ExifTag("GPSLongitude", 4),
- new ExifTag("GPSAltitudeRef", 5),
- new ExifTag("GPSAltitude", 6),
- new ExifTag("GPSTimeStamp", 7),
- new ExifTag("GPSSatellites", 8),
- new ExifTag("GPSStatus", 9),
- new ExifTag("GPSMeasureMode", 10),
- new ExifTag("GPSDOP", 11),
- new ExifTag("GPSSpeedRef", 12),
- new ExifTag("GPSSpeed", 13),
- new ExifTag("GPSTrackRef", 14),
- new ExifTag("GPSTrack", 15),
- new ExifTag("GPSImgDirectionRef", 16),
- new ExifTag("GPSImgDirection", 17),
- new ExifTag("GPSMapDatum", 18),
- new ExifTag("GPSDestLatitudeRef", 19),
- new ExifTag("GPSDestLatitude", 20),
- new ExifTag("GPSDestLongitudeRef", 21),
- new ExifTag("GPSDestLongitude", 22),
- new ExifTag("GPSDestBearingRef", 23),
- new ExifTag("GPSDestBearing", 24),
- new ExifTag("GPSDestDistanceRef", 25),
- new ExifTag("GPSDestDistance", 26),
- new ExifTag("GPSProcessingMethod", 27),
- new ExifTag("GPSAreaInformation", 28),
- new ExifTag("GPSDateStamp", 29),
- new ExifTag("GPSDifferential", 30),
+ new ExifTag(TAG_GPS_VERSION_ID, 0),
+ new ExifTag(TAG_GPS_LATITUDE_REF, 1),
+ new ExifTag(TAG_GPS_LATITUDE, 2),
+ new ExifTag(TAG_GPS_LONGITUDE_REF, 3),
+ new ExifTag(TAG_GPS_LONGITUDE, 4),
+ new ExifTag(TAG_GPS_ALTITUDE_REF, 5),
+ new ExifTag(TAG_GPS_ALTITUDE, 6),
+ new ExifTag(TAG_GPS_TIMESTAMP, 7),
+ new ExifTag(TAG_GPS_SATELLITES, 8),
+ new ExifTag(TAG_GPS_STATUS, 9),
+ new ExifTag(TAG_GPS_MEASURE_MODE, 10),
+ new ExifTag(TAG_GPS_DOP, 11),
+ new ExifTag(TAG_GPS_SPEED_REF, 12),
+ new ExifTag(TAG_GPS_SPEED, 13),
+ new ExifTag(TAG_GPS_TRACK_REF, 14),
+ new ExifTag(TAG_GPS_TRACK, 15),
+ new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16),
+ new ExifTag(TAG_GPS_IMG_DIRECTION, 17),
+ new ExifTag(TAG_GPS_MAP_DATUM, 18),
+ new ExifTag(TAG_GPS_DEST_LATITUDE_REF, 19),
+ new ExifTag(TAG_GPS_DEST_LATITUDE, 20),
+ new ExifTag(TAG_GPS_DEST_LONGITUDE_REF, 21),
+ new ExifTag(TAG_GPS_DEST_LONGITUDE, 22),
+ new ExifTag(TAG_GPS_DEST_BEARING_REF, 23),
+ new ExifTag(TAG_GPS_DEST_BEARING, 24),
+ new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25),
+ new ExifTag(TAG_GPS_DEST_DISTANCE, 26),
+ new ExifTag(TAG_GPS_PROCESSING_METHOD, 27),
+ new ExifTag(TAG_GPS_AREA_INFORMATION, 28),
+ new ExifTag(TAG_GPS_DATESTAMP, 29),
+ new ExifTag(TAG_GPS_DIFFERENTIAL, 30),
};
// Primary image IFD Interoperability tag (See JEITA CP-3451 Table 17. page 56).
private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[] {
- new ExifTag("InteroperabilityIndex", 1),
+ new ExifTag(TAG_INTEROPERABILITY_INDEX, 1),
};
// IFD Thumbnail tags (See JEITA CP-3451 Table 18. page 57).
private static final ExifTag[] IFD_THUMBNAIL_TAGS = new ExifTag[] {
- new ExifTag("ThumbnailImageWidth", 256),
- new ExifTag("ThumbnailImageLength", 257),
- new ExifTag("BitsPerSample", 258),
- new ExifTag("Compression", 259),
- new ExifTag("PhotometricInterpretation", 262),
- new ExifTag("ImageDescription", 270),
- new ExifTag("Make", 271),
- new ExifTag("Model", 272),
- new ExifTag("StripOffsets", 273),
- new ExifTag("Orientation", 274),
- new ExifTag("SamplesPerPixel", 277),
- new ExifTag("RowsPerStrip", 278),
- new ExifTag("StripByteCounts", 279),
- new ExifTag("XResolution", 282),
- new ExifTag("YResolution", 283),
- new ExifTag("PlanarConfiguration", 284),
- new ExifTag("ResolutionUnit", 296),
- new ExifTag("TransferFunction", 301),
- new ExifTag("Software", 305),
- new ExifTag("DateTime", 306),
- new ExifTag("Artist", 315),
- new ExifTag("WhitePoint", 318),
- new ExifTag("PrimaryChromaticities", 319),
- new ExifTag("JPEGInterchangeFormat", 513),
- new ExifTag("JPEGInterchangeFormatLength", 514),
- new ExifTag("YCbCrCoefficients", 529),
- new ExifTag("YCbCrSubSampling", 530),
- new ExifTag("YCbCrPositioning", 531),
- new ExifTag("ReferenceBlackWhite", 532),
- new ExifTag("Copyright", 33432),
- new ExifTag("ExifIFDPointer", 34665),
- new ExifTag("GPSInfoIFDPointer", 34853),
+ new ExifTag(TAG_THUMBNAIL_IMAGE_WIDTH, 256),
+ new ExifTag(TAG_THUMBNAIL_IMAGE_LENGTH, 257),
+ new ExifTag(TAG_BITS_PER_SAMPLE, 258),
+ new ExifTag(TAG_COMPRESSION, 259),
+ new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262),
+ new ExifTag(TAG_IMAGE_DESCRIPTION, 270),
+ new ExifTag(TAG_MAKE, 271),
+ new ExifTag(TAG_MODEL, 272),
+ new ExifTag(TAG_STRIP_OFFSETS, 273),
+ new ExifTag(TAG_ORIENTATION, 274),
+ new ExifTag(TAG_SAMPLES_PER_PIXEL, 277),
+ new ExifTag(TAG_ROWS_PER_STRIP, 278),
+ new ExifTag(TAG_STRIP_BYTE_COUNTS, 279),
+ new ExifTag(TAG_X_RESOLUTION, 282),
+ new ExifTag(TAG_Y_RESOLUTION, 283),
+ new ExifTag(TAG_PLANAR_CONFIGURATION, 284),
+ new ExifTag(TAG_RESOLUTION_UNIT, 296),
+ new ExifTag(TAG_TRANSFER_FUNCTION, 301),
+ new ExifTag(TAG_SOFTWARE, 305),
+ new ExifTag(TAG_DATETIME, 306),
+ new ExifTag(TAG_ARTIST, 315),
+ new ExifTag(TAG_WHITE_POINT, 318),
+ new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319),
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513),
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514),
+ new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529),
+ new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530),
+ new ExifTag(TAG_Y_CB_CR_POSITIONING, 531),
+ new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532),
+ new ExifTag(TAG_COPYRIGHT, 33432),
+ new ExifTag(TAG_EXIF_IFD_POINTER, 34665),
+ new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853),
};
// See JEITA CP-3451 Figure 5. page 9.
@@ -391,9 +581,9 @@
};
// List of tags for pointing to the other image file directory offset.
private static final ExifTag[] IFD_POINTER_TAGS = new ExifTag[] {
- new ExifTag("ExifIFDPointer", 34665),
- new ExifTag("GPSInfoPointer", 34853),
- new ExifTag("InteroperabilityIFDPointer", 40965),
+ new ExifTag(TAG_EXIF_IFD_POINTER, 34665),
+ new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853),
+ new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965),
};
// List of indices of the indicated tag groups according to the IFD_POINTER_TAGS
private static final int[] IFD_POINTER_TAG_HINTS = new int[] {
@@ -401,15 +591,14 @@
};
// Tags for indicating the thumbnail offset and length
private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
- new ExifTag("JPEGInterchangeFormat", 513);
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513);
private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
- new ExifTag("JPEGInterchangeFormatLength", 514);
+ new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514);
// Mappings from tag number to tag name and each item represents one IFD tag group.
private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
- // Mapping from tag name to tag number and the corresponding tag group.
- private static final HashMap<String, Pair<Integer, Integer>> sExifTagMapForWriting
- = new HashMap<>();
+ // Mappings from tag name to tag number and each item represents one IFD tag group.
+ private static final HashMap[] sExifTagMapsForWriting = new HashMap[EXIF_TAGS.length];
// See JPEG File Interchange Format Version 1.02.
// The following values are defined for handling JPEG streams. In this implementation, we are
@@ -443,35 +632,26 @@
static {
System.loadLibrary("media_jni");
- initRawNative();
+ nativeInitRaw();
sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
// Build up the hash tables to look up Exif tags for reading Exif tags.
for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
sExifTagMapsForReading[hint] = new HashMap();
+ sExifTagMapsForWriting[hint] = new HashMap();
for (ExifTag tag : EXIF_TAGS[hint]) {
sExifTagMapsForReading[hint].put(tag.number, tag.name);
- }
- }
-
- // Build up the hash tables to look up Exif tags for writing Exif tags.
- // There are some tags that have the same tag name in the different group. For that tags,
- // Primary image TIFF IFD and Exif private IFD have a higher priority to map than the other
- // tag groups. For the same tags, it writes one tag in the only one IFD group, which has the
- // higher priority group.
- for (int hint = EXIF_TAGS.length - 1; hint >= 0; --hint) {
- for (ExifTag tag : EXIF_TAGS[hint]) {
- sExifTagMapForWriting.put(tag.name, new Pair<>(tag.number, hint));
+ sExifTagMapsForWriting[hint].put(tag.name, tag.number);
}
}
}
private final String mFilename;
- private final FileDescriptor mFileDescriptor;
- private final InputStream mInputStream;
+ private final FileDescriptor mSeekableFileDescriptor;
+ private final AssetManager.AssetInputStream mAssetInputStream;
private boolean mIsRaw;
- private final HashMap<String, String> mAttributes = new HashMap<>();
+ private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
private boolean mHasThumbnail;
// The following values used for indicating a thumbnail position.
private int mThumbnailOffset;
@@ -488,23 +668,33 @@
if (filename == null) {
throw new IllegalArgumentException("filename cannot be null");
}
+ FileInputStream in = new FileInputStream(filename);
+ mAssetInputStream = null;
mFilename = filename;
- mFileDescriptor = null;
- mInputStream = new FileInputStream(filename);
- loadAttributes();
+ if (isSeekableFD(in.getFD())) {
+ mSeekableFileDescriptor = in.getFD();
+ } else {
+ mSeekableFileDescriptor = null;
+ }
+ loadAttributes(in);
}
/**
- * Reads Exif tags from the specified image file descriptor.
+ * Reads Exif tags from the specified image file descriptor. Attribute mutation is supported
+ * for seekable file descriptors only.
*/
public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
if (fileDescriptor == null) {
throw new IllegalArgumentException("parcelFileDescriptor cannot be null");
}
+ mAssetInputStream = null;
mFilename = null;
- mFileDescriptor = fileDescriptor;
- mInputStream = new FileInputStream(fileDescriptor);
- loadAttributes();
+ if (isSeekableFD(fileDescriptor)) {
+ mSeekableFileDescriptor = fileDescriptor;
+ } else {
+ mSeekableFileDescriptor = null;
+ }
+ loadAttributes(new FileInputStream(fileDescriptor));
}
/**
@@ -516,9 +706,18 @@
throw new IllegalArgumentException("inputStream cannot be null");
}
mFilename = null;
- mFileDescriptor = null;
- mInputStream = inputStream;
- loadAttributes();
+ if (inputStream instanceof AssetManager.AssetInputStream) {
+ mAssetInputStream = (AssetManager.AssetInputStream) inputStream;
+ mSeekableFileDescriptor = null;
+ } else if (inputStream instanceof FileInputStream
+ && isSeekableFD(((FileInputStream) inputStream).getFD())) {
+ mAssetInputStream = null;
+ mSeekableFileDescriptor = ((FileInputStream) inputStream).getFD();
+ } else {
+ mAssetInputStream = null;
+ mSeekableFileDescriptor = null;
+ }
+ loadAttributes(inputStream);
}
/**
@@ -528,7 +727,15 @@
* @param tag the name of the tag.
*/
public String getAttribute(String tag) {
- return mAttributes.get(tag);
+ // Retrieves all tag groups. The value from primary image tag group has a higher priority
+ // than the value from the thumbnail tag group if there are more than one candidates.
+ for (int i = 0; i < EXIF_TAGS.length; ++i) {
+ Object value = mAttributes[i].get(tag);
+ if (value != null) {
+ return (String) value;
+ }
+ }
+ return null;
}
/**
@@ -540,11 +747,11 @@
* @param defaultValue the value to return if the tag is not available.
*/
public int getAttributeInt(String tag, int defaultValue) {
- String value = mAttributes.get(tag);
+ String value = getAttribute(tag);
if (value == null) return defaultValue;
try {
return Integer.valueOf(value);
- } catch (NumberFormatException ex) {
+ } catch (NumberFormatException e) {
return defaultValue;
}
}
@@ -558,7 +765,7 @@
* @param defaultValue the value to return if the tag is not available.
*/
public double getAttributeDouble(String tag, double defaultValue) {
- String value = mAttributes.get(tag);
+ String value = getAttribute(tag);
if (value == null) return defaultValue;
try {
int index = value.indexOf("/");
@@ -567,7 +774,7 @@
if (denom == 0) return defaultValue;
double num = Double.parseDouble(value.substring(0, index));
return num / denom;
- } catch (NumberFormatException ex) {
+ } catch (NumberFormatException e) {
return defaultValue;
}
}
@@ -579,92 +786,114 @@
* @param value the value of the tag.
*/
public void setAttribute(String tag, String value) {
- if (value == null) {
- mAttributes.remove(tag);
- return;
+ for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
+ if (sExifTagMapsForWriting[i].containsKey(tag)) {
+ mAttributes[i].put(tag, value);
+ }
}
- mAttributes.put(tag, value);
}
/**
- * Initialize mAttributes with the attributes from the file mFilename.
- *
- * mAttributes is a HashMap which stores the Exif attributes of the file.
- * The key is the standard tag name and the value is the tag's value: e.g.
- * Model -> Nikon. Numeric values are stored as strings.
- *
- * This function also initialize mHasThumbnail to indicate whether the
- * file has a thumbnail inside.
+ * This function decides which parser to read the image data according to the given input stream
+ * type and the content of the input stream. In each case, it reads the first three bytes to
+ * determine whether the image data format is JPEG or not.
*/
- private void loadAttributes() throws IOException {
- FileInputStream in = null;
- try {
- if (mFilename != null) {
- in = new FileInputStream(mFilename);
- }
- if (mFileDescriptor != null) {
- in = new FileInputStream(mFileDescriptor);
- }
- if (in != null) {
- // First test whether a given file is a one of RAW format or not.
- HashMap map = getRawAttributesNative(Os.dup(in.getFD()));
- mIsRaw = map != null;
- if (mIsRaw) {
- for (Object obj : map.entrySet()) {
- Map.Entry entry = (Map.Entry) obj;
- String attrName = (String) entry.getKey();
- String attrValue = (String) entry.getValue();
-
- switch (attrName) {
- case TAG_HAS_THUMBNAIL:
- mHasThumbnail = attrValue.equalsIgnoreCase("true");
- break;
- case TAG_THUMBNAIL_OFFSET:
- mThumbnailOffset = Integer.parseInt(attrValue);
- break;
- case TAG_THUMBNAIL_LENGTH:
- mThumbnailLength = Integer.parseInt(attrValue);
- break;
- default:
- mAttributes.put(attrName, attrValue);
- break;
- }
- }
-
- if (DEBUG) {
- printAttributes();
- }
- return;
- }
- }
- } catch (ErrnoException e) {
- e.rethrowAsIOException();
- } finally {
- IoUtils.closeQuietly(in);
+ private void loadAttributes(@NonNull InputStream in) throws IOException {
+ // Initialize mAttributes.
+ for (int i = 0; i < EXIF_TAGS.length; ++i) {
+ mAttributes[i] = new HashMap();
}
- try {
- if (mFileDescriptor != null) {
- Os.lseek(mFileDescriptor, 0, OsConstants.SEEK_SET);
+ // Process RAW input stream
+ if (mAssetInputStream != null) {
+ long asset = mAssetInputStream.getNativeAsset();
+ if (handleRawResult(nativeGetRawAttributesFromAsset(asset))) {
+ return;
}
-
- getJpegAttributes(mInputStream);
- } catch (ErrnoException e) {
- e.rethrowAsIOException();
- } finally {
- IoUtils.closeQuietly(mInputStream);
+ } else if (mSeekableFileDescriptor != null) {
+ if (handleRawResult(nativeGetRawAttributesFromFileDescriptor(
+ mSeekableFileDescriptor))) {
+ return;
+ }
+ } else {
+ in = new BufferedInputStream(in, JPEG_SIGNATURE_SIZE);
+ if (!isJpegInputStream((BufferedInputStream) in) && handleRawResult(
+ nativeGetRawAttributesFromInputStream(in))) {
+ return;
+ }
}
+ // Process JPEG input stream
+ getJpegAttributes(in);
+
if (DEBUG) {
printAttributes();
}
}
+ private static boolean isJpegInputStream(BufferedInputStream in) throws IOException {
+ in.mark(JPEG_SIGNATURE_SIZE);
+ byte[] signatureBytes = new byte[JPEG_SIGNATURE_SIZE];
+ if (in.read(signatureBytes) != JPEG_SIGNATURE_SIZE) {
+ throw new EOFException();
+ }
+ boolean isJpeg = Arrays.equals(JPEG_SIGNATURE, signatureBytes);
+ in.reset();
+ return isJpeg;
+ }
+
+ private boolean handleRawResult(HashMap map) {
+ if (map == null) {
+ return false;
+ }
+
+ // Mark for disabling the save feature.
+ mIsRaw = true;
+
+ for (Map.Entry entry : (Set<Map.Entry>) map.entrySet()) {
+ String attrName = (String) entry.getKey();
+
+ switch (attrName) {
+ case TAG_HAS_THUMBNAIL:
+ mHasThumbnail = ((String) entry.getValue()).equalsIgnoreCase("true");
+ break;
+ case TAG_THUMBNAIL_OFFSET:
+ mThumbnailOffset = Integer.parseInt((String) entry.getValue());
+ break;
+ case TAG_THUMBNAIL_LENGTH:
+ mThumbnailLength = Integer.parseInt((String) entry.getValue());
+ break;
+ case TAG_THUMBNAIL_DATA:
+ mThumbnailBytes = (byte[]) entry.getValue();
+ break;
+ default:
+ setAttribute(attrName, (String) entry.getValue());
+ break;
+ }
+ }
+
+ if (DEBUG) {
+ printAttributes();
+ }
+ return true;
+ }
+
+ private static boolean isSeekableFD(FileDescriptor fd) throws IOException {
+ try {
+ Os.lseek(fd, 0, OsConstants.SEEK_CUR);
+ return true;
+ } catch (ErrnoException e) {
+ return false;
+ }
+ }
+
// Prints out attributes for debugging.
private void printAttributes() {
- Log.d(TAG, "The size of tags: " + mAttributes.size());
- for (Map.Entry<String, String> entry : mAttributes.entrySet()) {
- Log.d(TAG, "tagName: " + entry.getKey() + ", tagValue: " + entry.getValue());
+ for (int i = 0; i < mAttributes.length; ++i) {
+ Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
+ for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
+ Log.d(TAG, "tagName: " + entry.getKey() + ", tagValue: " + entry.getValue());
+ }
}
}
@@ -679,9 +908,9 @@
throw new UnsupportedOperationException(
"ExifInterface does not support saving attributes on RAW formats.");
}
- if (mFileDescriptor == null && mFilename == null) {
+ if (mSeekableFileDescriptor == null && mFilename == null) {
throw new UnsupportedOperationException(
- "ExifInterface does not support saving attributes for input streams.");
+ "ExifInterface does not support saving attributes for the current input.");
}
// Keep the thumbnail in memory
@@ -698,11 +927,10 @@
if (!originalFile.renameTo(tempFile)) {
throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
}
- }
- if (mFileDescriptor != null) {
+ } else if (mSeekableFileDescriptor != null) {
tempFile = File.createTempFile("temp", "jpg");
- Os.lseek(mFileDescriptor, 0, OsConstants.SEEK_SET);
- in = new FileInputStream(mFileDescriptor);
+ Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
+ in = new FileInputStream(mSeekableFileDescriptor);
out = new FileOutputStream(tempFile);
Streams.copy(in, out);
}
@@ -720,10 +948,9 @@
in = new FileInputStream(tempFile);
if (mFilename != null) {
out = new FileOutputStream(mFilename);
- }
- if (mFileDescriptor != null) {
- Os.lseek(mFileDescriptor, 0, OsConstants.SEEK_SET);
- out = new FileOutputStream(mFileDescriptor);
+ } else if (mSeekableFileDescriptor != null) {
+ Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
+ out = new FileOutputStream(mSeekableFileDescriptor);
}
saveJpegAttributes(in, out);
} catch (ErrnoException e) {
@@ -760,13 +987,15 @@
// Read the thumbnail.
FileInputStream in = null;
- try {
- if (mFileDescriptor != null) {
- Os.lseek(mFileDescriptor, 0, OsConstants.SEEK_SET);
- in = new FileInputStream(mFileDescriptor);
- }
- if (mFilename != null) {
+ try {
+ if (mAssetInputStream != null) {
+ return nativeGetThumbnailFromAsset(
+ mAssetInputStream.getNativeAsset(), mThumbnailOffset, mThumbnailLength);
+ } else if (mFilename != null) {
in = new FileInputStream(mFilename);
+ } else if (mSeekableFileDescriptor != null) {
+ Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
+ in = new FileInputStream(mSeekableFileDescriptor);
}
if (in == null) {
// Should not be reached this.
@@ -809,10 +1038,10 @@
* Exif tags are not available.
*/
public boolean getLatLong(float output[]) {
- String latValue = mAttributes.get(TAG_GPS_LATITUDE);
- String latRef = mAttributes.get(TAG_GPS_LATITUDE_REF);
- String lngValue = mAttributes.get(TAG_GPS_LONGITUDE);
- String lngRef = mAttributes.get(TAG_GPS_LONGITUDE_REF);
+ String latValue = getAttribute(TAG_GPS_LATITUDE);
+ String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
+ String lngValue = getAttribute(TAG_GPS_LONGITUDE);
+ String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
try {
@@ -850,7 +1079,7 @@
* @hide
*/
public long getDateTime() {
- String dateTimeString = mAttributes.get(TAG_DATETIME);
+ String dateTimeString = getAttribute(TAG_DATETIME);
if (dateTimeString == null
|| !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
@@ -862,7 +1091,7 @@
if (datetime == null) return -1;
long msecs = datetime.getTime();
- String subSecs = mAttributes.get(TAG_SUBSECTIME);
+ String subSecs = getAttribute(TAG_SUBSECTIME);
if (subSecs != null) {
try {
long sub = Long.valueOf(subSecs);
@@ -875,7 +1104,7 @@
}
}
return msecs;
- } catch (IllegalArgumentException ex) {
+ } catch (IllegalArgumentException e) {
return -1;
}
}
@@ -886,8 +1115,8 @@
* @hide
*/
public long getGpsDateTime() {
- String date = mAttributes.get(TAG_GPS_DATESTAMP);
- String time = mAttributes.get(TAG_GPS_TIMESTAMP);
+ String date = getAttribute(TAG_GPS_DATESTAMP);
+ String time = getAttribute(TAG_GPS_TIMESTAMP);
if (date == null || time == null
|| (!sNonZeroTimePattern.matcher(date).matches()
&& !sNonZeroTimePattern.matcher(time).matches())) return -1;
@@ -899,7 +1128,7 @@
Date datetime = sFormatter.parse(dateTimeString, pos);
if (datetime == null) return -1;
return datetime.getTime();
- } catch (IllegalArgumentException ex) {
+ } catch (IllegalArgumentException e) {
return -1;
}
}
@@ -968,8 +1197,9 @@
}
bytesRead += 2;
int length = dataInputStream.readUnsignedShort() - 2;
- if (length < 0)
+ if (length < 0) {
throw new IOException("Invalid length");
+ }
bytesRead += length;
switch (marker) {
case MARKER_APP1: {
@@ -992,6 +1222,9 @@
if (length <= 0) {
throw new IOException("Invalid exif");
}
+ if (DEBUG) {
+ Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
+ }
byte[] bytes = new byte[length];
if (dataInputStream.read(bytes) != length) {
throw new IOException("Invalid exif");
@@ -1007,8 +1240,7 @@
if (dataInputStream.read(bytes) != length) {
throw new IOException("Invalid exif");
}
- mAttributes.put("UserComment",
- new String(bytes, Charset.forName("US-ASCII")));
+ setAttribute("UserComment", new String(bytes, Charset.forName("US-ASCII")));
break;
}
@@ -1026,10 +1258,9 @@
case MARKER_SOF14:
case MARKER_SOF15: {
dataInputStream.skipBytes(1);
- mAttributes.put("ImageLength",
+ setAttribute("ImageLength",
String.valueOf(dataInputStream.readUnsignedShort()));
- mAttributes.put("ImageWidth",
- String.valueOf(dataInputStream.readUnsignedShort()));
+ setAttribute("ImageWidth", String.valueOf(dataInputStream.readUnsignedShort()));
length -= 5;
break;
}
@@ -1082,8 +1313,9 @@
case MARKER_APP1: {
// Rewrite EXIF segment
int length = dataInputStream.readUnsignedShort() - 2;
- if (length < 0)
+ if (length < 0) {
throw new IOException("Invalid length");
+ }
bytesRead += 2;
int read;
while ((read = dataInputStream.read(
@@ -1104,8 +1336,9 @@
// Copy JPEG segment
int length = dataInputStream.readUnsignedShort();
dataOutputStream.writeUnsignedShort(length);
- if (length < 0)
+ if (length < 0) {
throw new IOException("Invalid length");
+ }
length -= 2;
bytesRead += 2;
int read;
@@ -1158,46 +1391,53 @@
}
firstIfdOffset -= 8;
if (firstIfdOffset > 0) {
- if (dataInputStream.skip(firstIfdOffset) != firstIfdOffset)
+ if (dataInputStream.skip(firstIfdOffset) != firstIfdOffset) {
throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset);
+ }
}
// Read primary image TIFF image file directory.
readImageFileDirectory(dataInputStream, IFD_TIFF_HINT);
// Process thumbnail.
- try {
- int jpegInterchangeFormat = Integer.parseInt(
- mAttributes.get(JPEG_INTERCHANGE_FORMAT_TAG.name));
- int jpegInterchangeFormatLength = Integer.parseInt(
- mAttributes.get(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name));
- // The following code limits the size of thumbnail size not to overflow EXIF data area.
- jpegInterchangeFormatLength = Math.min(jpegInterchangeFormat
- + jpegInterchangeFormatLength, exifOffsetFromBeginning + exifBytes.length)
- - jpegInterchangeFormat;
- if (jpegInterchangeFormat > 0 && jpegInterchangeFormatLength > 0) {
- mHasThumbnail = true;
- mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat;
- mThumbnailLength = jpegInterchangeFormatLength;
+ String jpegInterchangeFormatString = getAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
+ String jpegInterchangeFormatLengthString =
+ getAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
+ if (jpegInterchangeFormatString != null && jpegInterchangeFormatLengthString != null) {
+ try {
+ int jpegInterchangeFormat = Integer.parseInt(jpegInterchangeFormatString);
+ int jpegInterchangeFormatLength = Integer
+ .parseInt(jpegInterchangeFormatLengthString);
+ // The following code limits the size of thumbnail size not to overflow EXIF data area.
+ jpegInterchangeFormatLength = Math.min(jpegInterchangeFormat
+ + jpegInterchangeFormatLength, exifOffsetFromBeginning + exifBytes.length)
+ - jpegInterchangeFormat;
+ if (jpegInterchangeFormat > 0 && jpegInterchangeFormatLength > 0) {
+ mHasThumbnail = true;
+ mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat;
+ mThumbnailLength = jpegInterchangeFormatLength;
- // Do not store a thumbnail in memory if the given input can be re-read.
- if (mFileDescriptor == null && mFilename == null) {
- byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength];
- dataInputStream.seek(jpegInterchangeFormat);
- dataInputStream.readFully(thumbnailBytes);
- mThumbnailBytes = thumbnailBytes;
+ if (mFilename == null && mAssetInputStream == null
+ && mSeekableFileDescriptor == null) {
+ // Save the thumbnail in memory if the input doesn't support reading again.
+ byte[] thumbnailBytes = new byte[jpegInterchangeFormatLength];
+ dataInputStream.seek(jpegInterchangeFormat);
+ dataInputStream.readFully(thumbnailBytes);
+ mThumbnailBytes = thumbnailBytes;
- if (DEBUG) {
- Bitmap bitmap = BitmapFactory.decodeByteArray(
- thumbnailBytes, 0, thumbnailBytes.length);
- Log.d(TAG, "Thumbnail offset: " + mThumbnailOffset + ", length: "
- + mThumbnailLength + ", width: " + bitmap.getWidth() + ", height: "
- + bitmap.getHeight());
+ if (DEBUG) {
+ Bitmap bitmap = BitmapFactory.decodeByteArray(
+ thumbnailBytes, 0, thumbnailBytes.length);
+ Log.d(TAG, "Thumbnail offset: " + mThumbnailOffset + ", length: "
+ + mThumbnailLength + ", width: " + bitmap.getWidth()
+ + ", height: "
+ + bitmap.getHeight());
+ }
}
}
+ } catch (NumberFormatException e) {
+ // Ignored the corrupted image.
}
- } catch (NumberFormatException e) {
- // Ignored the corrupted image.
}
// For compatibility, keep data formats as follows.
@@ -1208,7 +1448,7 @@
convertToRational(TAG_FOCAL_LENGTH);
convertToDouble(TAG_DIGITAL_ZOOM_RATIO);
convertToDouble(TAG_EXPOSURE_TIME);
- convertToDouble(TAG_APERTURE);
+ convertToDouble(TAG_F_NUMBER);
convertToDouble(TAG_SUBJECT_DISTANCE);
convertToInt(TAG_ISO);
convertToDouble(TAG_EXPOSURE_BIAS_VALUE);
@@ -1224,29 +1464,29 @@
convertToTimetamp(TAG_GPS_TIMESTAMP);
// The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
- String valueOfDateTimeOriginal = mAttributes.get("DateTimeOriginal");
+ String valueOfDateTimeOriginal = getAttribute("DateTimeOriginal");
if (valueOfDateTimeOriginal != null) {
- mAttributes.put(TAG_DATETIME, valueOfDateTimeOriginal);
+ setAttribute(TAG_DATETIME, valueOfDateTimeOriginal);
}
// Add the default value.
- if (!mAttributes.containsKey(TAG_IMAGE_WIDTH)) {
- mAttributes.put(TAG_IMAGE_WIDTH, "0");
+ if (getAttribute(TAG_IMAGE_WIDTH) == null) {
+ setAttribute(TAG_IMAGE_WIDTH, "0");
}
- if (!mAttributes.containsKey(TAG_IMAGE_LENGTH)) {
- mAttributes.put(TAG_IMAGE_LENGTH, "0");
+ if (getAttribute(TAG_IMAGE_LENGTH) == null) {
+ setAttribute(TAG_IMAGE_LENGTH, "0");
}
- if (!mAttributes.containsKey(TAG_ORIENTATION)) {
- mAttributes.put(TAG_ORIENTATION, "0");
+ if (getAttribute(TAG_ORIENTATION) == null) {
+ setAttribute(TAG_ORIENTATION, "0");
}
- if (!mAttributes.containsKey(TAG_LIGHT_SOURCE)) {
- mAttributes.put(TAG_LIGHT_SOURCE, "0");
+ if (getAttribute(TAG_LIGHT_SOURCE) == null) {
+ setAttribute(TAG_LIGHT_SOURCE, "0");
}
}
// Converts the tag value to timestamp; Otherwise deletes the given tag.
private void convertToTimetamp(String tagName) {
- String entryValue = mAttributes.get(tagName);
+ String entryValue = getAttribute(tagName);
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
String[] components = entryValue.split(",");
@@ -1266,16 +1506,16 @@
int value = numerator / denominator;
stringBuilder.append(String.format("%02d", value));
}
- mAttributes.put(tagName, stringBuilder.toString());
+ setAttribute(tagName, stringBuilder.toString());
} else if (dataFormat != IFD_FORMAT_STRING) {
- mAttributes.remove(tagName);
+ setAttribute(tagName, null);
}
}
// Checks the tag value of a given tag formatted in double type; Otherwise try to convert it to
// double type or delete it.
private void convertToDouble(String tagName) {
- String entryValue = mAttributes.get(tagName);
+ String entryValue = getAttribute(tagName);
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
switch (dataFormat) {
@@ -1295,21 +1535,21 @@
}
stringBuilder.append((double) numerator / denominator);
}
- mAttributes.put(tagName, stringBuilder.toString());
+ setAttribute(tagName, stringBuilder.toString());
break;
}
case IFD_FORMAT_DOUBLE:
// Keep it as is.
break;
default:
- mAttributes.remove(tagName);
+ setAttribute(tagName, null);
break;
}
}
// Checks the tag value of a given tag formatted in int type; Otherwise deletes the tag value.
private void convertToRational(String tagName) {
- String entryValue = mAttributes.get(tagName);
+ String entryValue = getAttribute(tagName);
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
switch (dataFormat) {
@@ -1324,33 +1564,41 @@
double doubleValue = Double.parseDouble(component);
stringBuilder.append((int) (doubleValue * 10000.0)).append("/").append(10000);
}
- mAttributes.put(tagName, stringBuilder.toString());
+ setAttribute(tagName, stringBuilder.toString());
break;
}
case IFD_FORMAT_SRATIONAL:
// Keep it as is.
break;
default:
- mAttributes.remove(tagName);
+ setAttribute(tagName, null);
break;
}
}
// Checks the tag value of a given tag formatted in int type; Otherwise deletes the tag value.
private void convertToInt(String tagName) {
- String entryValue = mAttributes.get(tagName);
+ String entryValue = getAttribute(tagName);
if (entryValue == null) return;
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
if (dataFormat != IFD_FORMAT_SLONG) {
- mAttributes.remove(tagName);
+ setAttribute(tagName, null);
}
}
// Reads image file directory, which is a tag group in EXIF.
private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint)
throws IOException {
+ if (dataInputStream.peek() + 2 > dataInputStream.mLength) {
+ // Return if there is no data from the offset.
+ return;
+ }
// See JEITA CP-3451 Figure 5. page 9.
short numberOfDirectoryEntry = dataInputStream.readShort();
+ if (dataInputStream.peek() + 12 * numberOfDirectoryEntry > dataInputStream.mLength) {
+ // Return if the size of entries is too big.
+ return;
+ }
if (DEBUG) {
Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
@@ -1362,10 +1610,25 @@
int numberOfComponents = dataInputStream.readInt();
long nextEntryOffset = dataInputStream.peek() + 4; // next four bytes is for data
// offset or value.
+ // Look up a corresponding tag from tag number
+ String tagName = (String) sExifTagMapsForReading[hint].get(tagNumber);
if (DEBUG) {
- Log.d(TAG, String.format("tagNumber: %d, dataFormat: %d, numberOfComponents: %d",
- tagNumber, dataFormat, numberOfComponents));
+ Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d," +
+ "numberOfComponents: %d", hint, tagNumber, tagName, dataFormat,
+ numberOfComponents));
+ }
+
+ if (tagName == null || dataFormat <= 0 ||
+ dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
+ // Skip if the parsed tag number is not defined or invalid data format.
+ if (tagName == null) {
+ Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
+ } else {
+ Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
+ }
+ dataInputStream.seek(nextEntryOffset);
+ continue;
}
// Read a value from data field or seek to the value offset which is stored in data
@@ -1376,19 +1639,21 @@
if (DEBUG) {
Log.d(TAG, "seek to data offset: " + offset);
}
- dataInputStream.seek(offset);
- }
-
- // Look up a corresponding tag from tag number
- String tagName = (String) sExifTagMapsForReading[hint].get(tagNumber);
- // Skip if the parsed tag number is not defined.
- if (tagName == null) {
- dataInputStream.seek(nextEntryOffset);
- continue;
+ if (offset + byteCount <= dataInputStream.mLength) {
+ dataInputStream.seek(offset);
+ } else {
+ // Skip if invalid data offset.
+ Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset);
+ dataInputStream.seek(nextEntryOffset);
+ continue;
+ }
}
// Recursively parse IFD when a IFD pointer tag appears.
int innerIfdHint = getIfdHintFromTagNumber(tagNumber);
+ if (DEBUG) {
+ Log.d(TAG, "innerIfdHint: " + innerIfdHint + " byteCount: " + byteCount);
+ }
if (innerIfdHint >= 0) {
long offset = -1L;
// Get offset from data field
@@ -1417,9 +1682,11 @@
if (DEBUG) {
Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tagName));
}
- if (offset > 0L) {
+ if (offset > 0L && offset < dataInputStream.mLength) {
dataInputStream.seek(offset);
readImageFileDirectory(dataInputStream, innerIfdHint);
+ } else {
+ Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
}
dataInputStream.seek(nextEntryOffset);
@@ -1431,7 +1698,7 @@
String entryValue = readExifEntryValue(
dataInputStream, dataFormat, numberOfComponents);
if (entryValue != null) {
- mAttributes.put(tagName, entryValue);
+ setAttribute(tagName, entryValue);
}
} else {
StringBuilder entryValueBuilder = new StringBuilder();
@@ -1442,7 +1709,7 @@
entryValueBuilder.append(readExifEntryValue(
dataInputStream, dataFormat, numberOfComponents));
}
- mAttributes.put(tagName, entryValueBuilder.toString());
+ setAttribute(tagName, entryValueBuilder.toString());
}
if (dataInputStream.peek() != nextEntryOffset) {
@@ -1450,14 +1717,17 @@
}
}
- long nextIfdOffset = dataInputStream.readUnsignedInt();
- if (DEBUG) {
- Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
- }
- // The next IFD offset needs to be bigger than 8 since the first IFD offset is at least 8.
- if (nextIfdOffset > 8) {
- dataInputStream.seek(nextIfdOffset);
- readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT);
+ if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
+ long nextIfdOffset = dataInputStream.readUnsignedInt();
+ if (DEBUG) {
+ Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
+ }
+ // The next IFD offset needs to be bigger than 8
+ // since the first IFD offset is at least 8.
+ if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
+ dataInputStream.seek(nextIfdOffset);
+ readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT);
+ }
}
}
@@ -1515,19 +1785,18 @@
}
StringBuilder stringBuilder = new StringBuilder();
- while (true) {
+ while (index < numberOfComponents) {
int ch = bytes[index];
- if (ch < 0)
- throw new EOFException();
- if (ch == 0)
+ if (ch == 0) {
break;
- if (ch >= 32)
+ }
+ if (ch >= 32) {
stringBuilder.append((char) ch);
- else
+ }
+ else {
stringBuilder.append('?');
+ }
++index;
- if (index == numberOfComponents)
- break;
}
return stringBuilder.toString();
}
@@ -1541,8 +1810,9 @@
// Gets the corresponding IFD group index of the given tag number for writing Exif Tags.
private static int getIfdHintFromTagNumber(int tagNumber) {
for (int i = 0; i < IFD_POINTER_TAG_HINTS.length; ++i) {
- if (IFD_POINTER_TAGS[i].number == tagNumber)
+ if (IFD_POINTER_TAGS[i].number == tagNumber) {
return IFD_POINTER_TAG_HINTS[i];
+ }
}
return -1;
}
@@ -1554,49 +1824,46 @@
int[] ifdOffsets = new int[EXIF_TAGS.length];
int[] ifdDataSizes = new int[EXIF_TAGS.length];
- // Maps to store tags per IFD tag group
- HashMap[] ifdTags = new HashMap[EXIF_TAGS.length];
- for (int i = 0; i < EXIF_TAGS.length; ++i) {
- ifdTags[i] = new HashMap();
- }
-
// Remove IFD pointer tags (we'll re-add it later.)
for (ExifTag tag : IFD_POINTER_TAGS) {
- mAttributes.remove(tag.name);
- }
-
- // Assign tags to the corresponding group
- for (Map.Entry<String, String> entry : mAttributes.entrySet()) {
- Pair<Integer, Integer> pair = sExifTagMapForWriting.get(entry.getKey());
- if (pair != null) {
- int tagNumber = pair.first;
- int hint = pair.second;
- ifdTags[hint].put(tagNumber, entry.getValue());
- }
+ setAttribute(tag.name, null);
}
// Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
// offset when there is one or more tags in the thumbnail IFD.
- if (!ifdTags[IFD_INTEROPERABILITY_HINT].isEmpty()) {
- ifdTags[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].number, "0");
+ if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
+ mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name, "0");
}
- if (!ifdTags[IFD_EXIF_HINT].isEmpty()) {
- ifdTags[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].number, "0");
+ if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
+ mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name, "0");
}
- if (!ifdTags[IFD_GPS_HINT].isEmpty()) {
- ifdTags[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].number, "0");
+ if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
+ mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name, "0");
}
+ // Remove old thumbnail data
+ setAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name, null);
+ setAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, null);
if (mHasThumbnail) {
- ifdTags[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.number, "0");
- ifdTags[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.number,
+ mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name, "0");
+ mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
String.valueOf(mThumbnailLength));
}
+ // Remove null value tags.
+ for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
+ for (Object obj : mAttributes[hint].entrySet().toArray()) {
+ Map.Entry entry = (Map.Entry) obj;
+ if (entry.getValue() == null) {
+ mAttributes[hint].remove(entry.getKey());
+ }
+ }
+ }
+
// Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
// value which has a bigger size than 4 bytes.
for (int i = 0; i < 5; ++i) {
int sum = 0;
- for (Object entry : ifdTags[i].entrySet()) {
+ for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
String entryValue = (String) ((Map.Entry) entry).getValue();
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
int size = getSizeOfExifEntryValue(dataFormat, entryValue);
@@ -1610,16 +1877,16 @@
// Calculate IFD offsets.
int position = 8;
for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
- if (!ifdTags[hint].isEmpty()) {
+ if (!mAttributes[hint].isEmpty()) {
ifdOffsets[hint] = position;
- position += 2 + ifdTags[hint].size() * 12 + 4 + ifdDataSizes[hint];
+ position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint];
}
}
if (mHasThumbnail) {
int thumbnailOffset = position;
- ifdTags[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.number,
+ mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
String.valueOf(thumbnailOffset));
- ifdTags[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.number,
+ mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
String.valueOf(mThumbnailLength));
mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
position += mThumbnailLength;
@@ -1631,21 +1898,21 @@
Log.d(TAG, "totalSize length: " + totalSize);
for (int i = 0; i < 5; ++i) {
Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
- i, ifdOffsets[i], ifdTags[i].size(), ifdDataSizes[i]));
+ i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
}
}
// Update IFD pointer tags with the calculated offsets.
- if (!ifdTags[IFD_EXIF_HINT].isEmpty()) {
- ifdTags[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].number,
+ if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
+ mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name,
String.valueOf(ifdOffsets[IFD_EXIF_HINT]));
}
- if (!ifdTags[IFD_GPS_HINT].isEmpty()) {
- ifdTags[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].number,
+ if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
+ mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name,
String.valueOf(ifdOffsets[IFD_GPS_HINT]));
}
- if (!ifdTags[IFD_INTEROPERABILITY_HINT].isEmpty()) {
- ifdTags[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].number,
+ if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
+ mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name,
String.valueOf(ifdOffsets[IFD_INTEROPERABILITY_HINT]));
}
@@ -1658,16 +1925,16 @@
// Write IFD groups. See JEITA CP-3451C Figure 7. page 12.
for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
- if (!ifdTags[hint].isEmpty()) {
+ if (!mAttributes[hint].isEmpty()) {
// See JEITA CP-3451C 4.6.2 IFD structure. page 13.
// Write entry count
- dataOutputStream.writeUnsignedShort(ifdTags[hint].size());
+ dataOutputStream.writeUnsignedShort(mAttributes[hint].size());
// Write entry info
- int dataOffset = ifdOffsets[hint] + 2 + ifdTags[hint].size() * 12 + 4;
- for (Object obj : ifdTags[hint].entrySet()) {
- Map.Entry entry = (Map.Entry) obj;
- int tagNumber = (int) entry.getKey();
+ int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4;
+ for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
+ // Convert tag name to tag number.
+ int tagNumber = (int) sExifTagMapsForWriting[hint].get(entry.getKey());
String entryValue = (String) entry.getValue();
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
@@ -1695,15 +1962,14 @@
// Write the next offset. It writes the offset of thumbnail IFD if there is one or
// more tags in the thumbnail IFD when the current IFD is the primary image TIFF
// IFD; Otherwise 0.
- if (hint == 0 && !ifdTags[IFD_THUMBNAIL_HINT].isEmpty()) {
+ if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) {
dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]);
} else {
dataOutputStream.writeUnsignedInt(0);
}
// Write values of data field exceeding 4 bytes after the next offset.
- for (Object obj : ifdTags[hint].entrySet()) {
- Map.Entry entry = (Map.Entry) obj;
+ for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
String entryValue = (String) entry.getValue();
int dataFormat = getDataFormatOfExifEntryValue(entryValue);
@@ -1849,8 +2115,9 @@
public void seek(long byteCount) throws IOException {
mPosition = 0L;
reset();
- if (skip(byteCount) != byteCount)
+ if (skip(byteCount) != byteCount) {
throw new IOException("Couldn't seek up to the byteCount");
+ }
}
public long peek() {
@@ -1859,8 +2126,9 @@
public void readFully(byte[] buffer) throws IOException {
mPosition += buffer.length;
- if (mPosition > mLength)
+ if (mPosition > mLength) {
throw new EOFException();
+ }
if (super.read(buffer, 0, buffer.length) != buffer.length) {
throw new IOException("Couldn't read up to the length of buffer");
}
@@ -1868,22 +2136,26 @@
public byte readByte() throws IOException {
++mPosition;
- if (mPosition > mLength)
+ if (mPosition > mLength) {
throw new EOFException();
+ }
int ch = super.read();
- if (ch < 0)
+ if (ch < 0) {
throw new EOFException();
+ }
return (byte) ch;
}
public short readShort() throws IOException {
mPosition += 2;
- if (mPosition > mLength)
+ if (mPosition > mLength) {
throw new EOFException();
+ }
int ch1 = super.read();
int ch2 = super.read();
- if ((ch1 | ch2) < 0)
+ if ((ch1 | ch2) < 0) {
throw new EOFException();
+ }
if (mByteOrder == LITTLE_ENDIAN) {
return (short) ((ch2 << 8) + (ch1));
} else if (mByteOrder == BIG_ENDIAN) {
@@ -1894,14 +2166,16 @@
public int readInt() throws IOException {
mPosition += 4;
- if (mPosition > mLength)
+ if (mPosition > mLength) {
throw new EOFException();
+ }
int ch1 = super.read();
int ch2 = super.read();
int ch3 = super.read();
int ch4 = super.read();
- if ((ch1 | ch2 | ch3 | ch4) < 0)
+ if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new EOFException();
+ }
if (mByteOrder == LITTLE_ENDIAN) {
return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
} else if (mByteOrder == BIG_ENDIAN) {
@@ -1919,12 +2193,14 @@
public int readUnsignedShort() throws IOException {
mPosition += 2;
- if (mPosition > mLength)
+ if (mPosition > mLength) {
throw new EOFException();
+ }
int ch1 = super.read();
int ch2 = super.read();
- if ((ch1 | ch2) < 0)
+ if ((ch1 | ch2) < 0) {
throw new EOFException();
+ }
if (mByteOrder == LITTLE_ENDIAN) {
return ((ch2 << 8) + (ch1));
} else if (mByteOrder == BIG_ENDIAN) {
@@ -1939,8 +2215,9 @@
public long readLong() throws IOException {
mPosition += 8;
- if (mPosition > mLength)
+ if (mPosition > mLength) {
throw new EOFException();
+ }
int ch1 = super.read();
int ch2 = super.read();
int ch3 = super.read();
@@ -1949,8 +2226,9 @@
int ch6 = super.read();
int ch7 = super.read();
int ch8 = super.read();
- if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0)
+ if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
throw new EOFException();
+ }
if (mByteOrder == LITTLE_ENDIAN) {
return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
+ ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
@@ -1988,6 +2266,10 @@
}
// JNI methods for RAW formats.
- private static native void initRawNative();
- private static native HashMap getRawAttributesNative(FileDescriptor fileDescriptor);
+ private static native void nativeInitRaw();
+ private static native byte[] nativeGetThumbnailFromAsset(
+ long asset, int thumbnailOffset, int thumbnailLength);
+ private static native HashMap nativeGetRawAttributesFromAsset(long asset);
+ private static native HashMap nativeGetRawAttributesFromFileDescriptor(FileDescriptor fd);
+ private static native HashMap nativeGetRawAttributesFromInputStream(InputStream in);
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 987a8b6..97f670b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -20,7 +20,7 @@
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.media.AudioAttributes;
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
@@ -164,5 +164,5 @@
oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb);
- AudioRecordConfiguration[] getActiveRecordConfigurations();
+ AudioRecordingConfiguration[] getActiveRecordingConfigurations();
}
diff --git a/media/java/android/media/IRecordingConfigDispatcher.aidl b/media/java/android/media/IRecordingConfigDispatcher.aidl
index a5eb8b9f..e803283 100644
--- a/media/java/android/media/IRecordingConfigDispatcher.aidl
+++ b/media/java/android/media/IRecordingConfigDispatcher.aidl
@@ -16,6 +16,8 @@
package android.media;
+import android.media.AudioRecordingConfiguration;
+
/**
* AIDL for the RecordingActivity monitor in AudioService to signal audio recording updates.
*
@@ -23,6 +25,6 @@
*/
oneway interface IRecordingConfigDispatcher {
- void dispatchRecordingConfigChange();
+ void dispatchRecordingConfigChange(in AudioRecordingConfiguration[] configs);
}
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl
index 8091421..4b1e39f 100644
--- a/media/java/android/media/IRingtonePlayer.aidl
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -18,6 +18,7 @@
import android.media.AudioAttributes;
import android.net.Uri;
+import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
/**
@@ -36,4 +37,6 @@
/** Return the title of the media. */
String getTitle(in Uri uri);
+
+ ParcelFileDescriptor openRingtone(in Uri uri);
}
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index c08f4bf..81cc035 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -335,7 +335,6 @@
switch (status) {
case ACQUIRE_SUCCESS:
- si.createSurfacePlanes();
si.mIsImageValid = true;
case ACQUIRE_NO_BUFS:
case ACQUIRE_MAX_IMAGES:
@@ -693,7 +692,7 @@
width = ImageReader.this.getWidth();
break;
default:
- width = nativeGetWidth(mFormat);
+ width = nativeGetWidth();
}
return width;
}
@@ -709,7 +708,7 @@
height = ImageReader.this.getHeight();
break;
default:
- height = nativeGetHeight(mFormat);
+ height = nativeGetHeight();
}
return height;
}
@@ -729,6 +728,10 @@
@Override
public Plane[] getPlanes() {
throwISEIfImageIsInvalid();
+
+ if (mPlanes == null) {
+ mPlanes = nativeCreatePlanes(ImageReader.this.mNumPlanes, ImageReader.this.mFormat);
+ }
// Shallow copy is fine.
return mPlanes.clone();
}
@@ -766,7 +769,8 @@
}
private void clearSurfacePlanes() {
- if (mIsImageValid) {
+ // Image#getPlanes may not be called before the image is closed.
+ if (mIsImageValid && mPlanes != null) {
for (int i = 0; i < mPlanes.length; i++) {
if (mPlanes[i] != null) {
mPlanes[i].clearBuffer();
@@ -776,32 +780,25 @@
}
}
- private void createSurfacePlanes() {
- mPlanes = new SurfacePlane[ImageReader.this.mNumPlanes];
- for (int i = 0; i < ImageReader.this.mNumPlanes; i++) {
- mPlanes[i] = nativeCreatePlane(i, ImageReader.this.mFormat);
- }
- }
private class SurfacePlane extends android.media.Image.Plane {
- // SurfacePlane instance is created by native code when a new SurfaceImage is created
- private SurfacePlane(int index, int rowStride, int pixelStride) {
- mIndex = index;
+ // SurfacePlane instance is created by native code when SurfaceImage#getPlanes() is
+ // called
+ private SurfacePlane(int rowStride, int pixelStride, ByteBuffer buffer) {
mRowStride = rowStride;
mPixelStride = pixelStride;
+ mBuffer = buffer;
+ /**
+ * Set the byteBuffer order according to host endianness (native
+ * order), otherwise, the byteBuffer order defaults to
+ * ByteOrder.BIG_ENDIAN.
+ */
+ mBuffer.order(ByteOrder.nativeOrder());
}
@Override
public ByteBuffer getBuffer() {
- SurfaceImage.this.throwISEIfImageIsInvalid();
- if (mBuffer != null) {
- return mBuffer;
- } else {
- mBuffer = SurfaceImage.this.nativeImageGetBuffer(mIndex,
- ImageReader.this.mFormat);
- // Set the byteBuffer order according to host endianness (native order),
- // otherwise, the byteBuffer order defaults to ByteOrder.BIG_ENDIAN.
- return mBuffer.order(ByteOrder.nativeOrder());
- }
+ throwISEIfImageIsInvalid();
+ return mBuffer;
}
@Override
@@ -837,7 +834,6 @@
mBuffer = null;
}
- final private int mIndex;
final private int mPixelStride;
final private int mRowStride;
@@ -860,10 +856,10 @@
// If this image is detached from the ImageReader.
private AtomicBoolean mIsDetached = new AtomicBoolean(false);
- private synchronized native ByteBuffer nativeImageGetBuffer(int idx, int readerFormat);
- private synchronized native SurfacePlane nativeCreatePlane(int idx, int readerFormat);
- private synchronized native int nativeGetWidth(int format);
- private synchronized native int nativeGetHeight(int format);
+ private synchronized native SurfacePlane[] nativeCreatePlanes(int numPlanes,
+ int readerFormat);
+ private synchronized native int nativeGetWidth();
+ private synchronized native int nativeGetHeight();
private synchronized native int nativeGetFormat(int readerFormat);
}
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 851d436..83a4f17 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -748,8 +748,8 @@
final private int mPixelStride;
final private int mRowStride;
- // SurfacePlane instance is created by native code when a new
- // SurfaceImage is created
+ // SurfacePlane instance is created by native code when SurfaceImage#getPlanes() is
+ // called
private SurfacePlane(int rowStride, int pixelStride, ByteBuffer buffer) {
mRowStride = rowStride;
mPixelStride = pixelStride;
@@ -795,7 +795,7 @@
}
- // this will create the SurfacePlane object and fill the information
+ // Create the SurfacePlane object and fill the information
private synchronized native SurfacePlane[] nativeCreatePlanes(int numPlanes, int writerFmt);
private synchronized native int nativeGetWidth();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 4c6f0e6..c73cad4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2074,6 +2074,16 @@
*/
public static final int ERROR_SESSION_NOT_OPENED = 5;
+ /**
+ * This indicates that an operation was attempted that could not be
+ * supported by the crypto system of the device in its current
+ * configuration. It may occur when the license policy requires
+ * device security features that aren't supported by the device,
+ * or due to an internal error in the crypto system that prevents
+ * the specified security policy from being met.
+ */
+ public static final int ERROR_UNSUPPORTED_OPERATION = 6;
+
/** @hide */
@IntDef({
ERROR_NO_KEY,
@@ -2081,6 +2091,7 @@
ERROR_RESOURCE_BUSY,
ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
ERROR_SESSION_NOT_OPENED,
+ ERROR_UNSUPPORTED_OPERATION
})
@Retention(RetentionPolicy.SOURCE)
public @interface CryptoErrorCode {}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index db0c5bb..2650ee0 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -1070,16 +1070,15 @@
* A CryptoSession is obtained using {@link #getCryptoSession}
*/
public final class CryptoSession {
- private MediaDrm mDrm;
private byte[] mSessionId;
- CryptoSession(@NonNull MediaDrm drm, @NonNull byte[] sessionId,
- @NonNull String cipherAlgorithm, @NonNull String macAlgorithm)
+ CryptoSession(@NonNull byte[] sessionId,
+ @NonNull String cipherAlgorithm,
+ @NonNull String macAlgorithm)
{
mSessionId = sessionId;
- mDrm = drm;
- setCipherAlgorithmNative(drm, sessionId, cipherAlgorithm);
- setMacAlgorithmNative(drm, sessionId, macAlgorithm);
+ setCipherAlgorithmNative(MediaDrm.this, sessionId, cipherAlgorithm);
+ setMacAlgorithmNative(MediaDrm.this, sessionId, macAlgorithm);
}
/**
@@ -1092,7 +1091,7 @@
@NonNull
public byte[] encrypt(
@NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
- return encryptNative(mDrm, mSessionId, keyid, input, iv);
+ return encryptNative(MediaDrm.this, mSessionId, keyid, input, iv);
}
/**
@@ -1105,7 +1104,7 @@
@NonNull
public byte[] decrypt(
@NonNull byte[] keyid, @NonNull byte[] input, @NonNull byte[] iv) {
- return decryptNative(mDrm, mSessionId, keyid, input, iv);
+ return decryptNative(MediaDrm.this, mSessionId, keyid, input, iv);
}
/**
@@ -1116,7 +1115,7 @@
*/
@NonNull
public byte[] sign(@NonNull byte[] keyid, @NonNull byte[] message) {
- return signNative(mDrm, mSessionId, keyid, message);
+ return signNative(MediaDrm.this, mSessionId, keyid, message);
}
/**
@@ -1130,7 +1129,7 @@
*/
public boolean verify(
@NonNull byte[] keyid, @NonNull byte[] message, @NonNull byte[] signature) {
- return verifyNative(mDrm, mSessionId, keyid, message, signature);
+ return verifyNative(MediaDrm.this, mSessionId, keyid, message, signature);
}
};
@@ -1158,7 +1157,7 @@
@NonNull byte[] sessionId,
@NonNull String cipherAlgorithm, @NonNull String macAlgorithm)
{
- return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm);
+ return new CryptoSession(sessionId, cipherAlgorithm, macAlgorithm);
}
/**
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index b339925..177344a 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -28,6 +28,8 @@
import android.net.Uri;
import android.os.IBinder;
+import com.android.internal.util.Preconditions;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -189,6 +191,26 @@
}
/**
+ * Sets the data source (AssetFileDescriptor) to use. It is the caller's
+ * responsibility to close the file descriptor. It is safe to do so as soon
+ * as this call returns.
+ *
+ * @param afd the AssetFileDescriptor for the file you want to extract from.
+ */
+ public final void setDataSource(@NonNull AssetFileDescriptor afd)
+ throws IOException, IllegalArgumentException, IllegalStateException {
+ Preconditions.checkNotNull(afd);
+ // Note: using getDeclaredLength so that our behavior is the same
+ // as previous versions when the content provider is returning
+ // a full file.
+ if (afd.getDeclaredLength() < 0) {
+ setDataSource(afd.getFileDescriptor());
+ } else {
+ setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
+ }
+ }
+
+ /**
* Sets the data source (FileDescriptor) to use. It is the caller's responsibility
* to close the file descriptor. It is safe to do so as soon as this call returns.
*
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index a0e2481..26061e4 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -16,6 +16,10 @@
package android.media;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
@@ -606,6 +610,16 @@
/** BT.2020 color chromacity coordinates with KR = 0.2627, KB = 0.0593. */
public static final int COLOR_STANDARD_BT2020 = 6;
+ /** @hide */
+ @IntDef({
+ COLOR_STANDARD_BT709,
+ COLOR_STANDARD_BT601_PAL,
+ COLOR_STANDARD_BT601_NTSC,
+ COLOR_STANDARD_BT2020,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ColorStandard {}
+
/**
* An optional key describing the opto-electronic transfer function used
* for the video content.
@@ -628,6 +642,16 @@
/** ARIB STD-B67 hybrid-log-gamma transfer function. This is used by some HDR video content. */
public static final int COLOR_TRANSFER_HLG = 7;
+ /** @hide */
+ @IntDef({
+ COLOR_TRANSFER_LINEAR,
+ COLOR_TRANSFER_SDR_VIDEO,
+ COLOR_TRANSFER_ST2084,
+ COLOR_TRANSFER_HLG,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ColorTransfer {}
+
/**
* An optional key describing the range of the component values of the video content.
*
@@ -644,6 +668,26 @@
/** Full range. Y, Cr and Cb component values range from 0 to 255 for 8-bit content. */
public static final int COLOR_RANGE_FULL = 1;
+ /** @hide */
+ @IntDef({
+ COLOR_RANGE_LIMITED,
+ COLOR_RANGE_FULL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ColorRange {}
+
+ /**
+ * An optional key describing the static metadata of HDR (high-dynamic-range) video content.
+ *
+ * The associated value is a ByteBuffer. This buffer contains the raw contents of the
+ * Static Metadata Descriptor (including the descriptor ID) of an HDMI Dynamic Range and
+ * Mastering InfoFrame as defined by CTA-861.3. This key must be provided to video decoders
+ * for HDR video content unless this information is contained in the bitstream and the video
+ * decoder supports an HDR-capable profile. This key must be provided to video encoders for
+ * HDR video content.
+ */
+ public static final String KEY_HDR_STATIC_INFO = "hdr-static-info";
+
/**
* A key describing a unique ID for the content of a media track.
*
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 78f357f..5fd85d1 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1042,13 +1042,13 @@
if(needToSetSettings) {
if (notifications) {
- setSettingIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
+ setRingtoneIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
mDefaultNotificationSet = true;
} else if (ringtones) {
- setSettingIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
+ setRingtoneIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
mDefaultRingtoneSet = true;
} else if (alarms) {
- setSettingIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
+ setRingtoneIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
mDefaultAlarmSet = true;
}
}
@@ -1063,18 +1063,18 @@
pathFilenameStart + filenameLength == path.length();
}
- private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
-
- if(wasSettingAlreadySet(settingName)) {
+ private void setRingtoneIfNotSet(String settingName, Uri uri, long rowId) {
+ if (wasRingtoneAlreadySet(settingName)) {
return;
}
ContentResolver cr = mContext.getContentResolver();
String existingSettingValue = Settings.System.getString(cr, settingName);
if (TextUtils.isEmpty(existingSettingValue)) {
- // Set the setting to the given URI
- Settings.System.putString(cr, settingName,
- ContentUris.withAppendedId(uri, rowId).toString());
+ final Uri settingUri = Settings.System.getUriFor(settingName);
+ final Uri ringtoneUri = ContentUris.withAppendedId(uri, rowId);
+ RingtoneManager.setActualDefaultRingtoneUri(mContext,
+ RingtoneManager.getDefaultType(settingUri), ringtoneUri);
}
Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
}
@@ -1107,7 +1107,7 @@
return base + "_set";
}
- private boolean wasSettingAlreadySet(String name) {
+ private boolean wasRingtoneAlreadySet(String name) {
ContentResolver cr = mContext.getContentResolver();
String indicatorName = settingSetIndicatorName(name);
try {
@@ -1134,9 +1134,9 @@
selectionArgs = new String[] { "" };
}
- mDefaultRingtoneSet = wasSettingAlreadySet(Settings.System.RINGTONE);
- mDefaultNotificationSet = wasSettingAlreadySet(Settings.System.NOTIFICATION_SOUND);
- mDefaultAlarmSet = wasSettingAlreadySet(Settings.System.ALARM_ALERT);
+ mDefaultRingtoneSet = wasRingtoneAlreadySet(Settings.System.RINGTONE);
+ mDefaultNotificationSet = wasRingtoneAlreadySet(Settings.System.NOTIFICATION_SOUND);
+ mDefaultAlarmSet = wasRingtoneAlreadySet(Settings.System.ALARM_ALERT);
// Tell the provider to not delete the file.
// If the file is truly gone the delete is unnecessary, and we want to avoid
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index feb490d..86ebae1 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -16,10 +16,6 @@
package android.media;
-import com.android.internal.database.SortCursor;
-
-import libcore.io.Streams;
-
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Activity;
@@ -30,12 +26,17 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.System;
import android.util.Log;
+import com.android.internal.database.SortCursor;
+
+import libcore.io.Streams;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -223,9 +224,9 @@
*/
public static final int URI_COLUMN_INDEX = 2;
- private Activity mActivity;
- private Context mContext;
-
+ private final Activity mActivity;
+ private final Context mContext;
+
private Cursor mCursor;
private int mType = TYPE_RINGTONE;
@@ -246,7 +247,8 @@
* @param activity The activity used to get a managed cursor.
*/
public RingtoneManager(Activity activity) {
- mContext = mActivity = activity;
+ mActivity = activity;
+ mContext = activity;
setType(mType);
}
@@ -258,6 +260,7 @@
* @param context The context to used to get a cursor.
*/
public RingtoneManager(Context context) {
+ mActivity = null;
mContext = context;
setType(mType);
}
@@ -271,7 +274,6 @@
* @see #EXTRA_RINGTONE_TYPE
*/
public void setType(int type) {
-
if (mCursor != null) {
throw new IllegalStateException(
"Setting filter columns should be done before querying for ringtones.");
@@ -641,7 +643,8 @@
public static Uri getActualDefaultRingtoneUri(Context context, int type) {
String setting = getSettingForType(type);
if (setting == null) return null;
- final String uriString = Settings.System.getString(context.getContentResolver(), setting);
+ final String uriString = Settings.System.getStringForUser(context.getContentResolver(),
+ setting, context.getUserId());
return uriString != null ? Uri.parse(uriString) : null;
}
@@ -656,18 +659,19 @@
* @see #getActualDefaultRingtoneUri(Context, int)
*/
public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
+ final ContentResolver resolver = context.getContentResolver();
+
String setting = getSettingForType(type);
if (setting == null) return;
- Settings.System.putString(context.getContentResolver(), setting,
- ringtoneUri != null ? ringtoneUri.toString() : null);
+ Settings.System.putStringForUser(resolver, setting,
+ ringtoneUri != null ? ringtoneUri.toString() : null, context.getUserId());
// Stream selected ringtone into cache so it's available for playback
// when CE storage is still locked
if (ringtoneUri != null) {
- final ContentResolver cr = context.getContentResolver();
final Uri cacheUri = getCacheForType(type);
- try (InputStream in = cr.openInputStream(ringtoneUri);
- OutputStream out = cr.openOutputStream(cacheUri)) {
+ try (InputStream in = openRingtone(context, ringtoneUri);
+ OutputStream out = resolver.openOutputStream(cacheUri)) {
Streams.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to cache ringtone: " + e);
@@ -675,6 +679,28 @@
}
}
+ /**
+ * Try opening the given ringtone locally first, but failover to
+ * {@link IRingtonePlayer} if we can't access it directly. Typically happens
+ * when process doesn't hold
+ * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
+ */
+ private static InputStream openRingtone(Context context, Uri uri) throws IOException {
+ final ContentResolver resolver = context.getContentResolver();
+ try {
+ return resolver.openInputStream(uri);
+ } catch (SecurityException | IOException e) {
+ Log.w(TAG, "Failed to open directly; attempting failover: " + e);
+ final IRingtonePlayer player = context.getSystemService(AudioManager.class)
+ .getRingtonePlayer();
+ try {
+ return new ParcelFileDescriptor.AutoCloseInputStream(player.openRingtone(uri));
+ } catch (Exception e2) {
+ throw new IOException(e2);
+ }
+ }
+ }
+
private static String getSettingForType(int type) {
if ((type & TYPE_RINGTONE) != 0) {
return Settings.System.RINGTONE;
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 55fb82b..56d3c99 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -17,7 +17,9 @@
package android.media.audiopolicy;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioSystem;
@@ -36,19 +38,28 @@
private int mRouteFlags;
private String mRegistrationId;
private int mMixType = MIX_TYPE_INVALID;
+
+ // written by AudioPolicy
int mMixState = MIX_STATE_DISABLED;
int mCallbackFlags;
+ // initialized in constructor, read by AudioPolicyConfig
+ final int mDeviceId;
+ final String mDeviceAddress;
+
/**
* All parameters are guaranteed valid through the Builder.
*/
- private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags) {
+ private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags,
+ int deviceId, String deviceAddress) {
mRule = rule;
mFormat = format;
mRouteFlags = routeFlags;
mRegistrationId = null;
mMixType = rule.getTargetMixType();
mCallbackFlags = callbackFlags;
+ mDeviceId = deviceId;
+ mDeviceAddress = deviceAddress;
}
// CALLBACK_FLAG_* values: keep in sync with AudioMix::kCbFlag* values defined
@@ -74,6 +85,8 @@
@SystemApi
public static final int ROUTE_FLAG_LOOP_BACK = 0x1 << 1;
+ private static final int ROUTE_FLAG_SUPPORTED = ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK;
+
// MIX_TYPE_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h
/**
* @hide
@@ -172,6 +185,8 @@
private AudioFormat mFormat = null;
private int mRouteFlags = 0;
private int mCallbackFlags = 0;
+ private int mDeviceId = -1;
+ private String mDeviceAddress = null;
/**
* @hide
@@ -200,7 +215,7 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- public Builder setMixingRule(AudioMixingRule rule)
+ Builder setMixingRule(AudioMixingRule rule)
throws IllegalArgumentException {
if (rule == null) {
throw new IllegalArgumentException("Illegal null AudioMixingRule argument");
@@ -216,7 +231,7 @@
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
- public Builder setCallbackFlags(int flags) throws IllegalArgumentException {
+ Builder setCallbackFlags(int flags) throws IllegalArgumentException {
if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) {
throw new IllegalArgumentException("Illegal callback flags 0x"
+ Integer.toHexString(flags).toUpperCase());
@@ -226,6 +241,19 @@
}
/**
+ * @hide
+ * Only used by AudioPolicyConfig, not a public API.
+ * @param deviceId
+ * @param address
+ * @return the same Builder instance.
+ */
+ Builder setDevice(int deviceId, String address) {
+ mDeviceId = deviceId;
+ mDeviceAddress = address;
+ return this;
+ }
+
+ /**
* Sets the {@link AudioFormat} for the mix.
* @param format a non-null {@link AudioFormat} instance.
* @return the same Builder instance.
@@ -242,7 +270,8 @@
}
/**
- * Sets the routing behavior for the mix.
+ * Sets the routing behavior for the mix. If not set, routing behavior will default to
+ * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}.
* @param routeFlags one of {@link AudioMix#ROUTE_FLAG_LOOP_BACK},
* {@link AudioMix#ROUTE_FLAG_RENDER}
* @return the same Builder instance.
@@ -254,15 +283,41 @@
if (routeFlags == 0) {
throw new IllegalArgumentException("Illegal empty route flags");
}
- if ((routeFlags & (ROUTE_FLAG_LOOP_BACK | ROUTE_FLAG_RENDER)) == 0) {
+ if ((routeFlags & ROUTE_FLAG_SUPPORTED) == 0) {
throw new IllegalArgumentException("Invalid route flags 0x"
- + Integer.toHexString(routeFlags) + "when creating an AudioMix");
+ + Integer.toHexString(routeFlags) + "when configuring an AudioMix");
+ }
+ if ((routeFlags & ~ROUTE_FLAG_SUPPORTED) != 0) {
+ throw new IllegalArgumentException("Unknown route flags 0x"
+ + Integer.toHexString(routeFlags) + "when configuring an AudioMix");
}
mRouteFlags = routeFlags;
return this;
}
/**
+ * Sets the audio device used for playback. Cannot be used in the context of an audio
+ * policy used to inject audio to be recorded, or in a mix whose route flags doesn't
+ * specify {@link AudioMix#ROUTE_FLAG_RENDER}.
+ * @param device a non-null AudioDeviceInfo describing the audio device to play the output
+ * of this mix.
+ * @return the same Builder instance
+ * @throws IllegalArgumentException
+ */
+ @SystemApi
+ public Builder setDevice(@NonNull AudioDeviceInfo device) throws IllegalArgumentException {
+ if (device == null) {
+ throw new IllegalArgumentException("Illegal null AudioDeviceInfo argument");
+ }
+ if (!device.isSink()) {
+ throw new IllegalArgumentException("Unsupported device type on mix, not a sink");
+ }
+ mDeviceId = device.getId();
+ mDeviceAddress = device.getAddress();
+ return this;
+ }
+
+ /**
* Combines all of the settings and return a new {@link AudioMix} object.
* @return a new {@link AudioMix} object
* @throws IllegalArgumentException if no {@link AudioMixingRule} has been set.
@@ -273,8 +328,13 @@
throw new IllegalArgumentException("Illegal null AudioMixingRule");
}
if (mRouteFlags == 0) {
- // no route flags set, use default
- mRouteFlags = ROUTE_FLAG_RENDER;
+ // no route flags set, use default as described in Builder.setRouteFlags(int)
+ mRouteFlags = ROUTE_FLAG_LOOP_BACK;
+ }
+ // can't do loop back AND render at same time in this implementation
+ if (mRouteFlags == (ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK)) {
+ throw new IllegalArgumentException("Unsupported route behavior combination 0x" +
+ Integer.toHexString(mRouteFlags));
}
if (mFormat == null) {
// FIXME Can we eliminate this? Will AudioMix work with an unspecified sample rate?
@@ -284,7 +344,22 @@
}
mFormat = new AudioFormat.Builder().setSampleRate(rate).build();
}
- return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags);
+ if (mDeviceId != -1) {
+ if ((mRouteFlags & ROUTE_FLAG_RENDER) == 0) {
+ throw new IllegalArgumentException(
+ "Can't have audio device without flag ROUTE_FLAG_RENDER");
+ }
+ if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) {
+ throw new IllegalArgumentException("Unsupported device on non-playback mix");
+ }
+ } else {
+ if ((mRouteFlags & ROUTE_FLAG_RENDER) == ROUTE_FLAG_RENDER) {
+ throw new IllegalArgumentException(
+ "Can't have flag ROUTE_FLAG_RENDER without an audio device");
+ }
+ }
+ return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceId,
+ mDeviceAddress);
}
}
}
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 5d2bac0..3af3ae7 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -17,6 +17,8 @@
package android.media.audiopolicy;
import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioPatch;
import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion;
import android.os.Parcel;
import android.os.Parcelable;
@@ -81,6 +83,9 @@
dest.writeInt(mix.getRouteFlags());
// write callback flags
dest.writeInt(mix.mCallbackFlags);
+ // write device information
+ dest.writeInt(mix.mDeviceId);
+ dest.writeString(mix.mDeviceAddress);
// write mix format
dest.writeInt(mix.getFormat().getSampleRate());
dest.writeInt(mix.getFormat().getEncoding());
@@ -104,6 +109,8 @@
mixBuilder.setRouteFlags(routeFlags);
// read callback flags
mixBuilder.setCallbackFlags(in.readInt());
+ // read device information
+ mixBuilder.setDevice(in.readInt(), in.readString());
// read mix format
int sampleRate = in.readInt();
int encoding = in.readInt();
@@ -197,8 +204,14 @@
int mixIndex = 0;
for (AudioMix mix : mMixes) {
if (!mRegistrationId.isEmpty()) {
- mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
- + mixIndex++);
+ if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) ==
+ AudioMix.ROUTE_FLAG_LOOP_BACK) {
+ mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
+ + mixIndex++);
+ } else if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_RENDER) ==
+ AudioMix.ROUTE_FLAG_RENDER) {
+ mix.setRegistration(mix.mDeviceAddress);
+ }
} else {
mix.setRegistration("");
}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index ada0e2c..fe2796c 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -365,7 +365,7 @@
if (options == null) {
throw new IllegalArgumentException("options are null");
}
- subscribeInternal(parentId, options, callback);
+ subscribeInternal(parentId, new Bundle(options), callback);
}
/**
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index c61d7ad..95cb8ae 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -154,8 +154,8 @@
metadata.getString(MediaMetadata.METADATA_KEY_WRITER));
}
if (metadata.containsKey(MediaMetadata.METADATA_KEY_YEAR)) {
- oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_YEAR),
- metadata.getString(MediaMetadata.METADATA_KEY_YEAR));
+ oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_YEAR),
+ metadata.getLong(MediaMetadata.METADATA_KEY_YEAR));
}
return oldMetadata;
}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index a332195..e8c50e3 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -575,16 +575,11 @@
/**
* The original network ID of this TV channel.
*
- * <p>This is used to identify the originating delivery system, if applicable. Use the same
- * coding for {@code original_network_id} in the underlying broadcast standard if it is
- * defined there (e.g. ETSI EN 300 468/TR 101 211 and ARIB STD-B10). If channels cannot be
- * globally identified by 2-tuple {{@link #COLUMN_TRANSPORT_STREAM_ID},
- * {@link #COLUMN_SERVICE_ID}}, one must carefully assign a value to this field to form a
- * unique 3-tuple identification {{@code COLUMN_ORIGINAL_NETWORK_ID},
- * {@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}} for its channels.
+ * <p>It is used to identify the originating delivery system, if applicable. Use the same
+ * coding for {@code original_network_id} for ETSI EN 300 468/TR 101 211 and ARIB STD-B10.
*
- * <p>This is a required field if the channel cannot be uniquely identified by a 2-tuple
- * {{@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}}.
+ * <p>This is a required field only if the underlying broadcast standard defines the same
+ * name field. Otherwise, leave empty.
*
* <p>Type: INTEGER
*/
@@ -593,13 +588,13 @@
/**
* The transport stream ID of this channel.
*
- * <p>This is used to identify the Transport Stream that contains the current channel from
- * any other multiplex within a network, if applicable. Use the same coding for
+ * <p>It is used to identify the Transport Stream that contains the current channel from any
+ * other multiplex within a network, if applicable. Use the same coding for
* {@code transport_stream_id} defined in ISO/IEC 13818-1 if the channel is transmitted via
- * the MPEG Transport Stream as is the case for many digital broadcast standards.
+ * the MPEG Transport Stream.
*
- * <p>This is a required field if the current channel is transmitted via the MPEG Transport
- * Stream.
+ * <p>This is a required field only if the current channel is transmitted via the MPEG
+ * Transport Stream. Leave empty otherwise.
*
* <p>Type: INTEGER
*/
@@ -608,15 +603,13 @@
/**
* The service ID of this channel.
*
- * <p>This is used to identify the current service (roughly equivalent to channel) from any
- * other service within the Transport Stream, if applicable. Use the same coding for
- * {@code service_id} in the underlying broadcast standard if it is defined there (e.g. ETSI
- * EN 300 468 and ARIB STD-B10) or {@code program_number} (which usually has the same value
- * as {@code service_id}) in ISO/IEC 13818-1 if the channel is transmitted via the MPEG
- * Transport Stream.
+ * <p>It is used to identify the current service, or channel from any other services within
+ * a given Transport Stream, if applicable. Use the same coding for {@code service_id} in
+ * ETSI EN 300 468 and ARIB STD-B10 or {@code program_number} in ISO/IEC 13818-1.
*
- * <p>This is a required field if the current channel is transmitted via the MPEG Transport
- * Stream.
+ * <p>This is a required field only if the underlying broadcast standard defines the same
+ * name field, or the current channel is transmitted via the MPEG Transport Stream. Leave
+ * empty otherwise.
*
* <p>Type: INTEGER
*/
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 63e3edc..7c9591d 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -349,12 +349,12 @@
/**
* Returns the number of tuners this TV input has.
*
- * <p>This method is valid only for the input of type {@link #TYPE_TUNER}.
+ * <p>This method is valid only for inputs of type {@link #TYPE_TUNER}. For inputs of other
+ * types, it returns 0.
*
* <p>Tuners correspond to physical/logical resources that allow reception of TV signal. Having
* <i>N</i> tuners means that the TV input is capable of receiving <i>N</i> different channels
* concurrently.
- *
*/
public int getTunerCount() {
return mTunerCount;
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 0b0306c..89e4577 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.graphics.Rect;
import android.media.PlaybackParams;
@@ -1241,6 +1242,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
public void setParentalControlsEnabled(boolean enabled) {
try {
mService.setParentalControlsEnabled(enabled, mUserId);
@@ -1292,6 +1294,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
public void addBlockedRating(@NonNull TvContentRating rating) {
Preconditions.checkNotNull(rating);
try {
@@ -1310,6 +1313,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
public void removeBlockedRating(@NonNull TvContentRating rating) {
Preconditions.checkNotNull(rating);
try {
@@ -1444,6 +1448,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
public List<TvInputHardwareInfo> getHardwareList() {
try {
return mService.getHardwareList();
@@ -1462,6 +1467,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback,
TvInputInfo info) {
try {
@@ -1488,6 +1494,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
public void releaseTvInputHardware(int deviceId, Hardware hardware) {
try {
mService.releaseTvInputHardware(deviceId, hardware.getInterface(), mUserId);
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 8fb58b5..bc20c17 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -491,7 +491,7 @@
* until this method is called.
*
* <p>The TV input service must call this method as soon as the content rendered onto its
- * surface is ready for viewing. This method must be called each time {@link #onTune(Uri)}
+ * surface is ready for viewing. This method must be called each time {@link #onTune}
* is called.
*
* @see #notifyVideoUnavailable
@@ -837,14 +837,15 @@
public abstract boolean onTune(Uri channelUri);
/**
- * Calls {@link #onTune(Uri)}. Override this method in order to handle {@code params}.
+ * Calls {@link #onTune(Uri)}. Override this method in order to handle domain-specific
+ * features that are only known between certain TV inputs and their clients.
*
* @param channelUri The URI of the channel.
- * @param params The extra parameters from other applications.
+ * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers
+ * will not create conflicting keys.
* @return {@code true} if the tuning was successful, {@code false} otherwise.
- * @hide
*/
- @SystemApi
public boolean onTune(Uri channelUri, Bundle params) {
return onTune(channelUri);
}
@@ -1209,7 +1210,7 @@
}
/**
- * Calls {@link #onTune}.
+ * Calls {@link #onTune(Uri, Bundle)}.
*/
void tune(Uri channelUri, Bundle params) {
mCurrentPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
@@ -1836,7 +1837,7 @@
* a hardware TV Input (e.g. HDMI 1) and forward the application's surface to the session so
* that the user can see the screen of the hardware TV Input when she tunes to a channel from
* this TV input. The implementation of this class is expected to change the channel of the
- * external set-top box via a proprietary protocol when {@link HardwareSession#onTune(Uri)} is
+ * external set-top box via a proprietary protocol when {@link HardwareSession#onTune} is
* requested by the application.
*
* <p>Note that this class is not for inputs for internal hardware like built-in tuner and HDMI
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 6a44b1e..e623353 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -121,6 +121,8 @@
/**
* Returns the audio channel count. Valid only for {@link #TYPE_AUDIO} tracks.
+ *
+ * @throws IllegalStateException if not called on an audio track
*/
public final int getAudioChannelCount() {
if (mType != TYPE_AUDIO) {
@@ -131,6 +133,8 @@
/**
* Returns the audio sample rate, in the unit of Hz. Valid only for {@link #TYPE_AUDIO} tracks.
+ *
+ * @throws IllegalStateException if not called on an audio track
*/
public final int getAudioSampleRate() {
if (mType != TYPE_AUDIO) {
@@ -142,6 +146,8 @@
/**
* Returns the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
* tracks.
+ *
+ * @throws IllegalStateException if not called on a video track
*/
public final int getVideoWidth() {
if (mType != TYPE_VIDEO) {
@@ -153,6 +159,8 @@
/**
* Returns the height of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
* tracks.
+ *
+ * @throws IllegalStateException if not called on a video track
*/
public final int getVideoHeight() {
if (mType != TYPE_VIDEO) {
@@ -164,6 +172,8 @@
/**
* Returns the frame rate of the video, in the unit of fps (frames per second). Valid only for
* {@link #TYPE_VIDEO} tracks.
+ *
+ * @throws IllegalStateException if not called on a video track
*/
public final float getVideoFrameRate() {
if (mType != TYPE_VIDEO) {
@@ -175,6 +185,8 @@
/**
* Returns the pixel aspect ratio (the ratio of a pixel's width to its height) of the video.
* Valid only for {@link #TYPE_VIDEO} tracks.
+ *
+ * @throws IllegalStateException if not called on a video track
*/
public final float getVideoPixelAspectRatio() {
if (mType != TYPE_VIDEO) {
@@ -189,6 +201,8 @@
*
* <p>The complete list of values are defined in ETSI TS 101 154 V1.7.1 Annex B, ATSC A/53 Part
* 4 and SMPTE 2016-1-2007.
+ *
+ * @throws IllegalStateException if not called on a video track
*/
public final byte getVideoActiveFormatDescription() {
if (mType != TYPE_VIDEO) {
@@ -268,6 +282,8 @@
* @param type The type of the track.
* @param id The ID of the track that uniquely identifies the current track among all the
* other tracks in the same TV program.
+ * @throws IllegalArgumentException if the type is not any of {@link #TYPE_AUDIO},
+ * {@link #TYPE_VIDEO} and {@link #TYPE_SUBTITLE}
*/
public Builder(int type, @NonNull String id) {
if (type != TYPE_AUDIO
@@ -304,6 +320,7 @@
* Sets the audio channel count. Valid only for {@link #TYPE_AUDIO} tracks.
*
* @param audioChannelCount The audio channel count.
+ * @throws IllegalStateException if not called on an audio track
*/
public final Builder setAudioChannelCount(int audioChannelCount) {
if (mType != TYPE_AUDIO) {
@@ -318,6 +335,7 @@
* tracks.
*
* @param audioSampleRate The audio sample rate.
+ * @throws IllegalStateException if not called on an audio track
*/
public final Builder setAudioSampleRate(int audioSampleRate) {
if (mType != TYPE_AUDIO) {
@@ -332,6 +350,7 @@
* tracks.
*
* @param videoWidth The width of the video.
+ * @throws IllegalStateException if not called on a video track
*/
public final Builder setVideoWidth(int videoWidth) {
if (mType != TYPE_VIDEO) {
@@ -346,6 +365,7 @@
* tracks.
*
* @param videoHeight The height of the video.
+ * @throws IllegalStateException if not called on a video track
*/
public final Builder setVideoHeight(int videoHeight) {
if (mType != TYPE_VIDEO) {
@@ -360,6 +380,7 @@
* {@link #TYPE_VIDEO} tracks.
*
* @param videoFrameRate The frame rate of the video.
+ * @throws IllegalStateException if not called on a video track
*/
public final Builder setVideoFrameRate(float videoFrameRate) {
if (mType != TYPE_VIDEO) {
@@ -379,6 +400,7 @@
* pixel aspect ratio for most video formats.
*
* @param videoPixelAspectRatio The pixel aspect ratio of the video.
+ * @throws IllegalStateException if not called on a video track
*/
public final Builder setVideoPixelAspectRatio(float videoPixelAspectRatio) {
if (mType != TYPE_VIDEO) {
@@ -398,6 +420,7 @@
* 4 and SMPTE 2016-1-2007.
*
* @param videoActiveFormatDescription The AFD code of the video.
+ * @throws IllegalStateException if not called on a video track
*/
public final Builder setVideoActiveFormatDescription(byte videoActiveFormatDescription) {
if (mType != TYPE_VIDEO) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 5c4b528..9623076 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.graphics.Canvas;
@@ -56,7 +57,7 @@
* TV inputs available on the system can be obtained by calling
* {@link TvInputManager#getTvInputList() TvInputManager.getTvInputList()}.)
*
- * <p>Once the application supplies the URI for a specific TV channel to {@link #tune(String, Uri)}
+ * <p>Once the application supplies the URI for a specific TV channel to {@link #tune}
* method, it takes care of underlying service binding (and unbinding if the current TvView is
* already bound to a service) and automatically allocates/deallocates resources needed. In addition
* to a few essential methods to control how the contents are presented, it also provides a way to
@@ -206,13 +207,18 @@
}
/**
- * Sets the Z order of a window owning the surface of this TvView above the normal TvView
- * but below an application.
+ * Controls whether the TvView's surface is placed on top of another regular surface view in the
+ * window (but still behind the window itself).
+ * This is typically used to place overlays on top of an underlying TvView.
*
- * @see SurfaceView#setZOrderMediaOverlay
- * @hide
+ * <p>Note that this must be set before the TvView's containing window is attached to the
+ * window manager.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+ *
+ * @param isMediaOverlay {@code true} to be on top of another regular surface, {@code false}
+ * otherwise.
*/
- @SystemApi
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
if (isMediaOverlay) {
mWindowZOrder = ZORDER_MEDIA_OVERLAY;
@@ -230,12 +236,18 @@
}
/**
- * Sets the Z order of a window owning the surface of this TvView on top of an application.
+ * Controls whether the TvView's surface is placed on top of its window. Normally it is placed
+ * behind the window, to allow it to (for the most part) appear to composite with the views in
+ * the hierarchy. By setting this, you cause it to be placed above the window. This means that
+ * none of the contents of the window this TvView is in will be visible on top of its surface.
*
- * @see SurfaceView#setZOrderOnTop
- * @hide
+ * <p>Note that this must be set before the TvView's containing window is attached to the window
+ * manager.
+ *
+ * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+ *
+ * @param onTop {@code true} to be on top of its window, {@code false} otherwise.
*/
- @SystemApi
public void setZOrderOnTop(boolean onTop) {
if (onTop) {
mWindowZOrder = ZORDER_ON_TOP;
@@ -280,14 +292,15 @@
}
/**
- * Tunes to a given channel.
+ * Tunes to a given channel. This can be used to provide domain-specific features that are only
+ * known between certain TvView applications and their TV inputs.
*
* @param inputId The ID of TV input for the given channel.
* @param channelUri The URI of a channel.
- * @param params Extra parameters.
- * @hide
+ * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+ * name, i.e. prefixed with a package name you own, so that different developers will
+ * not create conflicting keys.
*/
- @SystemApi
public void tune(String inputId, Uri channelUri, Bundle params) {
if (DEBUG) Log.d(TAG, "tune(" + channelUri + ")");
if (TextUtils.isEmpty(inputId)) {
@@ -360,11 +373,8 @@
*
* @param unblockedRating A TvContentRating to unblock.
* @see TvInputService.Session#notifyContentBlocked(TvContentRating)
- * @hide
- * @deprecated Use {@link #unblockContent} instead.
+ * @removed
*/
- @Deprecated
- @SystemApi
public void requestUnblockContent(TvContentRating unblockedRating) {
unblockContent(unblockedRating);
}
@@ -379,6 +389,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS)
public void unblockContent(TvContentRating unblockedRating) {
if (mSession != null) {
mSession.unblockContent(unblockedRating);
@@ -539,7 +550,7 @@
}
/**
- * Calls {@link TvInputService.Session#appPrivateCommand(String, Bundle)} for the current
+ * Calls {@link TvInputService.Session#onAppPrivateCommand(String, Bundle)} for the current
* session.
*
* @param action The name of the private command to send. This <em>must</em> be a scoped name,
@@ -893,7 +904,7 @@
/**
* This is invoked when the channel of this TvView is changed by the underlying TV input
- * without any {@link TvView#tune(String, Uri)} request.
+ * without any {@link TvView#tune} request.
*
* @param inputId The ID of the TV input bound to this view.
* @param channelUri The URI of a channel.
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index b5ea2a0..f593685 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -370,15 +370,14 @@
* called when the loading is complete.
* </p><p>
* In case the media item does not have any children, call {@link Result#sendResult}
- * with an empty list which is not {@code null}. If {@code null} is sent that means
- * the given {@code parentId} is invalid and {@link MediaBrowser.SubscriptionCallback#onError}
- * will be called.
+ * with an empty list. When the given {@code parentId} is invalid, implementations must
+ * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
+ * {@link MediaBrowser.SubscriptionCallback#onError}.
* </p>
*
* @param parentId The id of the parent media item whose children are to be
* queried.
- * @param result The Result to send the list of children to. Send null if the
- * id is invalid.
+ * @param result The Result to send the list of children to.
*/
public abstract void onLoadChildren(@NonNull String parentId,
@NonNull Result<List<MediaBrowser.MediaItem>> result);
@@ -394,15 +393,14 @@
* called when the loading is complete.
* </p><p>
* In case the media item does not have any children, call {@link Result#sendResult}
- * with an empty list which is not {@code null}. If {@code null} is sent that means
- * the given {@code parentId} is invalid and {@link MediaBrowser.SubscriptionCallback#onError}
- * will be called.
+ * with an empty list. When the given {@code parentId} is invalid, implementations must
+ * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
+ * {@link MediaBrowser.SubscriptionCallback#onError}.
* </p>
*
* @param parentId The id of the parent media item whose children are to be
* queried.
- * @param result The Result to send the list of children to. Send null if the
- * id is invalid.
+ * @param result The Result to send the list of children to.
* @param options A bundle of service-specific arguments sent from the media
* browse. The information returned through the result should be
* affected by the contents of this bundle.
@@ -424,13 +422,18 @@
* result.detach} may be called before returning from this function, and
* then {@link Result#sendResult result.sendResult} called when the item has
* been loaded.
- * <p>
- * The default implementation sends a null result.
+ * </p><p>
+ * When the given {@code itemId} is invalid, implementations must call
+ * {@link Result#sendResult result.sendResult} with {@code null}, which will
+ * invoke {@link MediaBrowser.ItemCallback#onError}.
+ * </p><p>
+ * The default implementation calls {@link Result#sendResult result.sendResult}
+ * with {@code null}.
+ * </p>
*
* @param itemId The id for the specific
* {@link android.media.browse.MediaBrowser.MediaItem}.
- * @param result The Result to send the item to. Send null if the id is
- * invalid.
+ * @param result The Result to send the item to.
*/
public void onLoadItem(String itemId, Result<MediaBrowser.MediaItem> result) {
result.sendResult(null);
diff --git a/media/jni/android_media_ExifInterface.cpp b/media/jni/android_media_ExifInterface.cpp
index f7481af..418a3f2 100644
--- a/media/jni/android_media_ExifInterface.cpp
+++ b/media/jni/android_media_ExifInterface.cpp
@@ -19,12 +19,15 @@
#include "android_media_Utils.h"
+#include "android/graphics/CreateJavaOutputStreamAdaptor.h"
#include "src/piex_types.h"
#include "src/piex.h"
#include <jni.h>
#include <JNIHelp.h>
+#include <androidfw/Asset.h>
#include <android_runtime/AndroidRuntime.h>
+#include <android/graphics/Utils.h>
#include <nativehelper/ScopedLocalRef.h>
#include <utils/Log.h>
@@ -35,6 +38,9 @@
using namespace android;
+static const char kJpegSignatureChars[] = {(char)0xff, (char)0xd8, (char)0xff};
+static const int kJpegSignatureSize = 3;
+
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className);
@@ -82,18 +88,48 @@
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
}
-static jobject ExifInterface_getRawMetadata(
- JNIEnv* env, jclass /* clazz */, jobject jfileDescriptor) {
- int fd = jniGetFDFromFileDescriptor(env, jfileDescriptor);
- if (fd < 0) {
- ALOGI("Invalid file descriptor");
+static bool is_asset_stream(const SkStream& stream) {
+ return stream.hasLength() && stream.hasPosition();
+}
+
+static jobject ExifInterface_getThumbnailFromAsset(
+ JNIEnv* env, jclass /* clazz */, jlong jasset, jint jthumbnailOffset,
+ jint jthumbnailLength) {
+ Asset* asset = reinterpret_cast<Asset*>(jasset);
+ std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset));
+
+ std::unique_ptr<jbyte[]> thumbnailData(new jbyte[(int)jthumbnailLength]);
+ if (thumbnailData.get() == NULL) {
+ ALOGI("No memory to get thumbnail");
return NULL;
}
- piex::PreviewImageData image_data;
- std::unique_ptr<FileStream> stream(new FileStream(fd));
+ // Do not know the current offset. So rewind it.
+ stream->rewind();
- if (!GetExifFromRawImage(stream.get(), String8("[file descriptor]"), image_data)) {
+ // Read thumbnail.
+ stream->skip((int)jthumbnailOffset);
+ stream->read((void*)thumbnailData.get(), (int)jthumbnailLength);
+
+ // Copy to the byte array.
+ jbyteArray byteArray = env->NewByteArray(jthumbnailLength);
+ env->SetByteArrayRegion(byteArray, 0, jthumbnailLength, thumbnailData.get());
+ return byteArray;
+}
+
+static jobject getRawAttributes(JNIEnv* env, SkStream* stream, bool returnThumbnail) {
+ std::unique_ptr<SkStream> streamDeleter(stream);
+
+ std::unique_ptr<::piex::StreamInterface> piexStream;
+ if (is_asset_stream(*stream)) {
+ piexStream.reset(new AssetStream(streamDeleter.release()));
+ } else {
+ piexStream.reset(new BufferedStream(streamDeleter.release()));
+ }
+
+ piex::PreviewImageData image_data;
+
+ if (!GetExifFromRawImage(piexStream.get(), String8("[piex stream]"), image_data)) {
ALOGI("Raw image not detected");
return NULL;
}
@@ -253,7 +289,117 @@
}
}
- return KeyedVectorToHashMap(env, map);
+ jobject hashMap = KeyedVectorToHashMap(env, map);
+
+ if (returnThumbnail) {
+ std::unique_ptr<jbyte[]> thumbnailData(new jbyte[image_data.thumbnail.length]);
+ if (thumbnailData.get() == NULL) {
+ ALOGE("No memory to parse a thumbnail");
+ return NULL;
+ }
+ jbyteArray jthumbnailByteArray = env->NewByteArray(image_data.thumbnail.length);
+ if (jthumbnailByteArray == NULL) {
+ ALOGE("No memory to parse a thumbnail");
+ return NULL;
+ }
+ piexStream.get()->GetData(image_data.thumbnail.offset, image_data.thumbnail.length,
+ (uint8_t*)thumbnailData.get());
+ env->SetByteArrayRegion(
+ jthumbnailByteArray, 0, image_data.thumbnail.length, thumbnailData.get());
+ jstring jkey = env->NewStringUTF(String8("thumbnailData"));
+ env->CallObjectMethod(hashMap, gFields.hashMap.put, jkey, jthumbnailByteArray);
+ env->DeleteLocalRef(jkey);
+ env->DeleteLocalRef(jthumbnailByteArray);
+ }
+ return hashMap;
+}
+
+static jobject ExifInterface_getRawAttributesFromAsset(
+ JNIEnv* env, jclass /* clazz */, jlong jasset) {
+ std::unique_ptr<char[]> jpegSignature(new char[kJpegSignatureSize]);
+ if (jpegSignature.get() == NULL) {
+ ALOGE("No enough memory to parse");
+ return NULL;
+ }
+
+ Asset* asset = reinterpret_cast<Asset*>(jasset);
+ std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset));
+
+ if (stream.get()->read(jpegSignature.get(), kJpegSignatureSize) != kJpegSignatureSize) {
+ // Rewind the stream.
+ stream.get()->rewind();
+
+ ALOGI("Corrupted image.");
+ return NULL;
+ }
+
+ // Rewind the stream.
+ stream.get()->rewind();
+
+ if (memcmp(jpegSignature.get(), kJpegSignatureChars, kJpegSignatureSize) == 0) {
+ ALOGI("Should be a JPEG stream.");
+ return NULL;
+ }
+
+ // Try to parse from the given stream.
+ jobject result = getRawAttributes(env, stream.get(), false);
+
+ // Rewind the stream for the chance to read JPEG.
+ if (result == NULL) {
+ stream.get()->rewind();
+ }
+ return result;
+}
+
+static jobject ExifInterface_getRawAttributesFromFileDescriptor(
+ JNIEnv* env, jclass /* clazz */, jobject jfileDescriptor) {
+ std::unique_ptr<char[]> jpegSignature(new char[kJpegSignatureSize]);
+ if (jpegSignature.get() == NULL) {
+ ALOGE("No enough memory to parse");
+ return NULL;
+ }
+
+ int fd = jniGetFDFromFileDescriptor(env, jfileDescriptor);
+ if (fd < 0) {
+ ALOGI("Invalid file descriptor");
+ return NULL;
+ }
+
+ // Restore the file descriptor's offset on exiting this function.
+ AutoFDSeek autoRestore(fd);
+
+ int dupFd = dup(fd);
+
+ FILE* file = fdopen(dupFd, "r");
+ if (file == NULL) {
+ ALOGI("Failed to open the file descriptor");
+ return NULL;
+ }
+
+ if (fgets(jpegSignature.get(), kJpegSignatureSize, file) == NULL) {
+ ALOGI("Corrupted image.");
+ return NULL;
+ }
+
+ if (memcmp(jpegSignature.get(), kJpegSignatureChars, kJpegSignatureSize) == 0) {
+ ALOGI("Should be a JPEG stream.");
+ return NULL;
+ }
+
+ // Rewind the file descriptor.
+ fseek(file, 0L, SEEK_SET);
+
+ std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file,
+ SkFILEStream::kCallerPasses_Ownership));
+ return getRawAttributes(env, fileStream.get(), false);
+}
+
+static jobject ExifInterface_getRawAttributesFromInputStream(
+ JNIEnv* env, jclass /* clazz */, jobject jinputStream) {
+ jbyteArray byteArray = env->NewByteArray(8*1024);
+ ScopedLocalRef<jbyteArray> scoper(env, byteArray);
+ std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, jinputStream, scoper.get()));
+ return getRawAttributes(env, stream.get(), true);
}
} // extern "C"
@@ -261,9 +407,14 @@
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- { "initRawNative", "()V", (void *)ExifInterface_initRaw },
- { "getRawAttributesNative", "(Ljava/io/FileDescriptor;)Ljava/util/HashMap;",
- (void*)ExifInterface_getRawMetadata },
+ { "nativeInitRaw", "()V", (void *)ExifInterface_initRaw },
+ { "nativeGetThumbnailFromAsset", "(JII)[B", (void *)ExifInterface_getThumbnailFromAsset },
+ { "nativeGetRawAttributesFromAsset", "(J)Ljava/util/HashMap;",
+ (void*)ExifInterface_getRawAttributesFromAsset },
+ { "nativeGetRawAttributesFromFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/util/HashMap;",
+ (void*)ExifInterface_getRawAttributesFromFileDescriptor },
+ { "nativeGetRawAttributesFromInputStream", "(Ljava/io/InputStream;)Ljava/util/HashMap;",
+ (void*)ExifInterface_getRawAttributesFromInputStream },
};
int register_android_media_ExifInterface(JNIEnv *env) {
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 9e90a19..c3993ae 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -16,6 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ImageReader_JNI"
+#include "android_media_Utils.h"
#include <utils/Log.h>
#include <utils/misc.h>
#include <utils/List.h>
@@ -23,7 +24,6 @@
#include <cstdio>
-#include <gui/CpuConsumer.h>
#include <gui/BufferItemConsumer.h>
#include <gui/Surface.h>
#include <camera3.h>
@@ -37,8 +37,6 @@
#include <stdint.h>
#include <inttypes.h>
-#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
-
#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext"
#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mNativeBuffer"
#define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID "mTimestamp"
@@ -47,9 +45,6 @@
using namespace android;
-enum {
- IMAGE_READER_MAX_NUM_PLANES = 3,
-};
enum {
ACQUIRE_SUCCESS = 0,
@@ -65,6 +60,7 @@
static struct {
jfieldID mNativeBuffer;
jfieldID mTimestamp;
+ jfieldID mPlanes;
} gSurfaceImageClassInfo;
static struct {
@@ -89,21 +85,12 @@
virtual void onFrameAvailable(const BufferItem& item);
- CpuConsumer::LockedBuffer* getLockedBuffer();
- void returnLockedBuffer(CpuConsumer::LockedBuffer* buffer);
+ BufferItem* getBufferItem();
+ void returnBufferItem(BufferItem* buffer);
- BufferItem* getOpaqueBuffer();
- void returnOpaqueBuffer(BufferItem* buffer);
- void setCpuConsumer(const sp<CpuConsumer>& consumer) { mConsumer = consumer; }
- CpuConsumer* getCpuConsumer() { return mConsumer.get(); }
-
- void setOpaqueConsumer(const sp<BufferItemConsumer>& consumer) { mOpaqueConsumer = consumer; }
- BufferItemConsumer* getOpaqueConsumer() { return mOpaqueConsumer.get(); }
- // This is the only opaque format exposed in the ImageFormat public API.
- // Note that we do support CPU access for HAL_PIXEL_FORMAT_RAW_OPAQUE
- // (ImageFormat#RAW_PRIVATE) so it doesn't count as opaque here.
- bool isOpaque() { return mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; }
+ void setBufferConsumer(const sp<BufferItemConsumer>& consumer) { mConsumer = consumer; }
+ BufferItemConsumer* getBufferConsumer() { return mConsumer.get(); }
void setProducer(const sp<IGraphicBufferProducer>& producer) { mProducer = producer; }
IGraphicBufferProducer* getProducer() { return mProducer.get(); }
@@ -124,10 +111,8 @@
static JNIEnv* getJNIEnv(bool* needsDetach);
static void detachJNI();
- List<CpuConsumer::LockedBuffer*> mBuffers;
- List<BufferItem*> mOpaqueBuffers;
- sp<CpuConsumer> mConsumer;
- sp<BufferItemConsumer> mOpaqueConsumer;
+ List<BufferItem*> mBuffers;
+ sp<BufferItemConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
jobject mWeakThiz;
jclass mClazz;
@@ -140,12 +125,14 @@
JNIImageReaderContext::JNIImageReaderContext(JNIEnv* env,
jobject weakThiz, jclass clazz, int maxImages) :
mWeakThiz(env->NewGlobalRef(weakThiz)),
- mClazz((jclass)env->NewGlobalRef(clazz)) {
+ mClazz((jclass)env->NewGlobalRef(clazz)),
+ mFormat(0),
+ mDataSpace(HAL_DATASPACE_UNKNOWN),
+ mWidth(-1),
+ mHeight(-1) {
for (int i = 0; i < maxImages; i++) {
- CpuConsumer::LockedBuffer *buffer = new CpuConsumer::LockedBuffer;
- BufferItem* opaqueBuffer = new BufferItem;
+ BufferItem* buffer = new BufferItem;
mBuffers.push_back(buffer);
- mOpaqueBuffers.push_back(opaqueBuffer);
}
}
@@ -174,36 +161,21 @@
}
}
-CpuConsumer::LockedBuffer* JNIImageReaderContext::getLockedBuffer() {
+BufferItem* JNIImageReaderContext::getBufferItem() {
if (mBuffers.empty()) {
return NULL;
}
- // Return a LockedBuffer pointer and remove it from the list
- List<CpuConsumer::LockedBuffer*>::iterator it = mBuffers.begin();
- CpuConsumer::LockedBuffer* buffer = *it;
+ // Return a BufferItem pointer and remove it from the list
+ List<BufferItem*>::iterator it = mBuffers.begin();
+ BufferItem* buffer = *it;
mBuffers.erase(it);
return buffer;
}
-void JNIImageReaderContext::returnLockedBuffer(CpuConsumer::LockedBuffer* buffer) {
+void JNIImageReaderContext::returnBufferItem(BufferItem* buffer) {
mBuffers.push_back(buffer);
}
-BufferItem* JNIImageReaderContext::getOpaqueBuffer() {
- if (mOpaqueBuffers.empty()) {
- return NULL;
- }
- // Return an opaque buffer pointer and remove it from the list
- List<BufferItem*>::iterator it = mOpaqueBuffers.begin();
- BufferItem* buffer = *it;
- mOpaqueBuffers.erase(it);
- return buffer;
-}
-
-void JNIImageReaderContext::returnOpaqueBuffer(BufferItem* buffer) {
- mOpaqueBuffers.push_back(buffer);
-}
-
JNIImageReaderContext::~JNIImageReaderContext() {
bool needsDetach = false;
JNIEnv* env = getJNIEnv(&needsDetach);
@@ -217,25 +189,15 @@
detachJNI();
}
- // Delete LockedBuffers
- for (List<CpuConsumer::LockedBuffer *>::iterator it = mBuffers.begin();
+ // Delete buffer items.
+ for (List<BufferItem *>::iterator it = mBuffers.begin();
it != mBuffers.end(); it++) {
delete *it;
}
- // Delete opaque buffers
- for (List<BufferItem *>::iterator it = mOpaqueBuffers.begin();
- it != mOpaqueBuffers.end(); it++) {
- delete *it;
- }
-
- mBuffers.clear();
if (mConsumer != 0) {
mConsumer.clear();
}
- if (mOpaqueConsumer != 0) {
- mOpaqueConsumer.clear();
- }
}
void JNIImageReaderContext::onFrameAvailable(const BufferItem& /*item*/)
@@ -257,11 +219,6 @@
extern "C" {
-static bool isFormatOpaque(int format) {
- // Only treat IMPLEMENTATION_DEFINED as an opaque format for now.
- return format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
-}
-
static JNIImageReaderContext* ImageReader_getContext(JNIEnv* env, jobject thiz)
{
JNIImageReaderContext *ctx;
@@ -270,24 +227,6 @@
return ctx;
}
-static CpuConsumer* ImageReader_getCpuConsumer(JNIEnv* env, jobject thiz)
-{
- ALOGV("%s:", __FUNCTION__);
- JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz);
- if (ctx == NULL) {
- jniThrowRuntimeException(env, "ImageReaderContext is not initialized");
- return NULL;
- }
-
- if (ctx->isOpaque()) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Opaque ImageReader doesn't support this method");
- return NULL;
- }
-
- return ctx->getCpuConsumer();
-}
-
static IGraphicBufferProducer* ImageReader_getProducer(JNIEnv* env, jobject thiz)
{
ALOGV("%s:", __FUNCTION__);
@@ -315,411 +254,7 @@
reinterpret_cast<jlong>(ctx.get()));
}
-static CpuConsumer::LockedBuffer* Image_getLockedBuffer(JNIEnv* env, jobject image)
-{
- return reinterpret_cast<CpuConsumer::LockedBuffer*>(
- env->GetLongField(image, gSurfaceImageClassInfo.mNativeBuffer));
-}
-
-static void Image_setBuffer(JNIEnv* env, jobject thiz,
- const CpuConsumer::LockedBuffer* buffer)
-{
- env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer, reinterpret_cast<jlong>(buffer));
-}
-
-static void Image_setOpaqueBuffer(JNIEnv* env, jobject thiz,
- const BufferItem* buffer)
-{
- env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer, reinterpret_cast<jlong>(buffer));
-}
-
-static uint32_t Image_getJpegSize(CpuConsumer::LockedBuffer* buffer, bool usingRGBAOverride)
-{
- ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!");
- uint32_t size = 0;
- uint32_t width = buffer->width;
- uint8_t* jpegBuffer = buffer->data;
-
- if (usingRGBAOverride) {
- width = (buffer->width + buffer->stride * (buffer->height - 1)) * 4;
- }
-
- // First check for JPEG transport header at the end of the buffer
- uint8_t* header = jpegBuffer + (width - sizeof(struct camera3_jpeg_blob));
- struct camera3_jpeg_blob *blob = (struct camera3_jpeg_blob*)(header);
- if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID) {
- size = blob->jpeg_size;
- ALOGV("%s: Jpeg size = %d", __FUNCTION__, size);
- }
-
- // failed to find size, default to whole buffer
- if (size == 0) {
- /*
- * This is a problem because not including the JPEG header
- * means that in certain rare situations a regular JPEG blob
- * will be misidentified as having a header, in which case
- * we will get a garbage size value.
- */
- ALOGW("%s: No JPEG header detected, defaulting to size=width=%d",
- __FUNCTION__, width);
- size = width;
- }
-
- return size;
-}
-
-static bool usingRGBAToJpegOverride(int32_t bufferFormat, int32_t readerCtxFormat) {
- return readerCtxFormat == HAL_PIXEL_FORMAT_BLOB && bufferFormat == HAL_PIXEL_FORMAT_RGBA_8888;
-}
-
-static int32_t applyFormatOverrides(int32_t bufferFormat, int32_t readerCtxFormat)
-{
- // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW
- // write limitations for some platforms (b/17379185).
- if (usingRGBAToJpegOverride(bufferFormat, readerCtxFormat)) {
- return HAL_PIXEL_FORMAT_BLOB;
- }
- return bufferFormat;
-}
-
-static void Image_getLockedBufferInfo(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx,
- uint8_t **base, uint32_t *size, int32_t readerFormat)
-{
- ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!");
- ALOG_ASSERT(base != NULL, "base is NULL!!!");
- ALOG_ASSERT(size != NULL, "size is NULL!!!");
- ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0));
-
- ALOGV("%s: buffer: %p", __FUNCTION__, buffer);
-
- uint32_t dataSize, ySize, cSize, cStride;
- uint8_t *cb, *cr;
- uint8_t *pData = NULL;
- int bytesPerPixel = 0;
-
- dataSize = ySize = cSize = cStride = 0;
- int32_t fmt = buffer->flexFormat;
-
- bool usingRGBAOverride = usingRGBAToJpegOverride(fmt, readerFormat);
- fmt = applyFormatOverrides(fmt, readerFormat);
- switch (fmt) {
- case HAL_PIXEL_FORMAT_YCbCr_420_888:
- pData =
- (idx == 0) ?
- buffer->data :
- (idx == 1) ?
- buffer->dataCb :
- buffer->dataCr;
- // only map until last pixel
- if (idx == 0) {
- dataSize = buffer->stride * (buffer->height - 1) + buffer->width;
- } else {
- dataSize = buffer->chromaStride * (buffer->height / 2 - 1) +
- buffer->chromaStep * (buffer->width / 2 - 1) + 1;
- }
- break;
- // NV21
- case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- cr = buffer->data + (buffer->stride * buffer->height);
- cb = cr + 1;
- // only map until last pixel
- ySize = buffer->width * (buffer->height - 1) + buffer->width;
- cSize = buffer->width * (buffer->height / 2 - 1) + buffer->width - 1;
-
- pData =
- (idx == 0) ?
- buffer->data :
- (idx == 1) ?
- cb:
- cr;
-
- dataSize = (idx == 0) ? ySize : cSize;
- break;
- case HAL_PIXEL_FORMAT_YV12:
- // Y and C stride need to be 16 pixel aligned.
- LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
- "Stride is not 16 pixel aligned %d", buffer->stride);
-
- ySize = buffer->stride * buffer->height;
- cStride = ALIGN(buffer->stride / 2, 16);
- cr = buffer->data + ySize;
- cSize = cStride * buffer->height / 2;
- cb = cr + cSize;
-
- pData =
- (idx == 0) ?
- buffer->data :
- (idx == 1) ?
- cb :
- cr;
- dataSize = (idx == 0) ? ySize : cSize;
- break;
- case HAL_PIXEL_FORMAT_Y8:
- // Single plane, 8bpp.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
-
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height;
- break;
- case HAL_PIXEL_FORMAT_Y16:
- bytesPerPixel = 2;
- // Single plane, 16bpp, strides are specified in pixels, not in bytes
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
-
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- break;
- case HAL_PIXEL_FORMAT_BLOB:
- // Used for JPEG data, height must be 1, width == size, single plane.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- ALOG_ASSERT(buffer->height == 1,
- "JPEG should has height value one but got %d", buffer->height);
-
- pData = buffer->data;
- dataSize = Image_getJpegSize(buffer, usingRGBAOverride);
- break;
- case HAL_PIXEL_FORMAT_RAW16:
- // Single plane 16bpp bayer data.
- bytesPerPixel = 2;
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- break;
- case HAL_PIXEL_FORMAT_RAW_OPAQUE:
- // Used for RAW_OPAQUE data, height must be 1, width == size, single plane.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- ALOG_ASSERT(buffer->height == 1,
- "RAW_PRIVATE should has height value one but got %d", buffer->height);
- pData = buffer->data;
- dataSize = buffer->width;
- break;
- case HAL_PIXEL_FORMAT_RAW10:
- // Single plane 10bpp bayer data.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- LOG_ALWAYS_FATAL_IF(buffer->width % 4,
- "Width is not multiple of 4 %d", buffer->width);
- LOG_ALWAYS_FATAL_IF(buffer->height % 2,
- "Height is not even %d", buffer->height);
- LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 10 / 8),
- "stride (%d) should be at least %d",
- buffer->stride, buffer->width * 10 / 8);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height;
- break;
- case HAL_PIXEL_FORMAT_RAW12:
- // Single plane 10bpp bayer data.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- LOG_ALWAYS_FATAL_IF(buffer->width % 4,
- "Width is not multiple of 4 %d", buffer->width);
- LOG_ALWAYS_FATAL_IF(buffer->height % 2,
- "Height is not even %d", buffer->height);
- LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 12 / 8),
- "stride (%d) should be at least %d",
- buffer->stride, buffer->width * 12 / 8);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height;
- break;
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
- // Single plane, 32bpp.
- bytesPerPixel = 4;
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- break;
- case HAL_PIXEL_FORMAT_RGB_565:
- // Single plane, 16bpp.
- bytesPerPixel = 2;
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- break;
- case HAL_PIXEL_FORMAT_RGB_888:
- // Single plane, 24bpp.
- bytesPerPixel = 3;
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- break;
- default:
- jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
- "Pixel format: 0x%x is unsupported", fmt);
- break;
- }
-
- *base = pData;
- *size = dataSize;
-}
-
-static jint Image_imageGetPixelStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx,
- int32_t halReaderFormat)
-{
- ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
- ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0), "Index is out of range:%d", idx);
-
- int pixelStride = 0;
- ALOG_ASSERT(buffer != NULL, "buffer is NULL");
-
- int32_t fmt = buffer->flexFormat;
-
- fmt = applyFormatOverrides(fmt, halReaderFormat);
-
- switch (fmt) {
- case HAL_PIXEL_FORMAT_YCbCr_420_888:
- pixelStride = (idx == 0) ? 1 : buffer->chromaStep;
- break;
- case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- pixelStride = (idx == 0) ? 1 : 2;
- break;
- case HAL_PIXEL_FORMAT_Y8:
- // Single plane 8bpp data.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pixelStride = 1;
- break;
- case HAL_PIXEL_FORMAT_YV12:
- pixelStride = 1;
- break;
- case HAL_PIXEL_FORMAT_BLOB:
- case HAL_PIXEL_FORMAT_RAW10:
- case HAL_PIXEL_FORMAT_RAW12:
- // Blob is used for JPEG data, RAW10 and RAW12 is used for 10-bit and 12-bit raw data,
- // those are single plane data with pixel stride 0 since they don't really have a
- // well defined pixel stride
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pixelStride = 0;
- break;
- case HAL_PIXEL_FORMAT_Y16:
- case HAL_PIXEL_FORMAT_RAW16:
- case HAL_PIXEL_FORMAT_RGB_565:
- // Single plane 16bpp data.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pixelStride = 2;
- break;
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pixelStride = 4;
- break;
- case HAL_PIXEL_FORMAT_RGB_888:
- // Single plane, 24bpp.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pixelStride = 3;
- break;
- case HAL_PIXEL_FORMAT_RAW_OPAQUE:
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pixelStride = 0; // RAW OPAQUE doesn't have pixel stride
- break;
- default:
- jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
- "Pixel format: 0x%x is unsupported", fmt);
- break;
- }
-
- return pixelStride;
-}
-
-static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx,
- int32_t halReaderFormat)
-{
- ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
- ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0));
-
- int rowStride = 0;
- ALOG_ASSERT(buffer != NULL, "buffer is NULL");
-
- int32_t fmt = buffer->flexFormat;
-
- fmt = applyFormatOverrides(fmt, halReaderFormat);
-
- switch (fmt) {
- case HAL_PIXEL_FORMAT_YCbCr_420_888:
- rowStride = (idx == 0) ? buffer->stride : buffer->chromaStride;
- break;
- case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- rowStride = buffer->width;
- break;
- case HAL_PIXEL_FORMAT_YV12:
- LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
- "Stride is not 16 pixel aligned %d", buffer->stride);
- rowStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
- break;
- case HAL_PIXEL_FORMAT_BLOB:
- // Blob is used for JPEG data. It is single plane and has 0 row stride and
- // 0 pixel stride
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- rowStride = 0;
- break;
- case HAL_PIXEL_FORMAT_RAW10:
- case HAL_PIXEL_FORMAT_RAW12:
- // RAW10 and RAW12 are used for 10-bit and 12-bit raw data, they are single plane
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- rowStride = buffer->stride;
- break;
- case HAL_PIXEL_FORMAT_Y8:
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
- "Stride is not 16 pixel aligned %d", buffer->stride);
- rowStride = buffer->stride;
- break;
- case HAL_PIXEL_FORMAT_Y16:
- case HAL_PIXEL_FORMAT_RAW16:
- // In native side, strides are specified in pixels, not in bytes.
- // Single plane 16bpp bayer data. even width/height,
- // row stride multiple of 16 pixels (32 bytes)
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
- "Stride is not 16 pixel aligned %d", buffer->stride);
- rowStride = buffer->stride * 2;
- break;
- case HAL_PIXEL_FORMAT_RGB_565:
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- rowStride = buffer->stride * 2;
- break;
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- rowStride = buffer->stride * 4;
- break;
- case HAL_PIXEL_FORMAT_RGB_888:
- // Single plane, 24bpp.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- rowStride = buffer->stride * 3;
- break;
- case HAL_PIXEL_FORMAT_RAW_OPAQUE:
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- rowStride = 0; // RAW OPAQUE doesn't have row stride
- break;
- default:
- ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt);
- jniThrowException(env, "java/lang/UnsupportedOperationException",
- "unsupported buffer format");
- break;
- }
-
- return rowStride;
-}
-
-static int Image_getBufferWidth(CpuConsumer::LockedBuffer* buffer) {
- if (buffer == NULL) return -1;
-
- if (!buffer->crop.isEmpty()) {
- return buffer->crop.getWidth();
- }
- return buffer->width;
-}
-
-static int Image_getBufferHeight(CpuConsumer::LockedBuffer* buffer) {
- if (buffer == NULL) return -1;
-
- if (!buffer->crop.isEmpty()) {
- return buffer->crop.getHeight();
- }
- return buffer->height;
-}
-
-// --------------------------Methods for opaque Image and ImageReader----------
-
-static BufferItemConsumer* ImageReader_getOpaqueConsumer(JNIEnv* env, jobject thiz)
+static BufferItemConsumer* ImageReader_getBufferConsumer(JNIEnv* env, jobject thiz)
{
ALOGV("%s:", __FUNCTION__);
JNIImageReaderContext* const ctx = ImageReader_getContext(env, thiz);
@@ -728,40 +263,21 @@
return NULL;
}
- if (!ctx->isOpaque()) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Non-opaque ImageReader doesn't support this method");
- }
-
- return ctx->getOpaqueConsumer();
+ return ctx->getBufferConsumer();
}
-static BufferItem* Image_getOpaqueBuffer(JNIEnv* env, jobject image)
+static void Image_setBufferItem(JNIEnv* env, jobject thiz,
+ const BufferItem* buffer)
+{
+ env->SetLongField(thiz, gSurfaceImageClassInfo.mNativeBuffer, reinterpret_cast<jlong>(buffer));
+}
+
+static BufferItem* Image_getBufferItem(JNIEnv* env, jobject image)
{
return reinterpret_cast<BufferItem*>(
env->GetLongField(image, gSurfaceImageClassInfo.mNativeBuffer));
}
-static int Image_getOpaqueBufferWidth(BufferItem* buffer) {
- if (buffer == NULL) return -1;
-
- if (!buffer->mCrop.isEmpty()) {
- return buffer->mCrop.getWidth();
- }
- return buffer->mGraphicBuffer->getWidth();
-}
-
-static int Image_getOpaqueBufferHeight(BufferItem* buffer) {
- if (buffer == NULL) return -1;
-
- if (!buffer->mCrop.isEmpty()) {
- return buffer->mCrop.getHeight();
- }
-
- return buffer->mGraphicBuffer->getHeight();
-}
-
-
// ----------------------------------------------------------------------------
@@ -784,6 +300,11 @@
"can't find android/graphics/ImageReader.%s",
ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID);
+ gSurfaceImageClassInfo.mPlanes = env->GetFieldID(
+ imageClazz, "mPlanes", "[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;");
+ LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mPlanes == NULL,
+ "can't find android/media/ImageReader$ReaderSurfaceImage.mPlanes");
+
gImageReaderClassInfo.mNativeContext = env->GetFieldID(
clazz, ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID, "J");
LOG_ALWAYS_FATAL_IF(gImageReaderClassInfo.mNativeContext == NULL,
@@ -800,7 +321,7 @@
// FindClass only gives a local reference of jclass object.
gSurfacePlaneClassInfo.clazz = (jclass) env->NewGlobalRef(planeClazz);
gSurfacePlaneClassInfo.ctor = env->GetMethodID(gSurfacePlaneClassInfo.clazz, "<init>",
- "(Landroid/media/ImageReader$SurfaceImage;III)V");
+ "(Landroid/media/ImageReader$SurfaceImage;IILjava/nio/ByteBuffer;)V");
LOG_ALWAYS_FATAL_IF(gSurfacePlaneClassInfo.ctor == NULL,
"Can not find SurfacePlane constructor");
}
@@ -831,81 +352,52 @@
sp<IGraphicBufferProducer> gbProducer;
sp<IGraphicBufferConsumer> gbConsumer;
BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
- sp<ConsumerBase> consumer;
- sp<CpuConsumer> cpuConsumer;
- sp<BufferItemConsumer> opaqueConsumer;
+ sp<BufferItemConsumer> bufferConsumer;
String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
width, height, format, maxImages, getpid(),
createProcessUniqueId());
+ uint32_t consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN;
+
if (isFormatOpaque(nativeFormat)) {
// Use the SW_READ_NEVER usage to tell producer that this format is not for preview or video
// encoding. The only possibility will be ZSL output.
- opaqueConsumer =
- new BufferItemConsumer(gbConsumer, GRALLOC_USAGE_SW_READ_NEVER, maxImages,
- /*controlledByApp*/true);
- if (opaqueConsumer == NULL) {
- jniThrowRuntimeException(env, "Failed to allocate native opaque consumer");
- return;
- }
- ctx->setOpaqueConsumer(opaqueConsumer);
- opaqueConsumer->setName(consumerName);
- consumer = opaqueConsumer;
- } else {
- cpuConsumer = new CpuConsumer(gbConsumer, maxImages, /*controlledByApp*/true);
- // TODO: throw dvm exOutOfMemoryError?
- if (cpuConsumer == NULL) {
- jniThrowRuntimeException(env, "Failed to allocate native CpuConsumer");
- return;
- }
- ctx->setCpuConsumer(cpuConsumer);
- cpuConsumer->setName(consumerName);
- consumer = cpuConsumer;
+ consumerUsage = GRALLOC_USAGE_SW_READ_NEVER;
}
+ bufferConsumer = new BufferItemConsumer(gbConsumer, consumerUsage, maxImages,
+ /*controlledByApp*/true);
+ if (bufferConsumer == nullptr) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "Failed to allocate native buffer consumer for format 0x%x", nativeFormat);
+ return;
+ }
+ ctx->setBufferConsumer(bufferConsumer);
+ bufferConsumer->setName(consumerName);
ctx->setProducer(gbProducer);
- consumer->setFrameAvailableListener(ctx);
+ bufferConsumer->setFrameAvailableListener(ctx);
ImageReader_setNativeContext(env, thiz, ctx);
ctx->setBufferFormat(nativeFormat);
ctx->setBufferDataspace(nativeDataspace);
ctx->setBufferWidth(width);
ctx->setBufferHeight(height);
- // Set the width/height/format/dataspace to the CpuConsumer
- // TODO: below code can be simplified once b/19977701 is fixed.
- if (isFormatOpaque(nativeFormat)) {
- res = opaqueConsumer->setDefaultBufferSize(width, height);
- if (res != OK) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to set opaque consumer buffer size");
- return;
- }
- res = opaqueConsumer->setDefaultBufferFormat(nativeFormat);
- if (res != OK) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to set opaque consumer buffer format");
- }
- res = opaqueConsumer->setDefaultBufferDataSpace(nativeDataspace);
- if (res != OK) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to set opaque consumer buffer dataSpace");
- }
- } else {
- res = cpuConsumer->setDefaultBufferSize(width, height);
- if (res != OK) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to set CpuConsumer buffer size");
- return;
- }
- res = cpuConsumer->setDefaultBufferFormat(nativeFormat);
- if (res != OK) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to set CpuConsumer buffer format");
- }
- res = cpuConsumer->setDefaultBufferDataSpace(nativeDataspace);
- if (res != OK) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Failed to set CpuConsumer buffer dataSpace");
- }
+ // Set the width/height/format/dataspace to the bufferConsumer.
+ res = bufferConsumer->setDefaultBufferSize(width, height);
+ if (res != OK) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Failed to set buffer consumer default size (%dx%d) for format 0x%x",
+ width, height, nativeFormat);
+ return;
+ }
+ res = bufferConsumer->setDefaultBufferFormat(nativeFormat);
+ if (res != OK) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Failed to set buffer consumer default format 0x%x", nativeFormat);
+ }
+ res = bufferConsumer->setDefaultBufferDataSpace(nativeDataspace);
+ if (res != OK) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Failed to set buffer consumer default dataSpace 0x%x", nativeDataspace);
}
}
@@ -919,12 +411,8 @@
return;
}
- ConsumerBase* consumer = NULL;
- if (ctx->isOpaque()) {
- consumer = ImageReader_getOpaqueConsumer(env, thiz);
- } else {
- consumer = ImageReader_getCpuConsumer(env, thiz);
- }
+ BufferItemConsumer* consumer = NULL;
+ consumer = ImageReader_getBufferConsumer(env, thiz);
if (consumer != NULL) {
consumer->abandon();
@@ -933,6 +421,39 @@
ImageReader_setNativeContext(env, thiz, NULL);
}
+static sp<Fence> Image_unlockIfLocked(JNIEnv* env, jobject image) {
+ ALOGV("%s", __FUNCTION__);
+ BufferItem* buffer = Image_getBufferItem(env, image);
+ if (buffer == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Image is not initialized");
+ return Fence::NO_FENCE;
+ }
+
+ // Is locked?
+ bool wasBufferLocked = false;
+ jobject planes = NULL;
+ if (!isFormatOpaque(buffer->mGraphicBuffer->getPixelFormat())) {
+ planes = env->GetObjectField(image, gSurfaceImageClassInfo.mPlanes);
+ }
+ wasBufferLocked = (planes != NULL);
+ if (wasBufferLocked) {
+ status_t res = OK;
+ int fenceFd = -1;
+ if (wasBufferLocked) {
+ res = buffer->mGraphicBuffer->unlockAsync(&fenceFd);
+ if (res != OK) {
+ jniThrowRuntimeException(env, "unlock buffer failed");
+ return Fence::NO_FENCE;
+ }
+ }
+ sp<Fence> releaseFence = new Fence(fenceFd);
+ return releaseFence;
+ ALOGV("Successfully unlocked the image");
+ }
+ return Fence::NO_FENCE;
+}
+
static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image)
{
ALOGV("%s:", __FUNCTION__);
@@ -942,156 +463,18 @@
return;
}
- if (ctx->isOpaque()) {
- BufferItemConsumer* opaqueConsumer = ctx->getOpaqueConsumer();
- BufferItem* opaqueBuffer = Image_getOpaqueBuffer(env, image);
- opaqueConsumer->releaseBuffer(*opaqueBuffer); // Not using fence for now.
- Image_setOpaqueBuffer(env, image, NULL);
- ctx->returnOpaqueBuffer(opaqueBuffer);
- ALOGV("%s: Opaque Image has been released", __FUNCTION__);
- } else {
- CpuConsumer* consumer = ctx->getCpuConsumer();
- CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, image);
- if (!buffer) {
- // Release an already closed image is harmless.
- return;
- }
- consumer->unlockBuffer(*buffer);
- Image_setBuffer(env, image, NULL);
- ctx->returnLockedBuffer(buffer);
- ALOGV("%s: Image (format: 0x%x) has been released", __FUNCTION__, ctx->getBufferFormat());
- }
-}
-
-static jint ImageReader_opaqueImageSetup(JNIEnv* env, JNIImageReaderContext* ctx, jobject image) {
- ALOGV("%s:", __FUNCTION__);
- if (ctx == NULL || !ctx->isOpaque()) {
- jniThrowRuntimeException(env, "ImageReaderContext is not initialized");
- return -1;
+ BufferItemConsumer* bufferConsumer = ctx->getBufferConsumer();
+ BufferItem* buffer = Image_getBufferItem(env, image);
+ if (buffer == nullptr) {
+ // Release an already closed image is harmless.
+ return;
}
- BufferItemConsumer* opaqueConsumer = ctx->getOpaqueConsumer();
- BufferItem* buffer = ctx->getOpaqueBuffer();
- if (buffer == NULL) {
- ALOGW("Unable to acquire a buffer item, very likely client tried to acquire more than"
- " maxImages buffers");
- return ACQUIRE_MAX_IMAGES;
- }
-
- status_t res = opaqueConsumer->acquireBuffer(buffer, 0);
- if (res != OK) {
- ctx->returnOpaqueBuffer(buffer);
- if (res == INVALID_OPERATION) {
- // Max number of images were already acquired.
- ALOGE("%s: Max number of buffers allowed are already acquired : %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return ACQUIRE_MAX_IMAGES;
- } else {
- ALOGE("%s: Acquire image failed with error: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return ACQUIRE_NO_BUFFERS;
- }
- }
-
- // Set SurfaceImage instance member variables
- Image_setOpaqueBuffer(env, image, buffer);
- env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
- static_cast<jlong>(buffer->mTimestamp));
-
- return ACQUIRE_SUCCESS;
-}
-
-static jint ImageReader_lockedImageSetup(JNIEnv* env, JNIImageReaderContext* ctx, jobject image) {
- CpuConsumer* consumer = ctx->getCpuConsumer();
- CpuConsumer::LockedBuffer* buffer = ctx->getLockedBuffer();
- if (buffer == NULL) {
- ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than"
- " maxImages buffers");
- return ACQUIRE_MAX_IMAGES;
- }
- status_t res = consumer->lockNextBuffer(buffer);
- if (res != NO_ERROR) {
- ctx->returnLockedBuffer(buffer);
- if (res != BAD_VALUE /*no buffers*/) {
- if (res == NOT_ENOUGH_DATA) {
- return ACQUIRE_MAX_IMAGES;
- } else {
- ALOGE("%s Fail to lockNextBuffer with error: %d ",
- __FUNCTION__, res);
- jniThrowExceptionFmt(env, "java/lang/AssertionError",
- "Unknown error (%d) when we tried to lock buffer.",
- res);
- }
- }
- return ACQUIRE_NO_BUFFERS;
- }
-
- if (buffer->flexFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
- jniThrowException(env, "java/lang/UnsupportedOperationException",
- "NV21 format is not supported by ImageReader");
- return -1;
- }
-
- // Check if the left-top corner of the crop rect is origin, we currently assume this point is
- // zero, will revist this once this assumption turns out problematic.
- Point lt = buffer->crop.leftTop();
- if (lt.x != 0 || lt.y != 0) {
- jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
- "crop left top corner [%d, %d] need to be at origin", lt.x, lt.y);
- return -1;
- }
-
- // Check if the producer buffer configurations match what ImageReader configured.
- int outputWidth = Image_getBufferWidth(buffer);
- int outputHeight = Image_getBufferHeight(buffer);
-
- int imgReaderFmt = ctx->getBufferFormat();
- int imageReaderWidth = ctx->getBufferWidth();
- int imageReaderHeight = ctx->getBufferHeight();
- if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) && (imgReaderFmt != HAL_PIXEL_FORMAT_BLOB) &&
- (imageReaderWidth != outputWidth || imageReaderHeight != outputHeight)) {
- ALOGV("%s: Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d",
- __FUNCTION__, outputWidth, outputHeight, imageReaderWidth, imageReaderHeight);
- }
-
- int bufFmt = buffer->format;
- if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888) {
- bufFmt = buffer->flexFormat;
- }
- if (imgReaderFmt != bufFmt) {
- if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && (bufFmt ==
- HAL_PIXEL_FORMAT_YCrCb_420_SP || bufFmt == HAL_PIXEL_FORMAT_YV12)) {
- // Special casing for when producer switches to a format compatible with flexible YUV
- // (HAL_PIXEL_FORMAT_YCbCr_420_888).
- ctx->setBufferFormat(bufFmt);
- ALOGD("%s: Overriding buffer format YUV_420_888 to %x.", __FUNCTION__, bufFmt);
- } else if (imgReaderFmt == HAL_PIXEL_FORMAT_BLOB && bufFmt == HAL_PIXEL_FORMAT_RGBA_8888) {
- // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW
- // write limitations for (b/17379185).
- ALOGD("%s: Receiving JPEG in HAL_PIXEL_FORMAT_RGBA_8888 buffer.", __FUNCTION__);
- } else {
- // Return the buffer to the queue.
- consumer->unlockBuffer(*buffer);
- ctx->returnLockedBuffer(buffer);
-
- // Throw exception
- ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x",
- buffer->format, ctx->getBufferFormat());
- String8 msg;
- msg.appendFormat("The producer output buffer format 0x%x doesn't "
- "match the ImageReader's configured buffer format 0x%x.",
- bufFmt, ctx->getBufferFormat());
- jniThrowException(env, "java/lang/UnsupportedOperationException",
- msg.string());
- return -1;
- }
- }
- // Set SurfaceImage instance member variables
- Image_setBuffer(env, image, buffer);
- env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
- static_cast<jlong>(buffer->timestamp));
-
- return ACQUIRE_SUCCESS;
+ sp<Fence> releaseFence = Image_unlockIfLocked(env, image);
+ bufferConsumer->releaseBuffer(*buffer, releaseFence);
+ Image_setBufferItem(env, image, NULL);
+ ctx->returnBufferItem(buffer);
+ ALOGV("%s: Image (format: 0x%x) has been released", __FUNCTION__, ctx->getBufferFormat());
}
static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) {
@@ -1103,11 +486,99 @@
return -1;
}
- if (ctx->isOpaque()) {
- return ImageReader_opaqueImageSetup(env, ctx, image);
- } else {
- return ImageReader_lockedImageSetup(env, ctx, image);
+ BufferItemConsumer* bufferConsumer = ctx->getBufferConsumer();
+ BufferItem* buffer = ctx->getBufferItem();
+ if (buffer == NULL) {
+ ALOGW("Unable to acquire a buffer item, very likely client tried to acquire more than"
+ " maxImages buffers");
+ return ACQUIRE_MAX_IMAGES;
}
+
+ status_t res = bufferConsumer->acquireBuffer(buffer, 0);
+ if (res != OK) {
+ ctx->returnBufferItem(buffer);
+ if (res != BufferQueue::NO_BUFFER_AVAILABLE) {
+ if (res == INVALID_OPERATION) {
+ // Max number of images were already acquired.
+ ALOGE("%s: Max number of buffers allowed are already acquired : %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return ACQUIRE_MAX_IMAGES;
+ } else {
+ ALOGE("%s: Acquire image failed with some unknown error: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+ "Unknown error (%d) when we tried to acquire an image.",
+ res);
+ return ACQUIRE_NO_BUFFERS;
+ }
+ }
+ // This isn't really an error case, as the application may acquire buffer at any time.
+ return ACQUIRE_NO_BUFFERS;
+ }
+
+ // Add some extra checks for non-opaque formats.
+ if (!isFormatOpaque(ctx->getBufferFormat())) {
+ // Check if the left-top corner of the crop rect is origin, we currently assume this point is
+ // zero, will revisit this once this assumption turns out problematic.
+ Point lt = buffer->mCrop.leftTop();
+ if (lt.x != 0 || lt.y != 0) {
+ jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
+ "crop left top corner [%d, %d] need to be at origin", lt.x, lt.y);
+ return -1;
+ }
+
+ // Check if the producer buffer configurations match what ImageReader configured.
+ int outputWidth = getBufferWidth(buffer);
+ int outputHeight = getBufferHeight(buffer);
+
+ int imgReaderFmt = ctx->getBufferFormat();
+ int imageReaderWidth = ctx->getBufferWidth();
+ int imageReaderHeight = ctx->getBufferHeight();
+ int bufferFormat = buffer->mGraphicBuffer->getPixelFormat();
+ if ((bufferFormat != HAL_PIXEL_FORMAT_BLOB) && (imgReaderFmt != HAL_PIXEL_FORMAT_BLOB) &&
+ (imageReaderWidth != outputWidth || imageReaderHeight != outputHeight)) {
+ ALOGV("%s: Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d",
+ __FUNCTION__, outputWidth, outputHeight, imageReaderWidth, imageReaderHeight);
+ }
+ if (imgReaderFmt != bufferFormat) {
+ if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 &&
+ isPossiblyYUV(bufferFormat)) {
+ // Treat formats that are compatible with flexible YUV
+ // (HAL_PIXEL_FORMAT_YCbCr_420_888) as HAL_PIXEL_FORMAT_YCbCr_420_888.
+ ALOGV("%s: Treat buffer format to 0x%x as HAL_PIXEL_FORMAT_YCbCr_420_888",
+ __FUNCTION__, bufferFormat);
+ } else if (imgReaderFmt == HAL_PIXEL_FORMAT_BLOB &&
+ bufferFormat == HAL_PIXEL_FORMAT_RGBA_8888) {
+ // Using HAL_PIXEL_FORMAT_RGBA_8888 Gralloc buffers containing JPEGs to get around
+ // SW write limitations for (b/17379185).
+ ALOGV("%s: Receiving JPEG in HAL_PIXEL_FORMAT_RGBA_8888 buffer.", __FUNCTION__);
+ } else {
+ // Return the buffer to the queue. No need to provide fence, as this buffer wasn't
+ // used anywhere yet.
+ bufferConsumer->releaseBuffer(*buffer);
+ ctx->returnBufferItem(buffer);
+
+ // Throw exception
+ ALOGE("Producer output buffer format: 0x%x, ImageReader configured format: 0x%x",
+ bufferFormat, ctx->getBufferFormat());
+ String8 msg;
+ msg.appendFormat("The producer output buffer format 0x%x doesn't "
+ "match the ImageReader's configured buffer format 0x%x.",
+ bufferFormat, ctx->getBufferFormat());
+ jniThrowException(env, "java/lang/UnsupportedOperationException",
+ msg.string());
+ return -1;
+ }
+ }
+
+ }
+
+ // Set SurfaceImage instance member variables
+ Image_setBufferItem(env, image, buffer);
+ env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
+ static_cast<jlong>(buffer->mTimestamp));
+
+ return ACQUIRE_SUCCESS;
}
static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) {
@@ -1118,29 +589,23 @@
return -1;
}
- status_t res = OK;
- if (!ctx->isOpaque()) {
- // TODO: Non-Opaque format detach is not implemented yet.
- jniThrowRuntimeException(env,
- "nativeDetachImage is not implemented yet for non-opaque format !!!");
- return -1;
- }
-
- BufferItemConsumer* opaqueConsumer = ctx->getOpaqueConsumer();
- BufferItem* opaqueBuffer = Image_getOpaqueBuffer(env, image);
- if (!opaqueBuffer) {
+ BufferItemConsumer* bufferConsumer = ctx->getBufferConsumer();
+ BufferItem* buffer = Image_getBufferItem(env, image);
+ if (!buffer) {
ALOGE(
- "Opaque Image already released and can not be detached from ImageReader!!!");
+ "Image already released and can not be detached from ImageReader!!!");
jniThrowException(env, "java/lang/IllegalStateException",
- "Opaque Image detach from ImageReader failed: buffer was already released");
+ "Image detach from ImageReader failed: buffer was already released");
return -1;
}
- res = opaqueConsumer->detachBuffer(opaqueBuffer->mSlot);
+ status_t res = OK;
+ Image_unlockIfLocked(env, image);
+ res = bufferConsumer->detachBuffer(buffer->mSlot);
if (res != OK) {
- ALOGE("Opaque Image detach failed: %s (%d)!!!", strerror(-res), res);
+ ALOGE("Image detach failed: %s (%d)!!!", strerror(-res), res);
jniThrowRuntimeException(env,
- "nativeDetachImage failed for opaque image!!!");
+ "nativeDetachImage failed for image!!!");
return res;
}
return OK;
@@ -1152,7 +617,7 @@
IGraphicBufferProducer* gbp = ImageReader_getProducer(env, thiz);
if (gbp == NULL) {
- jniThrowRuntimeException(env, "CpuConsumer is uninitialized");
+ jniThrowRuntimeException(env, "Buffer consumer is uninitialized");
return NULL;
}
@@ -1160,98 +625,115 @@
return android_view_Surface_createFromIGraphicBufferProducer(env, gbp);
}
-static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx, int readerFormat)
+static void Image_getLockedImage(JNIEnv* env, jobject thiz, LockedImage *image) {
+ ALOGV("%s", __FUNCTION__);
+ BufferItem* buffer = Image_getBufferItem(env, thiz);
+ if (buffer == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Image is not initialized");
+ return;
+ }
+
+ status_t res = lockImageFromBuffer(buffer,
+ GRALLOC_USAGE_SW_READ_OFTEN, buffer->mFence->dup(), image);
+ if (res != OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "lock buffer failed for format 0x%x",
+ buffer->mGraphicBuffer->getPixelFormat());
+ return;
+ }
+
+ // Carry over some fields from BufferItem.
+ image->crop = buffer->mCrop;
+ image->transform = buffer->mTransform;
+ image->scalingMode = buffer->mScalingMode;
+ image->timestamp = buffer->mTimestamp;
+ image->dataSpace = buffer->mDataSpace;
+ image->frameNumber = buffer->mFrameNumber;
+
+ ALOGV("%s: Successfully locked the image", __FUNCTION__);
+ // crop, transform, scalingMode, timestamp, and frameNumber should be set by producer,
+ // and we don't set them here.
+}
+
+static void Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx,
+ int32_t writerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) {
+ ALOGV("%s", __FUNCTION__);
+
+ status_t res = getLockedImageInfo(buffer, idx, writerFormat, base, size,
+ pixelStride, rowStride);
+ if (res != OK) {
+ jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
+ "Pixel format: 0x%x is unsupported", buffer->flexFormat);
+ }
+}
+
+static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz,
+ int numPlanes, int readerFormat)
{
- int rowStride, pixelStride;
+ ALOGV("%s: create SurfacePlane array with size %d", __FUNCTION__, numPlanes);
+ int rowStride = 0;
+ int pixelStride = 0;
+ uint8_t *pData = NULL;
+ uint32_t dataSize = 0;
+ jobject byteBuffer = NULL;
+
PublicFormat publicReaderFormat = static_cast<PublicFormat>(readerFormat);
int halReaderFormat = android_view_Surface_mapPublicFormatToHalFormat(
publicReaderFormat);
- ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
+ if (isFormatOpaque(halReaderFormat) && numPlanes > 0) {
+ String8 msg;
+ msg.appendFormat("Format 0x%x is opaque, thus not writable, the number of planes (%d)"
+ " must be 0", halReaderFormat, numPlanes);
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg.string());
+ return NULL;
+ }
+
+ jobjectArray surfacePlanes = env->NewObjectArray(numPlanes, gSurfacePlaneClassInfo.clazz,
+ /*initial_element*/NULL);
+ if (surfacePlanes == NULL) {
+ jniThrowRuntimeException(env, "Failed to create SurfacePlane arrays,"
+ " probably out of memory");
+ return NULL;
+ }
if (isFormatOpaque(halReaderFormat)) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Opaque images from Opaque ImageReader do not have any planes");
- return NULL;
+ // Return 0 element surface array.
+ return surfacePlanes;
}
- CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
+ LockedImage lockedImg = LockedImage();
+ Image_getLockedImage(env, thiz, &lockedImg);
+ // Create all SurfacePlanes
+ for (int i = 0; i < numPlanes; i++) {
+ Image_getLockedImageInfo(env, &lockedImg, i, halReaderFormat,
+ &pData, &dataSize, &pixelStride, &rowStride);
+ byteBuffer = env->NewDirectByteBuffer(pData, dataSize);
+ if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to allocate ByteBuffer");
+ return NULL;
+ }
- ALOG_ASSERT(buffer != NULL);
- if (buffer == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", "Image was released");
+ // Finally, create this SurfacePlane.
+ jobject surfacePlane = env->NewObject(gSurfacePlaneClassInfo.clazz,
+ gSurfacePlaneClassInfo.ctor, thiz, rowStride, pixelStride, byteBuffer);
+ env->SetObjectArrayElement(surfacePlanes, i, surfacePlane);
}
- rowStride = Image_imageGetRowStride(env, buffer, idx, halReaderFormat);
- pixelStride = Image_imageGetPixelStride(env, buffer, idx, halReaderFormat);
-
- jobject surfPlaneObj = env->NewObject(gSurfacePlaneClassInfo.clazz,
- gSurfacePlaneClassInfo.ctor, thiz, idx, rowStride, pixelStride);
-
- return surfPlaneObj;
+ return surfacePlanes;
}
-static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx, int readerFormat)
+static jint Image_getWidth(JNIEnv* env, jobject thiz)
{
- uint8_t *base = NULL;
- uint32_t size = 0;
- jobject byteBuffer;
- PublicFormat readerPublicFormat = static_cast<PublicFormat>(readerFormat);
- int readerHalFormat = android_view_Surface_mapPublicFormatToHalFormat(
- readerPublicFormat);
-
- ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
-
- if (isFormatOpaque(readerHalFormat)) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "Opaque images from Opaque ImageReader do not have any plane");
- return NULL;
- }
-
- CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
-
- if (buffer == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", "Image was released");
- }
-
- // Create byteBuffer from native buffer
- Image_getLockedBufferInfo(env, buffer, idx, &base, &size, readerHalFormat);
-
- if (size > static_cast<uint32_t>(INT32_MAX)) {
- // Byte buffer have 'int capacity', so check the range
- jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
- "Size too large for bytebuffer capacity %" PRIu32, size);
- return NULL;
- }
-
- byteBuffer = env->NewDirectByteBuffer(base, size);
- // TODO: throw dvm exOutOfMemoryError?
- if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) {
- jniThrowException(env, "java/lang/IllegalStateException", "Failed to allocate ByteBuffer");
- }
-
- return byteBuffer;
+ BufferItem* buffer = Image_getBufferItem(env, thiz);
+ return getBufferWidth(buffer);
}
-static jint Image_getWidth(JNIEnv* env, jobject thiz, jint format)
+static jint Image_getHeight(JNIEnv* env, jobject thiz)
{
- if (isFormatOpaque(format)) {
- BufferItem* opaqueBuffer = Image_getOpaqueBuffer(env, thiz);
- return Image_getOpaqueBufferWidth(opaqueBuffer);
- } else {
- CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
- return Image_getBufferWidth(buffer);
- }
-}
-
-static jint Image_getHeight(JNIEnv* env, jobject thiz, jint format)
-{
- if (isFormatOpaque(format)) {
- BufferItem* opaqueBuffer = Image_getOpaqueBuffer(env, thiz);
- return Image_getOpaqueBufferHeight(opaqueBuffer);
- } else {
- CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
- return Image_getBufferHeight(buffer);
- }
+ BufferItem* buffer = Image_getBufferItem(env, thiz);
+ return getBufferHeight(buffer);
}
static jint Image_getFormat(JNIEnv* env, jobject thiz, jint readerFormat)
@@ -1260,20 +742,21 @@
// Assuming opaque reader produce opaque images.
return static_cast<jint>(PublicFormat::PRIVATE);
} else {
- CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
+ BufferItem* buffer = Image_getBufferItem(env, thiz);
int readerHalFormat = android_view_Surface_mapPublicFormatToHalFormat(
static_cast<PublicFormat>(readerFormat));
- int32_t fmt = applyFormatOverrides(buffer->flexFormat, readerHalFormat);
+ int32_t fmt = applyFormatOverrides(
+ buffer->mGraphicBuffer->getPixelFormat(), readerHalFormat);
// Override the image format to HAL_PIXEL_FORMAT_YCbCr_420_888 if the actual format is
// NV21 or YV12. This could only happen when the Gralloc HAL version is v0.1 thus doesn't
// support lockycbcr(), the CpuConsumer need to use the lock() method in the
// lockNextBuffer() call. For Gralloc HAL v0.2 or newer, this format should already be
// overridden to HAL_PIXEL_FORMAT_YCbCr_420_888 for the flexible YUV compatible formats.
- if (fmt == HAL_PIXEL_FORMAT_YCrCb_420_SP || fmt == HAL_PIXEL_FORMAT_YV12) {
+ if (isPossiblyYUV(fmt)) {
fmt = HAL_PIXEL_FORMAT_YCbCr_420_888;
}
PublicFormat publicFmt = android_view_Surface_mapHalFormatDataspaceToPublicFormat(
- fmt, buffer->dataSpace);
+ fmt, buffer->mDataSpace);
return static_cast<jint>(publicFmt);
}
}
@@ -1293,11 +776,10 @@
};
static const JNINativeMethod gImageMethods[] = {
- {"nativeImageGetBuffer", "(II)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer },
- {"nativeCreatePlane", "(II)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
- (void*)Image_createSurfacePlane },
- {"nativeGetWidth", "(I)I", (void*)Image_getWidth },
- {"nativeGetHeight", "(I)I", (void*)Image_getHeight },
+ {"nativeCreatePlanes", "(II)[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
+ (void*)Image_createSurfacePlanes },
+ {"nativeGetWidth", "()I", (void*)Image_getWidth },
+ {"nativeGetHeight", "()I", (void*)Image_getHeight },
{"nativeGetFormat", "(I)I", (void*)Image_getFormat },
};
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index f50da85..d5d9fc9 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -16,34 +16,28 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ImageWriter_JNI"
+#include "android_media_Utils.h"
+
#include <utils/Log.h>
#include <utils/String8.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
-#include <gui/CpuConsumer.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
#include <camera3.h>
-
#include <jni.h>
#include <JNIHelp.h>
#include <stdint.h>
#include <inttypes.h>
-#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
-
#define IMAGE_BUFFER_JNI_ID "mNativeBuffer"
// ----------------------------------------------------------------------------
using namespace android;
-enum {
- IMAGE_WRITER_MAX_NUM_PLANES = 3,
-};
-
static struct {
jmethodID postEventFromNative;
jfieldID mWriterFormat;
@@ -60,8 +54,6 @@
jmethodID ctor;
} gSurfacePlaneClassInfo;
-typedef CpuConsumer::LockedBuffer LockedImage;
-
// ----------------------------------------------------------------------------
class JNIImageWriterContext : public BnProducerListener {
@@ -181,13 +173,11 @@
// -------------------------------Private method declarations--------------
-static bool isPossiblyYUV(PixelFormat format);
static void Image_setNativeContext(JNIEnv* env, jobject thiz,
sp<GraphicBuffer> buffer, int fenceFd);
static void Image_getNativeContext(JNIEnv* env, jobject thiz,
GraphicBuffer** buffer, int* fenceFd);
static void Image_unlockIfLocked(JNIEnv* env, jobject thiz);
-static bool isFormatOpaque(int format);
// --------------------------ImageWriter methods---------------------------------------
@@ -672,28 +662,6 @@
return buffer->getHeight();
}
-// Some formats like JPEG defined with different values between android.graphics.ImageFormat and
-// graphics.h, need convert to the one defined in graphics.h here.
-static int Image_getPixelFormat(JNIEnv* env, int format) {
- int jpegFormat;
- jfieldID fid;
-
- ALOGV("%s: format = 0x%x", __FUNCTION__, format);
-
- jclass imageFormatClazz = env->FindClass("android/graphics/ImageFormat");
- ALOG_ASSERT(imageFormatClazz != NULL);
-
- fid = env->GetStaticFieldID(imageFormatClazz, "JPEG", "I");
- jpegFormat = env->GetStaticIntField(imageFormatClazz, fid);
-
- // Translate the JPEG to BLOB for camera purpose.
- if (format == jpegFormat) {
- format = HAL_PIXEL_FORMAT_BLOB;
- }
-
- return format;
-}
-
static jint Image_getFormat(JNIEnv* env, jobject thiz) {
ALOGV("%s", __FUNCTION__);
GraphicBuffer* buffer;
@@ -704,7 +672,10 @@
return 0;
}
- return Image_getPixelFormat(env, buffer->getPixelFormat());
+ // ImageWriter doesn't support data space yet, assuming it is unknown.
+ PublicFormat publicFmt = android_view_Surface_mapHalFormatDataspaceToPublicFormat(
+ buffer->getPixelFormat(), HAL_DATASPACE_UNKNOWN);
+ return static_cast<jint>(publicFmt);
}
static void Image_setFenceFd(JNIEnv* env, jobject thiz, int fenceFd) {
@@ -723,272 +694,34 @@
return;
}
- void* pData = NULL;
- android_ycbcr ycbcr = android_ycbcr();
- status_t res;
- int format = Image_getFormat(env, thiz);
- int flexFormat = format;
- if (isPossiblyYUV(format)) {
- // ImageWriter doesn't use crop by itself, app sets it, use the no crop version.
- res = buffer->lockAsyncYCbCr(GRALLOC_USAGE_SW_WRITE_OFTEN, &ycbcr, fenceFd);
- // Clear the fenceFd as it is already consumed by lock call.
- Image_setFenceFd(env, thiz, /*fenceFd*/-1);
- if (res != OK) {
- jniThrowRuntimeException(env, "lockAsyncYCbCr failed for YUV buffer");
- return;
- }
- pData = ycbcr.y;
- flexFormat = HAL_PIXEL_FORMAT_YCbCr_420_888;
+ // ImageWriter doesn't use crop by itself, app sets it, use the no crop version.
+ const Rect noCrop(buffer->width, buffer->height);
+ status_t res = lockImageFromBuffer(
+ buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, noCrop, fenceFd, image);
+ // Clear the fenceFd as it is already consumed by lock call.
+ Image_setFenceFd(env, thiz, /*fenceFd*/-1);
+ if (res != OK) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+ "lock buffer failed for format 0x%x",
+ buffer->getPixelFormat());
+ return;
}
- // lockAsyncYCbCr for YUV is unsuccessful.
- if (pData == NULL) {
- res = buffer->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, &pData, fenceFd);
- if (res != OK) {
- jniThrowRuntimeException(env, "lockAsync failed");
- return;
- }
- }
-
- image->data = reinterpret_cast<uint8_t*>(pData);
- image->width = buffer->getWidth();
- image->height = buffer->getHeight();
- image->format = format;
- image->flexFormat = flexFormat;
- image->stride = (ycbcr.y != NULL) ? static_cast<uint32_t>(ycbcr.ystride) : buffer->getStride();
-
- image->dataCb = reinterpret_cast<uint8_t*>(ycbcr.cb);
- image->dataCr = reinterpret_cast<uint8_t*>(ycbcr.cr);
- image->chromaStride = static_cast<uint32_t>(ycbcr.cstride);
- image->chromaStep = static_cast<uint32_t>(ycbcr.chroma_step);
- ALOGV("Successfully locked the image");
+ ALOGV("%s: Successfully locked the image", __FUNCTION__);
// crop, transform, scalingMode, timestamp, and frameNumber should be set by producer,
// and we don't set them here.
}
-static bool usingRGBAToJpegOverride(int32_t bufferFormat, int32_t writerCtxFormat) {
- return writerCtxFormat == HAL_PIXEL_FORMAT_BLOB && bufferFormat == HAL_PIXEL_FORMAT_RGBA_8888;
-}
-
-static int32_t applyFormatOverrides(int32_t bufferFormat, int32_t writerCtxFormat)
-{
- // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW
- // write limitations for some platforms (b/17379185).
- if (usingRGBAToJpegOverride(bufferFormat, writerCtxFormat)) {
- return HAL_PIXEL_FORMAT_BLOB;
- }
- return bufferFormat;
-}
-
-static uint32_t Image_getJpegSize(LockedImage* buffer, bool usingRGBAOverride) {
- ALOGV("%s", __FUNCTION__);
- ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!");
- uint32_t size = 0;
- uint32_t width = buffer->width;
- uint8_t* jpegBuffer = buffer->data;
-
- if (usingRGBAOverride) {
- width = (buffer->width + buffer->stride * (buffer->height - 1)) * 4;
- }
-
- // First check for JPEG transport header at the end of the buffer
- uint8_t* header = jpegBuffer + (width - sizeof(struct camera3_jpeg_blob));
- struct camera3_jpeg_blob *blob = (struct camera3_jpeg_blob*)(header);
- if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID) {
- size = blob->jpeg_size;
- ALOGV("%s: Jpeg size = %d", __FUNCTION__, size);
- }
-
- // failed to find size, default to whole buffer
- if (size == 0) {
- /*
- * This is a problem because not including the JPEG header
- * means that in certain rare situations a regular JPEG blob
- * will be misidentified as having a header, in which case
- * we will get a garbage size value.
- */
- ALOGW("%s: No JPEG header detected, defaulting to size=width=%d",
- __FUNCTION__, width);
- size = width;
- }
-
- return size;
-}
-
static void Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx,
int32_t writerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) {
ALOGV("%s", __FUNCTION__);
- ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!");
- ALOG_ASSERT(base != NULL, "base is NULL!!!");
- ALOG_ASSERT(size != NULL, "size is NULL!!!");
- ALOG_ASSERT(pixelStride != NULL, "pixelStride is NULL!!!");
- ALOG_ASSERT(rowStride != NULL, "rowStride is NULL!!!");
- ALOG_ASSERT((idx < IMAGE_WRITER_MAX_NUM_PLANES) && (idx >= 0));
- ALOGV("%s: buffer: %p", __FUNCTION__, buffer);
-
- uint32_t dataSize, ySize, cSize, cStride;
- uint32_t pStride = 0, rStride = 0;
- uint8_t *cb, *cr;
- uint8_t *pData = NULL;
- int bytesPerPixel = 0;
-
- dataSize = ySize = cSize = cStride = 0;
- int32_t fmt = buffer->flexFormat;
-
- bool usingRGBAOverride = usingRGBAToJpegOverride(fmt, writerFormat);
- fmt = applyFormatOverrides(fmt, writerFormat);
- switch (fmt) {
- case HAL_PIXEL_FORMAT_YCbCr_420_888:
- pData =
- (idx == 0) ?
- buffer->data :
- (idx == 1) ?
- buffer->dataCb :
- buffer->dataCr;
- // only map until last pixel
- if (idx == 0) {
- pStride = 1;
- rStride = buffer->stride;
- dataSize = buffer->stride * (buffer->height - 1) + buffer->width;
- } else {
- pStride = buffer->chromaStep;
- rStride = buffer->chromaStride;
- dataSize = buffer->chromaStride * (buffer->height / 2 - 1) +
- buffer->chromaStep * (buffer->width / 2 - 1) + 1;
- }
- break;
- // NV21
- case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- cr = buffer->data + (buffer->stride * buffer->height);
- cb = cr + 1;
- // only map until last pixel
- ySize = buffer->width * (buffer->height - 1) + buffer->width;
- cSize = buffer->width * (buffer->height / 2 - 1) + buffer->width - 1;
-
- pData =
- (idx == 0) ?
- buffer->data :
- (idx == 1) ?
- cb:
- cr;
-
- dataSize = (idx == 0) ? ySize : cSize;
- pStride = (idx == 0) ? 1 : 2;
- rStride = buffer->width;
- break;
- case HAL_PIXEL_FORMAT_YV12:
- // Y and C stride need to be 16 pixel aligned.
- LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
- "Stride is not 16 pixel aligned %d", buffer->stride);
-
- ySize = buffer->stride * buffer->height;
- cStride = ALIGN(buffer->stride / 2, 16);
- cr = buffer->data + ySize;
- cSize = cStride * buffer->height / 2;
- cb = cr + cSize;
-
- pData =
- (idx == 0) ?
- buffer->data :
- (idx == 1) ?
- cb :
- cr;
- dataSize = (idx == 0) ? ySize : cSize;
- pStride = 1;
- rStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
- break;
- case HAL_PIXEL_FORMAT_Y8:
- // Single plane, 8bpp.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
-
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height;
- pStride = 1;
- rStride = buffer->stride;
- break;
- case HAL_PIXEL_FORMAT_Y16:
- bytesPerPixel = 2;
- // Single plane, 16bpp, strides are specified in pixels, not in bytes
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
-
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- pStride = bytesPerPixel;
- rStride = buffer->stride * 2;
- break;
- case HAL_PIXEL_FORMAT_BLOB:
- // Used for JPEG data, height must be 1, width == size, single plane.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- ALOG_ASSERT(buffer->height == 1, "JPEG should has height value %d", buffer->height);
-
- pData = buffer->data;
- dataSize = Image_getJpegSize(buffer, usingRGBAOverride);
- pStride = bytesPerPixel;
- rowStride = 0;
- break;
- case HAL_PIXEL_FORMAT_RAW16:
- // Single plane 16bpp bayer data.
- bytesPerPixel = 2;
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- pStride = bytesPerPixel;
- rStride = buffer->stride * 2;
- break;
- case HAL_PIXEL_FORMAT_RAW10:
- // Single plane 10bpp bayer data.
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- LOG_ALWAYS_FATAL_IF(buffer->width % 4,
- "Width is not multiple of 4 %d", buffer->width);
- LOG_ALWAYS_FATAL_IF(buffer->height % 2,
- "Height is not even %d", buffer->height);
- LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 10 / 8),
- "stride (%d) should be at least %d",
- buffer->stride, buffer->width * 10 / 8);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height;
- pStride = 0;
- rStride = buffer->stride;
- break;
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
- // Single plane, 32bpp.
- bytesPerPixel = 4;
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- pStride = bytesPerPixel;
- rStride = buffer->stride * 4;
- break;
- case HAL_PIXEL_FORMAT_RGB_565:
- // Single plane, 16bpp.
- bytesPerPixel = 2;
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- pStride = bytesPerPixel;
- rStride = buffer->stride * 2;
- break;
- case HAL_PIXEL_FORMAT_RGB_888:
- // Single plane, 24bpp.
- bytesPerPixel = 3;
- ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
- pData = buffer->data;
- dataSize = buffer->stride * buffer->height * bytesPerPixel;
- pStride = bytesPerPixel;
- rStride = buffer->stride * 3;
- break;
- default:
- jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
- "Pixel format: 0x%x is unsupported", fmt);
- break;
+ status_t res = getLockedImageInfo(buffer, idx, writerFormat, base, size,
+ pixelStride, rowStride);
+ if (res != OK) {
+ jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
+ "Pixel format: 0x%x is unsupported", buffer->flexFormat);
}
-
- *base = pData;
- *size = dataSize;
- *pixelStride = pStride;
- *rowStride = rStride;
}
static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz,
@@ -1024,7 +757,8 @@
Image_getLockedImage(env, thiz, &lockedImg);
// Create all SurfacePlanes
- writerFormat = Image_getPixelFormat(env, writerFormat);
+ PublicFormat publicWriterFormat = static_cast<PublicFormat>(writerFormat);
+ writerFormat = android_view_Surface_mapPublicFormatToHalFormat(publicWriterFormat);
for (int i = 0; i < numPlanes; i++) {
Image_getLockedImageInfo(env, &lockedImg, i, writerFormat,
&pData, &dataSize, &pixelStride, &rowStride);
@@ -1044,39 +778,6 @@
return surfacePlanes;
}
-// -------------------------------Private convenience methods--------------------
-
-static bool isFormatOpaque(int format) {
- // Only treat IMPLEMENTATION_DEFINED as an opaque format for now.
- return format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
-}
-
-static bool isPossiblyYUV(PixelFormat format) {
- switch (static_cast<int>(format)) {
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_RGBX_8888:
- case HAL_PIXEL_FORMAT_RGB_888:
- case HAL_PIXEL_FORMAT_RGB_565:
- case HAL_PIXEL_FORMAT_BGRA_8888:
- case HAL_PIXEL_FORMAT_Y8:
- case HAL_PIXEL_FORMAT_Y16:
- case HAL_PIXEL_FORMAT_RAW16:
- case HAL_PIXEL_FORMAT_RAW10:
- case HAL_PIXEL_FORMAT_RAW_OPAQUE:
- case HAL_PIXEL_FORMAT_BLOB:
- case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
- return false;
-
- case HAL_PIXEL_FORMAT_YV12:
- case HAL_PIXEL_FORMAT_YCbCr_420_888:
- case HAL_PIXEL_FORMAT_YCbCr_422_SP:
- case HAL_PIXEL_FORMAT_YCrCb_420_SP:
- case HAL_PIXEL_FORMAT_YCbCr_422_I:
- default:
- return true;
- }
-}
-
} // extern "C"
// ----------------------------------------------------------------------------
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 2004a3a..810996e 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -65,6 +65,7 @@
jint cryptoErrorResourceBusy;
jint cryptoErrorInsufficientOutputProtection;
jint cryptoErrorSessionNotOpened;
+ jint cryptoErrorUnsupportedOperation;
} gCryptoErrorCodes;
static struct CodecActionCodes {
@@ -869,6 +870,10 @@
err = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
defaultMsg = "Attempted to use a closed session";
break;
+ case ERROR_DRM_CANNOT_HANDLE:
+ err = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
+ defaultMsg = "Operation not supported in this configuration";
+ break;
default: /* Other negative DRM error codes go out as is. */
break;
}
@@ -1302,7 +1307,10 @@
jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID);
CryptoPlugin::Pattern pattern;
- if (patternObj != NULL) {
+ if (patternObj == NULL) {
+ pattern.mEncryptBlocks = 0;
+ pattern.mSkipBlocks = 0;
+ } else {
pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID);
pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID);
}
@@ -1770,6 +1778,11 @@
gCryptoErrorCodes.cryptoErrorSessionNotOpened =
env->GetStaticIntField(clazz.get(), field);
+ field = env->GetStaticFieldID(clazz.get(), "ERROR_UNSUPPORTED_OPERATION", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorUnsupportedOperation =
+ env->GetStaticIntField(clazz.get(), field);
+
clazz.reset(env->FindClass("android/media/MediaCodec$CodecException"));
CHECK(clazz.get() != NULL);
field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I");
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 9c4f7c4..12833f4 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -26,32 +26,82 @@
#include <nativehelper/ScopedLocalRef.h>
+#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
+
namespace android {
+AssetStream::AssetStream(SkStream* stream)
+ : mStream(stream) {
+}
+
+AssetStream::~AssetStream() {
+}
+
+piex::Error AssetStream::GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) {
+ // Seek first.
+ if (mPosition != offset) {
+ if (!mStream->seek(offset)) {
+ return piex::Error::kFail;
+ }
+ }
+
+ // Read bytes.
+ size_t size = mStream->read((void*)data, length);
+ mPosition += size;
+
+ return size == length ? piex::Error::kOk : piex::Error::kFail;
+}
+
+BufferedStream::BufferedStream(SkStream* stream)
+ : mStream(stream) {
+}
+
+BufferedStream::~BufferedStream() {
+}
+
+piex::Error BufferedStream::GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) {
+ // Seek first.
+ if (offset + length > mStreamBuffer.bytesWritten()) {
+ size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
+ if (sizeToRead <= kMinSizeToRead) {
+ sizeToRead = kMinSizeToRead;
+ }
+ void* tempBuffer = malloc(sizeToRead);
+ if (tempBuffer != NULL) {
+ size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
+ if (bytesRead != sizeToRead) {
+ free(tempBuffer);
+ return piex::Error::kFail;
+ }
+ mStreamBuffer.write(tempBuffer, bytesRead);
+ free(tempBuffer);
+ }
+ }
+
+ // Read bytes.
+ if (mStreamBuffer.read((void*)data, offset, length)) {
+ return piex::Error::kOk;
+ } else {
+ return piex::Error::kFail;
+ }
+}
+
FileStream::FileStream(const int fd)
- : mPosition(0),
- mSize(0) {
+ : mPosition(0) {
mFile = fdopen(fd, "r");
if (mFile == NULL) {
return;
}
- // Get the size.
- fseek(mFile, 0l, SEEK_END);
- mSize = ftell(mFile);
- fseek(mFile, 0l, SEEK_SET);
}
FileStream::FileStream(const String8 filename)
- : mPosition(0),
- mSize(0) {
+ : mPosition(0) {
mFile = fopen(filename.string(), "r");
if (mFile == NULL) {
return;
}
- // Get the size.
- fseek(mFile, 0l, SEEK_END);
- mSize = ftell(mFile);
- fseek(mFile, 0l, SEEK_SET);
}
FileStream::~FileStream() {
@@ -77,7 +127,7 @@
mPosition += size;
// Handle errors.
- if (ferror(mFile) || (size == 0 && feof(mFile))) {
+ if (ferror(mFile)) {
ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
return piex::Error::kFail;
}
@@ -88,21 +138,12 @@
return mFile != NULL;
}
-size_t FileStream::size() const {
- return mSize;
-}
-
bool GetExifFromRawImage(
- FileStream* stream, const String8& filename, piex::PreviewImageData& image_data) {
+ piex::StreamInterface* stream, const String8& filename,
+ piex::PreviewImageData& image_data) {
// Reset the PreviewImageData to its default.
image_data = piex::PreviewImageData();
- if (!stream->exists()) {
- // File is not exists.
- ALOGV("File is not exists: %s", filename.string());
- return false;
- }
-
if (!piex::IsRaw(stream)) {
// Format not supported.
ALOGV("Format not supported: %s", filename.string());
@@ -117,12 +158,6 @@
return false;
}
- if (image_data.thumbnail_offset + image_data.thumbnail_length > stream->size()) {
- // Corrupted image.
- ALOGV("Corrupted file: %s", filename.string());
- return false;
- }
-
return true;
}
@@ -509,5 +544,398 @@
return OK;
}
+// -----------Utility functions used by ImageReader/Writer JNI-----------------
+
+enum {
+ IMAGE_MAX_NUM_PLANES = 3,
+};
+
+bool usingRGBAToJpegOverride(int32_t imageFormat,
+ int32_t containerFormat) {
+ return containerFormat == HAL_PIXEL_FORMAT_BLOB && imageFormat == HAL_PIXEL_FORMAT_RGBA_8888;
+}
+
+int32_t applyFormatOverrides(int32_t imageFormat, int32_t containerFormat) {
+ // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW
+ // write limitations for some platforms (b/17379185).
+ if (usingRGBAToJpegOverride(imageFormat, containerFormat)) {
+ return HAL_PIXEL_FORMAT_BLOB;
+ }
+ return containerFormat;
+}
+
+bool isFormatOpaque(int format) {
+ // This is the only opaque format exposed in the ImageFormat public API.
+ // Note that we do support CPU access for HAL_PIXEL_FORMAT_RAW_OPAQUE
+ // (ImageFormat#RAW_PRIVATE) so it doesn't count as opaque here.
+ return format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+}
+
+bool isPossiblyYUV(PixelFormat format) {
+ switch (static_cast<int>(format)) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_RGB_888:
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_Y8:
+ case HAL_PIXEL_FORMAT_Y16:
+ case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RAW10:
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ case HAL_PIXEL_FORMAT_BLOB:
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ return false;
+
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ default:
+ return true;
+ }
+}
+
+uint32_t Image_getJpegSize(LockedImage* buffer, bool usingRGBAOverride) {
+ ALOGV("%s", __FUNCTION__);
+ LOG_ALWAYS_FATAL_IF(buffer == NULL, "Input buffer is NULL!!!");
+ uint32_t size = 0;
+ uint32_t width = buffer->width;
+ uint8_t* jpegBuffer = buffer->data;
+
+ if (usingRGBAOverride) {
+ width = (buffer->width + buffer->stride * (buffer->height - 1)) * 4;
+ }
+
+ // First check for JPEG transport header at the end of the buffer
+ uint8_t* header = jpegBuffer + (width - sizeof(struct camera3_jpeg_blob));
+ struct camera3_jpeg_blob *blob = (struct camera3_jpeg_blob*)(header);
+ if (blob->jpeg_blob_id == CAMERA3_JPEG_BLOB_ID) {
+ size = blob->jpeg_size;
+ ALOGV("%s: Jpeg size = %d", __FUNCTION__, size);
+ }
+
+ // failed to find size, default to whole buffer
+ if (size == 0) {
+ /*
+ * This is a problem because not including the JPEG header
+ * means that in certain rare situations a regular JPEG blob
+ * will be mis-identified as having a header, in which case
+ * we will get a garbage size value.
+ */
+ ALOGW("%s: No JPEG header detected, defaulting to size=width=%d",
+ __FUNCTION__, width);
+ size = width;
+ }
+
+ return size;
+}
+
+status_t getLockedImageInfo(LockedImage* buffer, int idx,
+ int32_t containerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) {
+ ALOGV("%s", __FUNCTION__);
+ LOG_ALWAYS_FATAL_IF(buffer == NULL, "Input buffer is NULL!!!");
+ LOG_ALWAYS_FATAL_IF(base == NULL, "base is NULL!!!");
+ LOG_ALWAYS_FATAL_IF(size == NULL, "size is NULL!!!");
+ LOG_ALWAYS_FATAL_IF(pixelStride == NULL, "pixelStride is NULL!!!");
+ LOG_ALWAYS_FATAL_IF(rowStride == NULL, "rowStride is NULL!!!");
+ LOG_ALWAYS_FATAL_IF((idx >= IMAGE_MAX_NUM_PLANES) || (idx < 0), "idx (%d) is illegal", idx);
+
+ ALOGV("%s: buffer: %p", __FUNCTION__, buffer);
+
+ uint32_t dataSize, ySize, cSize, cStride;
+ uint32_t pStride = 0, rStride = 0;
+ uint8_t *cb, *cr;
+ uint8_t *pData = NULL;
+ int bytesPerPixel = 0;
+
+ dataSize = ySize = cSize = cStride = 0;
+ int32_t fmt = buffer->flexFormat;
+
+ bool usingRGBAOverride = usingRGBAToJpegOverride(fmt, containerFormat);
+ fmt = applyFormatOverrides(fmt, containerFormat);
+ switch (fmt) {
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ pData =
+ (idx == 0) ?
+ buffer->data :
+ (idx == 1) ?
+ buffer->dataCb :
+ buffer->dataCr;
+ // only map until last pixel
+ if (idx == 0) {
+ pStride = 1;
+ rStride = buffer->stride;
+ dataSize = buffer->stride * (buffer->height - 1) + buffer->width;
+ } else {
+ pStride = buffer->chromaStep;
+ rStride = buffer->chromaStride;
+ dataSize = buffer->chromaStride * (buffer->height / 2 - 1) +
+ buffer->chromaStep * (buffer->width / 2 - 1) + 1;
+ }
+ break;
+ // NV21
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ cr = buffer->data + (buffer->stride * buffer->height);
+ cb = cr + 1;
+ // only map until last pixel
+ ySize = buffer->width * (buffer->height - 1) + buffer->width;
+ cSize = buffer->width * (buffer->height / 2 - 1) + buffer->width - 1;
+
+ pData =
+ (idx == 0) ?
+ buffer->data :
+ (idx == 1) ?
+ cb:
+ cr;
+
+ dataSize = (idx == 0) ? ySize : cSize;
+ pStride = (idx == 0) ? 1 : 2;
+ rStride = buffer->width;
+ break;
+ case HAL_PIXEL_FORMAT_YV12:
+ // Y and C stride need to be 16 pixel aligned.
+ LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
+ "Stride is not 16 pixel aligned %d", buffer->stride);
+
+ ySize = buffer->stride * buffer->height;
+ cStride = ALIGN(buffer->stride / 2, 16);
+ cr = buffer->data + ySize;
+ cSize = cStride * buffer->height / 2;
+ cb = cr + cSize;
+
+ pData =
+ (idx == 0) ?
+ buffer->data :
+ (idx == 1) ?
+ cb :
+ cr;
+ dataSize = (idx == 0) ? ySize : cSize;
+ pStride = 1;
+ rStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
+ break;
+ case HAL_PIXEL_FORMAT_Y8:
+ // Single plane, 8bpp.
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height;
+ pStride = 1;
+ rStride = buffer->stride;
+ break;
+ case HAL_PIXEL_FORMAT_Y16:
+ bytesPerPixel = 2;
+ // Single plane, 16bpp, strides are specified in pixels, not in bytes
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height * bytesPerPixel;
+ pStride = bytesPerPixel;
+ rStride = buffer->stride * 2;
+ break;
+ case HAL_PIXEL_FORMAT_BLOB:
+ // Used for JPEG data, height must be 1, width == size, single plane.
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+ // When RGBA override is being used, buffer height will be equal to width
+ if (usingRGBAOverride) {
+ LOG_ALWAYS_FATAL_IF(buffer->height != buffer->width,
+ "RGBA override BLOB format buffer should have height == width");
+ } else {
+ LOG_ALWAYS_FATAL_IF(buffer->height != 1,
+ "BLOB format buffer should have height value 1");
+ }
+
+
+ pData = buffer->data;
+ dataSize = Image_getJpegSize(buffer, usingRGBAOverride);
+ pStride = 0;
+ rStride = 0;
+ break;
+ case HAL_PIXEL_FORMAT_RAW16:
+ // Single plane 16bpp bayer data.
+ bytesPerPixel = 2;
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height * bytesPerPixel;
+ pStride = bytesPerPixel;
+ rStride = buffer->stride * 2;
+ break;
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ // Used for RAW_OPAQUE data, height must be 1, width == size, single plane.
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+ LOG_ALWAYS_FATAL_IF(buffer->height != 1,
+ "RAW_PRIVATE should has height value one but got %d", buffer->height);
+ pData = buffer->data;
+ dataSize = buffer->width;
+ pStride = 0; // RAW OPAQUE doesn't have pixel stride
+ rStride = 0; // RAW OPAQUE doesn't have row stride
+ break;
+ case HAL_PIXEL_FORMAT_RAW10:
+ // Single plane 10bpp bayer data.
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+ LOG_ALWAYS_FATAL_IF(buffer->width % 4,
+ "Width is not multiple of 4 %d", buffer->width);
+ LOG_ALWAYS_FATAL_IF(buffer->height % 2,
+ "Height is not even %d", buffer->height);
+ LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 10 / 8),
+ "stride (%d) should be at least %d",
+ buffer->stride, buffer->width * 10 / 8);
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height;
+ pStride = 0;
+ rStride = buffer->stride;
+ break;
+ case HAL_PIXEL_FORMAT_RAW12:
+ // Single plane 10bpp bayer data.
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+ LOG_ALWAYS_FATAL_IF(buffer->width % 4,
+ "Width is not multiple of 4 %d", buffer->width);
+ LOG_ALWAYS_FATAL_IF(buffer->height % 2,
+ "Height is not even %d", buffer->height);
+ LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 12 / 8),
+ "stride (%d) should be at least %d",
+ buffer->stride, buffer->width * 12 / 8);
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height;
+ pStride = 0;
+ rStride = buffer->stride;
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ // Single plane, 32bpp.
+ bytesPerPixel = 4;
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height * bytesPerPixel;
+ pStride = bytesPerPixel;
+ rStride = buffer->stride * 4;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ // Single plane, 16bpp.
+ bytesPerPixel = 2;
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height * bytesPerPixel;
+ pStride = bytesPerPixel;
+ rStride = buffer->stride * 2;
+ break;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ // Single plane, 24bpp.
+ bytesPerPixel = 3;
+ LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
+ pData = buffer->data;
+ dataSize = buffer->stride * buffer->height * bytesPerPixel;
+ pStride = bytesPerPixel;
+ rStride = buffer->stride * 3;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+
+ *base = pData;
+ *size = dataSize;
+ *pixelStride = pStride;
+ *rowStride = rStride;
+
+ return OK;
+}
+
+status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage,
+ const Rect& rect, int fenceFd, LockedImage* outputImage) {
+ ALOGV("%s: Try to lock the GraphicBuffer", __FUNCTION__);
+
+ if (buffer == nullptr || outputImage == nullptr) {
+ ALOGE("Input BufferItem or output LockedImage is NULL!");
+ return BAD_VALUE;
+ }
+ if (isFormatOpaque(buffer->getPixelFormat())) {
+ ALOGE("Opaque format buffer is not lockable!");
+ return BAD_VALUE;
+ }
+
+ void* pData = NULL;
+ android_ycbcr ycbcr = android_ycbcr();
+ status_t res;
+ int format = buffer->getPixelFormat();
+ int flexFormat = format;
+ if (isPossiblyYUV(format)) {
+ res = buffer->lockAsyncYCbCr(inUsage, rect, &ycbcr, fenceFd);
+ pData = ycbcr.y;
+ flexFormat = HAL_PIXEL_FORMAT_YCbCr_420_888;
+ }
+
+ // lockAsyncYCbCr for YUV is unsuccessful.
+ if (pData == NULL) {
+ res = buffer->lockAsync(inUsage, rect, &pData, fenceFd);
+ if (res != OK) {
+ ALOGE("Lock buffer failed!");
+ return res;
+ }
+ }
+
+ outputImage->data = reinterpret_cast<uint8_t*>(pData);
+ outputImage->width = buffer->getWidth();
+ outputImage->height = buffer->getHeight();
+ outputImage->format = format;
+ outputImage->flexFormat = flexFormat;
+ outputImage->stride =
+ (ycbcr.y != NULL) ? static_cast<uint32_t>(ycbcr.ystride) : buffer->getStride();
+
+ outputImage->dataCb = reinterpret_cast<uint8_t*>(ycbcr.cb);
+ outputImage->dataCr = reinterpret_cast<uint8_t*>(ycbcr.cr);
+ outputImage->chromaStride = static_cast<uint32_t>(ycbcr.cstride);
+ outputImage->chromaStep = static_cast<uint32_t>(ycbcr.chroma_step);
+ ALOGV("%s: Successfully locked the image from the GraphicBuffer", __FUNCTION__);
+ // Crop, transform, scalingMode, timestamp, and frameNumber should be set by caller,
+ // and cann't be set them here.
+ return OK;
+}
+
+status_t lockImageFromBuffer(BufferItem* bufferItem, uint32_t inUsage,
+ int fenceFd, LockedImage* outputImage) {
+ ALOGV("%s: Try to lock the BufferItem", __FUNCTION__);
+ if (bufferItem == nullptr || outputImage == nullptr) {
+ ALOGE("Input BufferItem or output LockedImage is NULL!");
+ return BAD_VALUE;
+ }
+
+ status_t res = lockImageFromBuffer(bufferItem->mGraphicBuffer, inUsage, bufferItem->mCrop,
+ fenceFd, outputImage);
+ if (res != OK) {
+ ALOGE("%s: lock graphic buffer failed", __FUNCTION__);
+ return res;
+ }
+
+ outputImage->crop = bufferItem->mCrop;
+ outputImage->transform = bufferItem->mTransform;
+ outputImage->scalingMode = bufferItem->mScalingMode;
+ outputImage->timestamp = bufferItem->mTimestamp;
+ outputImage->dataSpace = bufferItem->mDataSpace;
+ outputImage->frameNumber = bufferItem->mFrameNumber;
+ ALOGV("%s: Successfully locked the image from the BufferItem", __FUNCTION__);
+ return OK;
+}
+
+int getBufferWidth(BufferItem* buffer) {
+ if (buffer == NULL) return -1;
+
+ if (!buffer->mCrop.isEmpty()) {
+ return buffer->mCrop.getWidth();
+ }
+
+ ALOGV("%s: buffer->mGraphicBuffer: %p", __FUNCTION__, buffer->mGraphicBuffer.get());
+ return buffer->mGraphicBuffer->getWidth();
+}
+
+int getBufferHeight(BufferItem* buffer) {
+ if (buffer == NULL) return -1;
+
+ if (!buffer->mCrop.isEmpty()) {
+ return buffer->mCrop.getHeight();
+ }
+
+ ALOGV("%s: buffer->mGraphicBuffer: %p", __FUNCTION__, buffer->mGraphicBuffer.get());
+ return buffer->mGraphicBuffer->getHeight();
+}
+
} // namespace android
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index a30e1be..8184f94 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -21,18 +21,62 @@
#include "src/piex.h"
#include <android_runtime/AndroidRuntime.h>
+#include <camera3.h>
+#include <gui/CpuConsumer.h>
#include <jni.h>
#include <JNIHelp.h>
#include <utils/KeyedVector.h>
#include <utils/String8.h>
+#include <SkStream.h>
namespace android {
+class AssetStream : public piex::StreamInterface {
+private:
+ SkStream *mStream;
+ size_t mPosition;
+
+public:
+ AssetStream(SkStream* stream);
+ ~AssetStream();
+
+ // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+ // provided by the caller, guaranteed to be at least "length" bytes long.
+ // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+ // 'offset' bytes from the start of the stream.
+ // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+ // change the contents of 'data'.
+ piex::Error GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) override;
+};
+
+class BufferedStream : public piex::StreamInterface {
+private:
+ SkStream *mStream;
+ // Growable memory stream
+ SkDynamicMemoryWStream mStreamBuffer;
+
+ // Minimum size to read on filling the buffer.
+ const size_t kMinSizeToRead = 8192;
+
+public:
+ BufferedStream(SkStream* stream);
+ ~BufferedStream();
+
+ // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
+ // provided by the caller, guaranteed to be at least "length" bytes long.
+ // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
+ // 'offset' bytes from the start of the stream.
+ // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
+ // change the contents of 'data'.
+ piex::Error GetData(
+ const size_t offset, const size_t length, std::uint8_t* data) override;
+};
+
class FileStream : public piex::StreamInterface {
private:
FILE *mFile;
size_t mPosition;
- size_t mSize;
public:
FileStream(const int fd);
@@ -48,13 +92,12 @@
piex::Error GetData(
const size_t offset, const size_t length, std::uint8_t* data) override;
bool exists() const;
- size_t size() const;
};
// Reads EXIF metadata from a given raw image via piex.
// And returns true if the operation is successful; otherwise, false.
bool GetExifFromRawImage(
- FileStream* stream, const String8& filename, piex::PreviewImageData& image_data);
+ piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data);
// Returns true if the conversion is successful; otherwise, false.
bool ConvertKeyValueArraysToKeyedVector(
@@ -69,6 +112,33 @@
JNIEnv *env, jobjectArray keys, jobjectArray values,
sp<AMessage> *msg);
+// -----------Utility functions used by ImageReader/Writer JNI-----------------
+
+typedef CpuConsumer::LockedBuffer LockedImage;
+
+bool usingRGBAToJpegOverride(int32_t imageFormat, int32_t containerFormat);
+
+int32_t applyFormatOverrides(int32_t imageFormat, int32_t containerFormat);
+
+uint32_t Image_getJpegSize(LockedImage* buffer, bool usingRGBAOverride);
+
+bool isFormatOpaque(int format);
+
+bool isPossiblyYUV(PixelFormat format);
+
+status_t getLockedImageInfo(LockedImage* buffer, int idx, int32_t containerFormat,
+ uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride);
+
+status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage,
+ const Rect& rect, int fenceFd, LockedImage* outputImage);
+
+status_t lockImageFromBuffer(BufferItem* bufferItem, uint32_t inUsage,
+ int fenceFd, LockedImage* outputImage);
+
+int getBufferWidth(BufferItem *buffer);
+
+int getBufferHeight(BufferItem *buffer);
+
}; // namespace android
#endif // _ANDROID_MEDIA_UTILS_H_
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index 7c1142b..7e438a1 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -11,7 +11,6 @@
LOCAL_STATIC_JAVA_LIBRARIES := easymocklib \
mockito-target \
- core-tests \
android-support-test \
android-ex-camera2
diff --git a/media/tests/MediaFrameworkTest/assets/image_exif_byte_order_ii.jpg b/media/tests/MediaFrameworkTest/assets/image_exif_byte_order_ii.jpg
new file mode 100644
index 0000000..477cd3a
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/assets/image_exif_byte_order_ii.jpg
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/assets/image_exif_byte_order_mm.jpg b/media/tests/MediaFrameworkTest/assets/image_exif_byte_order_mm.jpg
new file mode 100644
index 0000000..78ac703
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/assets/image_exif_byte_order_mm.jpg
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/assets/lg_g4_iso_800.dng b/media/tests/MediaFrameworkTest/assets/lg_g4_iso_800.dng
new file mode 100644
index 0000000..5fcc720
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/assets/lg_g4_iso_800.dng
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/assets/volantis.jpg b/media/tests/MediaFrameworkTest/assets/volantis.jpg
new file mode 100644
index 0000000..cfe300f
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/assets/volantis.jpg
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/res/raw/volantis.jpg b/media/tests/MediaFrameworkTest/res/raw/volantis.jpg
new file mode 100644
index 0000000..cfe300f
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/res/raw/volantis.jpg
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/res/values/exifinterface.xml b/media/tests/MediaFrameworkTest/res/values/exifinterface.xml
index 8fc6adc..d556ad3 100644
--- a/media/tests/MediaFrameworkTest/res/values/exifinterface.xml
+++ b/media/tests/MediaFrameworkTest/res/values/exifinterface.xml
@@ -76,9 +76,9 @@
<item>0</item>
</array>
<array name="lg_g4_iso_800_dng">
- <item>false</item>
- <item>0</item>
- <item>0</item>
+ <item>true</item>
+ <item>256</item>
+ <item>144</item>
<item>true</item>
<item>53.834507</item>
<item>10.69585</item>
@@ -105,4 +105,34 @@
<item>1</item>
<item />
</array>
+ <array name="volantis_jpg">
+ <item>false</item>
+ <item>0</item>
+ <item>0</item>
+ <item>true</item>
+ <item>37.423</item>
+ <item>-122.162</item>
+ <item>0.0</item>
+ <item>htc</item>
+ <item>Nexus 9</item>
+ <item>1.2904</item>
+ <item>2016:03:09 17:36:42</item>
+ <item>0.0083</item>
+ <item>64</item>
+ <item>3097/1000</item>
+ <item />
+ <item />
+ <item>2016:03:09</item>
+ <item>37/1,25/1,2291/100</item>
+ <item>N</item>
+ <item>122/1,9/1,4330/100</item>
+ <item>W</item>
+ <item />
+ <item>08:35:34</item>
+ <item>720</item>
+ <item>1280</item>
+ <item>175</item>
+ <item>1</item>
+ <item>0</item>
+ </array>
</resources>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
index 1c80746..5bd6079 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java
@@ -23,20 +23,20 @@
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.os.Environment;
-import android.os.ParcelFileDescriptor;
import android.test.AndroidTestCase;
import android.util.Log;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
-import java.lang.reflect.Type;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -46,16 +46,17 @@
private static final boolean VERBOSE = false; // lots of logging
private static final double DIFFERENCE_TOLERANCE = .005;
- private static final int BUFFER_SIZE = 32768;
// List of files.
- private static final String EXIF_BYTE_ORDER_II_JPEG = "ExifByteOrderII.jpg";
- private static final String EXIF_BYTE_ORDER_MM_JPEG = "ExifByteOrderMM.jpg";
+ private static final String EXIF_BYTE_ORDER_II_JPEG = "image_exif_byte_order_ii.jpg";
+ private static final String EXIF_BYTE_ORDER_MM_JPEG = "image_exif_byte_order_mm.jpg";
private static final String LG_G4_ISO_800_DNG = "lg_g4_iso_800.dng";
+ private static final String VOLANTIS_JPEG = "volantis.jpg";
private static final int[] IMAGE_RESOURCES = new int[] {
- R.raw.image_exif_byte_order_ii, R.raw.image_exif_byte_order_mm, R.raw.lg_g4_iso_800 };
+ R.raw.image_exif_byte_order_ii, R.raw.image_exif_byte_order_mm, R.raw.lg_g4_iso_800,
+ R.raw.volantis };
private static final String[] IMAGE_FILENAMES = new String[] {
- EXIF_BYTE_ORDER_II_JPEG, EXIF_BYTE_ORDER_MM_JPEG, LG_G4_ISO_800_DNG };
+ EXIF_BYTE_ORDER_II_JPEG, EXIF_BYTE_ORDER_MM_JPEG, LG_G4_ISO_800_DNG, VOLANTIS_JPEG };
private static final String[] EXIF_TAGS = {
ExifInterface.TAG_MAKE,
@@ -165,8 +166,6 @@
@Override
protected void setUp() throws Exception {
- byte[] buffer = new byte[BUFFER_SIZE];
-
for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
String outputPath = new File(Environment.getExternalStorageDirectory(),
IMAGE_FILENAMES[i]).getAbsolutePath();
@@ -314,26 +313,30 @@
expectedValue.whiteBalance);
}
- private void testExifInterfaceForJpeg(String fileName, int typedArrayResourceId)
+ private void testExifInterfaceCommon(File imageFile, ExpectedValue expectedValue)
throws IOException {
- ExpectedValue expectedValue = new ExpectedValue(
- getContext().getResources().obtainTypedArray(typedArrayResourceId));
- File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
-
// Created via path.
ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
if (VERBOSE) {
- printExifTagsAndValues(fileName, exifInterface);
+ printExifTagsAndValues(imageFile.getName(), exifInterface);
+ }
+ compareWithExpectedValue(exifInterface, expectedValue);
+
+ // Created from an asset file.
+ InputStream in = mContext.getAssets().open(imageFile.getName());
+ exifInterface = new ExifInterface(in);
+ if (VERBOSE) {
+ printExifTagsAndValues(imageFile.getName(), exifInterface);
}
compareWithExpectedValue(exifInterface, expectedValue);
// Created via InputStream.
- FileInputStream in = null;
+ in = null;
try {
- in = new FileInputStream(imageFile.getAbsolutePath());
+ in = new BufferedInputStream(new FileInputStream(imageFile.getAbsolutePath()));
exifInterface = new ExifInterface(in);
if (VERBOSE) {
- printExifTagsAndValues(fileName, exifInterface);
+ printExifTagsAndValues(imageFile.getName(), exifInterface);
}
compareWithExpectedValue(exifInterface, expectedValue);
} finally {
@@ -345,18 +348,30 @@
FileDescriptor fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
exifInterface = new ExifInterface(fd);
if (VERBOSE) {
- printExifTagsAndValues(fileName, exifInterface);
+ printExifTagsAndValues(imageFile.getName(), exifInterface);
}
compareWithExpectedValue(exifInterface, expectedValue);
} catch (ErrnoException e) {
e.rethrowAsIOException();
}
+ }
+
+ private void testExifInterfaceForJpeg(String fileName, int typedArrayResourceId)
+ throws IOException {
+ ExpectedValue expectedValue = new ExpectedValue(
+ getContext().getResources().obtainTypedArray(typedArrayResourceId));
+ File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
+
+ // Test for reading from various inputs.
+ testExifInterfaceCommon(imageFile, expectedValue);
// Test for saving attributes.
+ ExifInterface exifInterface;
try {
FileDescriptor fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600);
exifInterface = new ExifInterface(fd);
exifInterface.saveAttributes();
+ fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600);
exifInterface = new ExifInterface(fd);
if (VERBOSE) {
printExifTagsAndValues(fileName, exifInterface);
@@ -383,25 +398,11 @@
getContext().getResources().obtainTypedArray(typedArrayResourceId));
File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
- // Created via path.
- ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath());
- if (VERBOSE) {
- printExifTagsAndValues(fileName, exifInterface);
- }
- compareWithExpectedValue(exifInterface, expectedValue);
+ // Test for reading from various inputs.
+ testExifInterfaceCommon(imageFile, expectedValue);
- // Created via FileDescriptor.
- FileInputStream in = null;
- try {
- in = new FileInputStream(imageFile);
- exifInterface = new ExifInterface(in.getFD());
- if (VERBOSE) {
- printExifTagsAndValues(fileName, exifInterface);
- }
- compareWithExpectedValue(exifInterface, expectedValue);
- } finally {
- IoUtils.closeQuietly(in);
- }
+ // Since ExifInterface does not support for saving attributes for RAW files, do not test
+ // about writing back in here.
}
public void testReadExifDataFromExifByteOrderIIJpeg() throws Throwable {
@@ -415,4 +416,19 @@
public void testReadExifDataFromLgG4Iso800Dng() throws Throwable {
testExifInterfaceForRaw(LG_G4_ISO_800_DNG, R.array.lg_g4_iso_800_dng);
}
+
+ public void testCorruptedImage() throws Throwable {
+ byte[] bytes = new byte[1024];
+ try {
+ new ExifInterface(new ByteArrayInputStream(bytes));
+ fail("Should not reach here!");
+ } catch (IOException e) {
+ // Success
+ }
+ }
+
+ public void testReadExifDataFromVolantisJpg() throws Throwable {
+ // Test if it is possible to parse the volantis generated JPEG smoothly.
+ testExifInterfaceForJpeg(VOLANTIS_JPEG, R.array.volantis_jpg);
+ }
}
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 637e06e..6fe239e 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -5,6 +5,8 @@
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.REMOVE_TASKS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.CACHE_CONTENT" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name=".DocumentsApplication"
@@ -40,18 +42,6 @@
</activity>
<activity
- android:name=".DownloadsActivity"
- android:theme="@style/DocumentsTheme"
- android:label="@string/downloads_label"
- android:icon="@drawable/ic_doc_text">
- <intent-filter>
- <action android:name="android.provider.action.MANAGE_ROOT" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.document/root" />
- </intent-filter>
- </activity>
-
- <activity
android:name=".LauncherActivity"
android:theme="@android:style/Theme.NoDisplay"
android:icon="@drawable/ic_files_app"
@@ -72,6 +62,10 @@
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<intent-filter>
+ <action android:name="android.intent.action.VIEW_DOWNLOADS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
<action android:name="android.provider.action.BROWSE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.document/root" />
@@ -113,6 +107,12 @@
</intent-filter>
</receiver>
+ <receiver android:name=".BootReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ </intent-filter>
+ </receiver>
+
<service
android:name=".services.FileOperationService"
android:exported="false">
diff --git a/packages/DocumentsUI/perf-tests/Android.mk b/packages/DocumentsUI/perf-tests/Android.mk
index c83094e..11c163b 100644
--- a/packages/DocumentsUI/perf-tests/Android.mk
+++ b/packages/DocumentsUI/perf-tests/Android.mk
@@ -10,8 +10,8 @@
../tests/src/com/android/documentsui/DocumentsProviderHelper.java \
../tests/src/com/android/documentsui/StubProvider.java
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target ub-uiautomator
+LOCAL_JAVA_LIBRARIES := android-support-v4 android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator
LOCAL_PACKAGE_NAME := DocumentsUIPerfTests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
diff --git a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesActivityPerfTest.java b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesActivityPerfTest.java
index 05fd2f7..bf056f1 100644
--- a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesActivityPerfTest.java
+++ b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesActivityPerfTest.java
@@ -121,7 +121,7 @@
activity.removeEventListener(listener);
}
- assertEquals(i, measurements.size());
+ assertEquals(i + 1, measurements.size());
// Go back to the empty root.
bots.roots.openRoot(STRESS_ROOT_0_ID);
diff --git a/packages/DocumentsUI/res/animator-ldrtl/dir_leave.xml b/packages/DocumentsUI/res/animator-ldrtl/dir_leave.xml
deleted file mode 100644
index 8e2925c..0000000
--- a/packages/DocumentsUI/res/animator-ldrtl/dir_leave.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="-1"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime"
- android:interpolator="@android:interpolator/accelerate_quad" />
diff --git a/packages/DocumentsUI/res/animator/dir_enter.xml b/packages/DocumentsUI/res/animator/dir_enter.xml
index 7f547f1..43c50bd 100644
--- a/packages/DocumentsUI/res/animator/dir_enter.xml
+++ b/packages/DocumentsUI/res/animator/dir_enter.xml
@@ -13,10 +13,24 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="1"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime"
- android:interpolator="@android:interpolator/decelerate_quad" />
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="together">
+
+ <objectAnimator
+ android:valueFrom="0f"
+ android:valueTo="1f"
+ android:propertyName="alpha"
+ android:valueType="floatType"
+ android:duration="@android:integer/config_mediumAnimTime"
+ android:interpolator="@android:interpolator/decelerate_quad" />
+
+ <!-- position property maps to AnimationView.setPosition -->
+ <objectAnimator
+ android:propertyName="position"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:duration="@android:integer/config_mediumAnimTime"
+ android:interpolator="@android:interpolator/decelerate_quad" />
+
+</set>
diff --git a/packages/DocumentsUI/res/animator/dir_leave.xml b/packages/DocumentsUI/res/animator/dir_leave.xml
index fda0faf..7574655 100644
--- a/packages/DocumentsUI/res/animator/dir_leave.xml
+++ b/packages/DocumentsUI/res/animator/dir_leave.xml
@@ -13,10 +13,24 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="1"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime"
- android:interpolator="@android:interpolator/accelerate_quad" />
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="together">
+
+ <objectAnimator
+ android:valueFrom="1f"
+ android:valueTo="0f"
+ android:propertyName="alpha"
+ android:valueType="floatType"
+ android:duration="@android:integer/config_mediumAnimTime"
+ android:interpolator="@android:interpolator/decelerate_quad" />
+
+ <!-- position property maps to AnimationView.setPosition -->
+ <objectAnimator
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:propertyName="position"
+ android:valueType="floatType"
+ android:duration="@android:integer/config_mediumAnimTime"
+ android:interpolator="@android:interpolator/accelerate_quad" />
+
+</set>
\ No newline at end of file
diff --git a/packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml b/packages/DocumentsUI/res/animator/fade_in.xml
similarity index 90%
rename from packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml
rename to packages/DocumentsUI/res/animator/fade_in.xml
index 6c7e224..3ce012b 100644
--- a/packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml
+++ b/packages/DocumentsUI/res/animator/fade_in.xml
@@ -14,9 +14,9 @@
-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="-1"
- android:valueTo="0"
- android:propertyName="position"
+ android:valueFrom="0f"
+ android:valueTo="1f"
+ android:propertyName="alpha"
android:valueType="floatType"
android:duration="@android:integer/config_mediumAnimTime"
android:interpolator="@android:interpolator/decelerate_quad" />
diff --git a/packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml b/packages/DocumentsUI/res/animator/fade_out.xml
similarity index 90%
copy from packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml
copy to packages/DocumentsUI/res/animator/fade_out.xml
index 6c7e224..8d02c77 100644
--- a/packages/DocumentsUI/res/animator-ldrtl/dir_enter.xml
+++ b/packages/DocumentsUI/res/animator/fade_out.xml
@@ -14,9 +14,9 @@
-->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="-1"
- android:valueTo="0"
- android:propertyName="position"
+ android:valueFrom="1f"
+ android:valueTo="0f"
+ android:propertyName="alpha"
android:valueType="floatType"
android:duration="@android:integer/config_mediumAnimTime"
android:interpolator="@android:interpolator/decelerate_quad" />
diff --git a/packages/DocumentsUI/res/color/item_details.xml b/packages/DocumentsUI/res/color/item_details.xml
new file mode 100644
index 0000000..769b944
--- /dev/null
+++ b/packages/DocumentsUI/res/color/item_details.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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_enabled="true"
+ android:color="?android:attr/textColorPrimary"
+ android:alpha="0.63" />
+ <item
+ android:state_enabled="false"
+ android:color="?android:attr/textColorPrimary"
+ android:alpha="0.3" />
+</selector>
diff --git a/packages/DocumentsUI/res/color/item_title.xml b/packages/DocumentsUI/res/color/item_title.xml
new file mode 100644
index 0000000..ef6aea3
--- /dev/null
+++ b/packages/DocumentsUI/res/color/item_title.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_enabled="true"
+ android:color="?android:attr/textColorPrimary" />
+ <item
+ android:state_enabled="false"
+ android:color="?android:attr/textColorPrimary"
+ android:alpha="0.3" />
+</selector>
diff --git a/packages/DocumentsUI/res/drawable/ic_breadcrumb_arrow_down.xml b/packages/DocumentsUI/res/drawable/ic_breadcrumb_arrow_down.xml
new file mode 100644
index 0000000..199a308
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/ic_breadcrumb_arrow_down.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromDegrees="90"
+ android:toDegrees="90"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:drawable="@drawable/ic_breadcrumb_arrow">
+</rotate>
\ No newline at end of file
diff --git a/packages/DocumentsUI/res/layout/dialog_delete_confirmation.xml b/packages/DocumentsUI/res/layout/dialog_delete_confirmation.xml
index 990ce0b..a1c2910 100644
--- a/packages/DocumentsUI/res/layout/dialog_delete_confirmation.xml
+++ b/packages/DocumentsUI/res/layout/dialog_delete_confirmation.xml
@@ -18,8 +18,9 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingTop="30dp"
- android:gravity="center"
+ android:paddingTop="24dp"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
android:textAppearance="@android:style/TextAppearance.Material.Subhead"
android:textColor="@*android:color/primary_text_default_material_light">
</TextView>
diff --git a/packages/DocumentsUI/res/layout/drawer_layout.xml b/packages/DocumentsUI/res/layout/drawer_layout.xml
index 065102b..b65c5a0 100644
--- a/packages/DocumentsUI/res/layout/drawer_layout.xml
+++ b/packages/DocumentsUI/res/layout/drawer_layout.xml
@@ -46,6 +46,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
+ android:popupTheme="?actionBarPopupTheme"
+ android:background="@android:color/transparent"
android:overlapAnchor="true" />
</com.android.documentsui.DocumentsToolbar>
diff --git a/packages/DocumentsUI/res/layout/fixed_layout.xml b/packages/DocumentsUI/res/layout/fixed_layout.xml
index 84a928d..deb0894 100644
--- a/packages/DocumentsUI/res/layout/fixed_layout.xml
+++ b/packages/DocumentsUI/res/layout/fixed_layout.xml
@@ -44,6 +44,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
+ android:popupTheme="?actionBarPopupTheme"
+ android:background="@android:color/transparent"
android:overlapAnchor="true" />
</com.android.documentsui.DocumentsToolbar>
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index 03c6a83..8eb46ddf 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -14,7 +14,8 @@
limitations under the License.
-->
-<com.android.documentsui.DirectoryView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.dirlist.AnimationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/directory_background"
@@ -99,4 +100,4 @@
</FrameLayout>
-</com.android.documentsui.DirectoryView>
+</com.android.documentsui.dirlist.AnimationView>
diff --git a/packages/DocumentsUI/res/layout/item_dir_grid.xml b/packages/DocumentsUI/res/layout/item_dir_grid.xml
index a4f06d1..429a972 100644
--- a/packages/DocumentsUI/res/layout/item_dir_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_dir_grid.xml
@@ -66,7 +66,7 @@
android:singleLine="true"
android:textAlignment="viewStart"
android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="@*android:color/primary_text_default_material_light" />
+ android:textColor="@color/item_title" />
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml
index af1703f..56a061f 100644
--- a/packages/DocumentsUI/res/layout/item_doc_grid.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml
@@ -93,7 +93,7 @@
android:ellipsize="end"
android:textAlignment="viewStart"
android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="@*android:color/primary_text_default_material_light" />
+ android:textColor="@color/item_title" />
<TextView
android:id="@+id/size"
@@ -106,7 +106,7 @@
android:ellipsize="end"
android:textAlignment="viewStart"
android:textAppearance="@android:style/TextAppearance.Material.Caption"
- android:textColor="@*android:color/primary_text_default_material_light" />
+ android:textColor="@color/item_details" />
<TextView
android:id="@+id/date"
@@ -118,7 +118,7 @@
android:ellipsize="end"
android:textAlignment="viewStart"
android:textAppearance="@android:style/TextAppearance.Material.Caption"
- android:textColor="@*android:color/primary_text_default_material_light" />
+ android:textColor="@color/item_details" />
</RelativeLayout>
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index 29f65e09..a939fcd 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -85,7 +85,7 @@
android:singleLine="true"
android:textAlignment="viewStart"
android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="?android:attr/textColorPrimary" />
+ android:textColor="@color/item_title" />
<LinearLayout
android:id="@+id/line2"
@@ -102,8 +102,8 @@
android:ellipsize="end"
android:singleLine="true"
android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Body1"
- android:textColor="?android:attr/textColorSecondary" />
+ android:textAppearance="@android:style/TextAppearance.Material.Caption"
+ android:textColor="@color/item_details" />
<TextView
android:id="@+id/size"
@@ -113,8 +113,8 @@
android:ellipsize="end"
android:singleLine="true"
android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Body1"
- android:textColor="?android:attr/textColorSecondary" />
+ android:textAppearance="@android:style/TextAppearance.Material.Caption"
+ android:textColor="@color/item_details" />
<TextView
android:id="@android:id/summary"
@@ -125,8 +125,8 @@
android:ellipsize="end"
android:singleLine="true"
android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Body1"
- android:textColor="?android:attr/textColorSecondary" />
+ android:textAppearance="@android:style/TextAppearance.Material.Caption"
+ android:textColor="@color/item_details" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/item_subdir.xml b/packages/DocumentsUI/res/layout/item_subdir.xml
index b8251d1..ffe4afe 100644
--- a/packages/DocumentsUI/res/layout/item_subdir.xml
+++ b/packages/DocumentsUI/res/layout/item_subdir.xml
@@ -24,16 +24,6 @@
android:orientation="horizontal"
android:baselineAligned="false">
- <ImageView
- android:id="@+id/subdir"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:paddingEnd="8dp"
- android:scaleType="centerInside"
- android:visibility="gone"
- android:src="@drawable/ic_subdirectory_arrow"
- android:contentDescription="@null" />
-
<TextView
android:id="@android:id/title"
android:layout_width="0dp"
diff --git a/packages/DocumentsUI/res/layout/item_subdir_title.xml b/packages/DocumentsUI/res/layout/item_subdir_title.xml
index de6c523..8d0d807 100644
--- a/packages/DocumentsUI/res/layout/item_subdir_title.xml
+++ b/packages/DocumentsUI/res/layout/item_subdir_title.xml
@@ -28,6 +28,7 @@
android:singleLine="true"
android:ellipsize="end"
android:textAlignment="viewStart"
+ android:drawablePadding="12dp"
+ android:drawableRight="@drawable/ic_breadcrumb_arrow_down"
android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title" />
-
</LinearLayout>
diff --git a/packages/DocumentsUI/res/layout/single_pane_layout.xml b/packages/DocumentsUI/res/layout/single_pane_layout.xml
index 235d22d..7b7e229 100644
--- a/packages/DocumentsUI/res/layout/single_pane_layout.xml
+++ b/packages/DocumentsUI/res/layout/single_pane_layout.xml
@@ -43,6 +43,8 @@
android:id="@+id/stack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:popupTheme="?actionBarPopupTheme"
+ android:background="@android:color/transparent"
android:layout_marginStart="4dp"
android:overlapAnchor="true" />
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index 79f6fa9..2aee569 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -66,7 +66,7 @@
android:id="@+id/menu_sort"
android:title="@string/menu_sort"
android:icon="@drawable/ic_menu_sortby"
- android:showAsAction="never">
+ android:showAsAction="ifRoom">
<menu>
<item
android:id="@+id/menu_sort_name"
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index 8846b62..6fff804 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sommige lêers is omgeskakel"</string>
<string name="allow" msgid="7225948811296386551">"Laat toe"</string>
<string name="deny" msgid="2081879885755434506">"Weier"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Vee lêers uit?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Is jy seker jy wil <xliff:g id="COUNT_1">%1$d</xliff:g> lêers uitvee?</item>
- <item quantity="one">Is jy seker jy wil <xliff:g id="COUNT_0">%1$d</xliff:g> lêer uitvee?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Vee <xliff:g id="COUNT_1">%1$d</xliff:g> lêers uit?</item>
+ <item quantity="one">Vee <xliff:g id="COUNT_0">%1$d</xliff:g> lêer uit?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> gekies</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> gekies</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index 6f975da..c61db57 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"አንዳንድ ፋይሎች ተለውጠዋል"</string>
<string name="allow" msgid="7225948811296386551">"ይፍቀዱ"</string>
<string name="deny" msgid="2081879885755434506">"ያስተባብሉ"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ፋይሎች ይሰረዙ?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">እርግጠኛ ነዎት <xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎችን መሰረዝ ይፈልጋሉ?</item>
- <item quantity="other">እርግጠኛ ነዎት <xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎችን መሰረዝ ይፈልጋሉ?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎች ይሰረዙ?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ፋይሎች ይሰረዙ?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጠዋል</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ተመርጠዋል</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index 79c2c6a..dcfef2a 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -140,13 +140,20 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"تم تحويل بعض الملفات"</string>
<string name="allow" msgid="7225948811296386551">"السماح"</string>
<string name="deny" msgid="2081879885755434506">"رفض"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"هل تريد حذف الملفات؟"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="zero">هل تريد بالتأكيد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملف؟</item>
- <item quantity="two">هل تريد بالتأكيد حذف ملفين (<xliff:g id="COUNT_1">%1$d</xliff:g>)؟</item>
- <item quantity="few">هل تريد بالتأكيد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملفات؟</item>
- <item quantity="many">هل تريد بالتأكيد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملفًا؟</item>
- <item quantity="other">هل تريد بالتأكيد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملف؟</item>
- <item quantity="one">هل تريد بالتأكيد حذف <xliff:g id="COUNT_0">%1$d</xliff:g> ملف؟</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="zero">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملف؟</item>
+ <item quantity="two">هل تريد حذف ملفين (<xliff:g id="COUNT_1">%1$d</xliff:g>)؟</item>
+ <item quantity="few">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملفات؟</item>
+ <item quantity="many">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملفًا؟</item>
+ <item quantity="other">هل تريد حذف <xliff:g id="COUNT_1">%1$d</xliff:g> ملف؟</item>
+ <item quantity="one">هل تريد حذف <xliff:g id="COUNT_0">%1$d</xliff:g> ملف؟</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="zero">تم تحديد <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="two">تم تحديد <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="few">تم تحديد <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="many">تم تحديد <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="other">تم تحديد <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">تم تحديد <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index 8ba9036..e1505f8 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Bəzi fayllar konvertasiya edilib"</string>
<string name="allow" msgid="7225948811296386551">"İcazə verin"</string>
<string name="deny" msgid="2081879885755434506">"Rədd et"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Fayllar silinsin?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> faylı silmək istədiyinizə əminsiniz?</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> faylı silmək istədiyinizə əminsiniz?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> fayl silinsin?</item>
+ <item quantity="one"> <xliff:g id="COUNT_0">%1$d</xliff:g> fayl silinsin?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> seçilib</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> seçilib</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
index ea4485b..b884a19 100644
--- a/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
@@ -119,10 +119,14 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Neke datoteke su konvertovane"</string>
<string name="allow" msgid="7225948811296386551">"Dozvoli"</string>
<string name="deny" msgid="2081879885755434506">"Odbij"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Želite li da izbrišete datoteke?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Želite li stvarno da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> datoteku?</item>
- <item quantity="few">Želite li stvarno da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke?</item>
- <item quantity="other">Želite li stvarno da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> datoteku?</item>
+ <item quantity="few">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke?</item>
+ <item quantity="other">Želite li da izbrišete <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one">Izabrana je <xliff:g id="COUNT_1">%1$d</xliff:g> stavka</item>
+ <item quantity="few">Izabrane su <xliff:g id="COUNT_1">%1$d</xliff:g> stavke</item>
+ <item quantity="other">Izabrano je <xliff:g id="COUNT_1">%1$d</xliff:g> stavki</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index 66af887..94c34bd 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Някои файлове бяха преобразувани"</string>
<string name="allow" msgid="7225948811296386551">"Разрешаване"</string>
<string name="deny" msgid="2081879885755434506">"Отказване"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Да се изтрият ли файловете?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Наистина ли искате да изтриете <xliff:g id="COUNT_1">%1$d</xliff:g> файла?</item>
- <item quantity="one">Наистина ли искате да изтриете <xliff:g id="COUNT_0">%1$d</xliff:g> файл?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Искате ли да изтриете <xliff:g id="COUNT_1">%1$d</xliff:g> файла?</item>
+ <item quantity="one">Искате ли да изтриете <xliff:g id="COUNT_0">%1$d</xliff:g> файл?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">Избрахте <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">Избрахте <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 6bbe95b..2159044 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"কিছু ফাইল রূপান্তরিত হয়েছে"</string>
<string name="allow" msgid="7225948811296386551">"অনুমতি দিন"</string>
<string name="deny" msgid="2081879885755434506">"আস্বীকার করুন"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ফাইলগুলি মুছবেন?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">আপনি কি <xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল মোছার বিষয়ে নিশ্চিত?</item>
- <item quantity="other">আপনি কি <xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল মোছার বিষয়ে নিশ্চিত?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল মুছবেন?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি ফাইল মুছবেন?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>টি নির্বাচন করা হয়েছে</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>টি নির্বাচন করা হয়েছে</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-bs-rBA/strings.xml b/packages/DocumentsUI/res/values-bs-rBA/strings.xml
index 9f5f932..f366ce8 100644
--- a/packages/DocumentsUI/res/values-bs-rBA/strings.xml
+++ b/packages/DocumentsUI/res/values-bs-rBA/strings.xml
@@ -119,10 +119,14 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Neke od datoteka su pretvorene"</string>
<string name="allow" msgid="7225948811296386551">"Dozvoli"</string>
<string name="deny" msgid="2081879885755434506">"Odbijte"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Zaista želite obrisati fajlove?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one"> Jeste li sigurni da želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> fajl?</item>
- <item quantity="few"> Jeste li sigurni da želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> fajla?</item>
- <item quantity="other"> Jeste li sigurni da želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> fajlova?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> fajl.</item>
+ <item quantity="few">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> fajla.</item>
+ <item quantity="other">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> fajlova.</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> stavka je odabrana</item>
+ <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> stavke su odabrane</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> stavki je odabrano</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index 2b136f1..5af86b1 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"S\'han convertit alguns fitxers"</string>
<string name="allow" msgid="7225948811296386551">"Permet"</string>
<string name="deny" msgid="2081879885755434506">"Denega"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Vols suprimir els fitxers?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Confirmes que vols suprimir <xliff:g id="COUNT_1">%1$d</xliff:g> fitxers?</item>
- <item quantity="one">Confirmes que vols suprimir <xliff:g id="COUNT_0">%1$d</xliff:g> fitxer?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Vols suprimir <xliff:g id="COUNT_1">%1$d</xliff:g> fitxers?</item>
+ <item quantity="one">Vols suprimir <xliff:g id="COUNT_0">%1$d</xliff:g> fitxer?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> elements seleccionats</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> element seleccionat</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index 7196c6a..4e8b440 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -126,11 +126,16 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Některé soubory byly převedeny"</string>
<string name="allow" msgid="7225948811296386551">"Povolit"</string>
<string name="deny" msgid="2081879885755434506">"Odepřít"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Smazat soubory?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="few">Opravdu chcete smazat <xliff:g id="COUNT_1">%1$d</xliff:g> soubory?</item>
- <item quantity="many">Opravdu chcete smazat <xliff:g id="COUNT_1">%1$d</xliff:g> souboru?</item>
- <item quantity="other">Opravdu chcete smazat <xliff:g id="COUNT_1">%1$d</xliff:g> souborů?</item>
- <item quantity="one">Opravdu chcete smazat <xliff:g id="COUNT_0">%1$d</xliff:g> soubor?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="few">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> soubory?</item>
+ <item quantity="many">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> souboru?</item>
+ <item quantity="other">Smazat <xliff:g id="COUNT_1">%1$d</xliff:g> souborů?</item>
+ <item quantity="one">Smazat <xliff:g id="COUNT_0">%1$d</xliff:g> soubor?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="few">Vybrány <xliff:g id="COUNT_1">%1$d</xliff:g> položky</item>
+ <item quantity="many">Vybráno <xliff:g id="COUNT_1">%1$d</xliff:g> položky</item>
+ <item quantity="other">Vybráno <xliff:g id="COUNT_1">%1$d</xliff:g> položek</item>
+ <item quantity="one">Vybrána <xliff:g id="COUNT_0">%1$d</xliff:g> položka</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index ab9271c..ee5ba49 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Nogle filer er konverteret"</string>
<string name="allow" msgid="7225948811296386551">"Tillad"</string>
<string name="deny" msgid="2081879885755434506">"Afvis"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Vil du slette filerne?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Er du sikker på, at du vil slette <xliff:g id="COUNT_1">%1$d</xliff:g> fil?</item>
- <item quantity="other">Er du sikker på, at du vil slette <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
+ <item quantity="other">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one">Der er valgt <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="other">Der er valgt <xliff:g id="COUNT_1">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 8c06c7f..a42e955 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Einige Dateien wurden konvertiert"</string>
<string name="allow" msgid="7225948811296386551">"Zulassen"</string>
<string name="deny" msgid="2081879885755434506">"Ablehnen"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Dateien löschen?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Möchtest du wirklich <xliff:g id="COUNT_1">%1$d</xliff:g> Dateien löschen?</item>
- <item quantity="one">Möchtest du wirklich <xliff:g id="COUNT_0">%1$d</xliff:g> Datei löschen?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Möchtest du <xliff:g id="COUNT_1">%1$d</xliff:g> Dateien löschen?</item>
+ <item quantity="one">Möchtest du <xliff:g id="COUNT_0">%1$d</xliff:g> Datei löschen?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ausgewählt</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ausgewählt</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index 1752a29..e6839de 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Ορισμένα αρχεία μετατράπηκαν"</string>
<string name="allow" msgid="7225948811296386551">"Να επιτρέπεται"</string>
<string name="deny" msgid="2081879885755434506">"Άρνηση"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Διαγραφή αρχείων;"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Είστε βέβαιοι ότι θέλετε να διαγράψετε <xliff:g id="COUNT_1">%1$d</xliff:g> αρχεία;</item>
- <item quantity="one">Είστε βέβαιοι ότι θέλετε να διαγράψετε <xliff:g id="COUNT_0">%1$d</xliff:g> αρχείο;</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Να διαγραφούν <xliff:g id="COUNT_1">%1$d</xliff:g> αρχεία;</item>
+ <item quantity="one">Να διαγραφεί <xliff:g id="COUNT_0">%1$d</xliff:g> αρχείο;</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> επιλεγμένα</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> επιλεγμένο</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index 82c5557..8b46660 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="allow" msgid="7225948811296386551">"Allow"</string>
<string name="deny" msgid="2081879885755434506">"Deny"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Delete files?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Are you sure you want to delete <xliff:g id="COUNT_1">%1$d</xliff:g> files?</item>
- <item quantity="one">Are you sure you want to delete <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> files?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index 82c5557..8b46660 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="allow" msgid="7225948811296386551">"Allow"</string>
<string name="deny" msgid="2081879885755434506">"Deny"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Delete files?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Are you sure you want to delete <xliff:g id="COUNT_1">%1$d</xliff:g> files?</item>
- <item quantity="one">Are you sure you want to delete <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> files?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index 82c5557..8b46660 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Some files were converted"</string>
<string name="allow" msgid="7225948811296386551">"Allow"</string>
<string name="deny" msgid="2081879885755434506">"Deny"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Delete files?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Are you sure you want to delete <xliff:g id="COUNT_1">%1$d</xliff:g> files?</item>
- <item quantity="one">Are you sure you want to delete <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Delete <xliff:g id="COUNT_1">%1$d</xliff:g> files?</item>
+ <item quantity="one">Delete <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selected</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selected</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 1ba7c2d..8fd8381 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Se convirtieron algunos archivos"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Denegar"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"¿Quieres borrar los archivos?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">¿Confirmas que quieres borrar <xliff:g id="COUNT_1">%1$d</xliff:g> archivos?</item>
- <item quantity="one">¿Confirmas que quieres borrar <xliff:g id="COUNT_0">%1$d</xliff:g> archivo?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">¿Deseas borrar <xliff:g id="COUNT_1">%1$d</xliff:g> archivos?</item>
+ <item quantity="one">¿Deseas borrar <xliff:g id="COUNT_0">%1$d</xliff:g> archivo?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> elementos seleccionados</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> elemento seleccionado</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 0c1f2dd..81fc59abb 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Se han convertido algunos archivos"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Denegar"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"¿Eliminar archivos?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">¿Seguro que quieres eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> archivos?</item>
- <item quantity="one">¿Seguro que quieres eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> archivo?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">¿Eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> archivos?</item>
+ <item quantity="one">¿Eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> archivo?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> seleccionados</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> seleccionado</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index 13a4bfa..7cf134e 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Mõned failid teisendati"</string>
<string name="allow" msgid="7225948811296386551">"Luba"</string>
<string name="deny" msgid="2081879885755434506">"Keela"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Kas kustutada failid?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Kas soovite kindlasti <xliff:g id="COUNT_1">%1$d</xliff:g> faili kustutada?</item>
- <item quantity="one">Kas soovite kindlasti <xliff:g id="COUNT_0">%1$d</xliff:g> faili kustutada?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Kas kustutada <xliff:g id="COUNT_1">%1$d</xliff:g> faili?</item>
+ <item quantity="one">Kas kustutada <xliff:g id="COUNT_0">%1$d</xliff:g> fail?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> on valitud</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> on valitud</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index c66bae44..23e4079 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Artxibo batzuk bihurtu dira"</string>
<string name="allow" msgid="7225948811296386551">"Onartu"</string>
<string name="deny" msgid="2081879885755434506">"Ukatu"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Fitxategiak ezabatu nahi dituzu?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Ziur <xliff:g id="COUNT_1">%1$d</xliff:g> fitxategi ezabatu nahi dituzula?</item>
- <item quantity="one">Ziur <xliff:g id="COUNT_0">%1$d</xliff:g> fitxategi ezabatu nahi duzula?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> fitxategi ezabatu nahi dituzu?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> fitxategi ezabatu nahi duzu?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> hautatuta</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> hautatuta</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index 997eea8..6aa5626 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"بعضی از فایلها تبدیل شدند"</string>
<string name="allow" msgid="7225948811296386551">"ارزیابیشده"</string>
<string name="deny" msgid="2081879885755434506">"اجازه ندارد"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"فایلها حذف شوند؟"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">مطمئنید میخواهید <xliff:g id="COUNT_1">%1$d</xliff:g> فایل را حذف کنید؟</item>
- <item quantity="other">مطمئنید میخواهید <xliff:g id="COUNT_1">%1$d</xliff:g> فایل را حذف کنید؟</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> فایل حذف شود؟</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> فایل حذف شود؟</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> مورد انتخاب شد</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> مورد انتخاب شد</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index 1dfbe15..8be6d61 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Joitakin tiedostoja muunnettiin."</string>
<string name="allow" msgid="7225948811296386551">"Salli"</string>
<string name="deny" msgid="2081879885755434506">"Kiellä"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Poistetaanko tiedostot?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Haluatko varmasti poistaa <xliff:g id="COUNT_1">%1$d</xliff:g> tiedostoa?</item>
- <item quantity="one">Haluatko varmasti poistaa <xliff:g id="COUNT_0">%1$d</xliff:g> tiedoston?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Poistetaanko <xliff:g id="COUNT_1">%1$d</xliff:g> tiedostoa?</item>
+ <item quantity="one">Poistetaanko <xliff:g id="COUNT_0">%1$d</xliff:g> tiedosto?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valittu</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> valittu</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 08dacac..0a33570 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Certains fichiers ont été convertis"</string>
<string name="allow" msgid="7225948811296386551">"Autoriser"</string>
<string name="deny" msgid="2081879885755434506">"Refuser"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Supprimer les fichiers?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Voulez-vous vraiment supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichier?</item>
- <item quantity="other">Voulez-vous vraiment supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichier?</item>
+ <item quantity="other">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 6378191..478ea46 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Certains fichiers ont été convertis"</string>
<string name="allow" msgid="7225948811296386551">"Autoriser"</string>
<string name="deny" msgid="2081879885755434506">"Refuser"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Supprimer les fichiers ?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Voulez-vous vraiment supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichier ?</item>
- <item quantity="other">Voulez-vous vraiment supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers ?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichier ?</item>
+ <item quantity="other">Supprimer <xliff:g id="COUNT_1">%1$d</xliff:g> fichiers ?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> élément sélectionné</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> éléments sélectionnés</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index 79cc70f..c933faa 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Convertéronse algúns ficheiros"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Rexeitar"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Queres eliminar os ficheiros?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Seguro que queres eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros?</item>
- <item quantity="one">Seguro que queres eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Queres eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros?</item>
+ <item quantity="one">Queres eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">Seleccionáronse <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">Seleccionouse <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 11519a1..e9fda19 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -67,7 +67,7 @@
<string name="share_via" msgid="8966594246261344259">"આના દ્વારા શેર કરો"</string>
<string name="copy_notification_title" msgid="6374299806748219777">"ફાઇલો કૉપિ કરી રહ્યાં છે"</string>
<string name="move_notification_title" msgid="6193835179777284805">"ફાઇલો ખસેડી રહ્યાં છે"</string>
- <string name="delete_notification_title" msgid="3329403967712437496">"ફાઇલોને કાઢી નાખી રહ્યાં છે"</string>
+ <string name="delete_notification_title" msgid="3329403967712437496">"ફાઇલને નીકાળી રહ્યાં છે"</string>
<string name="copy_remaining" msgid="6283790937387975095">"<xliff:g id="DURATION">%s</xliff:g> બાકી"</string>
<plurals name="copy_begin" formatted="false" msgid="9071199452634086365">
<item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલો કૉપિ કરી રહ્યાં છે.</item>
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"કેટલીક ફાઇલો રૂપાંતરિત કરી હતી"</string>
<string name="allow" msgid="7225948811296386551">"મંજૂરી આપો"</string>
<string name="deny" msgid="2081879885755434506">"નકારો"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ફાઇલોને કાઢી નાખીએ?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">શું તમે ખરેખર <xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલને કાઢી નાખવા માગો છો?</item>
- <item quantity="other">શું તમે ખરેખર <xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલને કાઢી નાખવા માગો છો?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલ કાઢી નાખીએ?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ફાઇલ કાઢી નાખીએ?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> પસંદ કરી</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> પસંદ કરી</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index dcb7805..489bb98 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"कुछ फ़ाइलें रूपांतरित हो गई थीं"</string>
<string name="allow" msgid="7225948811296386551">"अनुमति दें"</string>
<string name="deny" msgid="2081879885755434506">"अस्वीकारें"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"फ़ाइलें हटाएं?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">क्या आप वाकई <xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें हटाना चाहते हैं?</item>
- <item quantity="other">क्या आप वाकई <xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें हटाना चाहते हैं?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें हटाएं?</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फ़ाइलें हटाएं?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> चयनित</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> चयनित</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 7d9b6f0..51b8673 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -119,10 +119,14 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Neke su datoteke konvertirane"</string>
<string name="allow" msgid="7225948811296386551">"Dopusti"</string>
<string name="deny" msgid="2081879885755434506">"Odbij"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Izbrisati datoteke?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Jeste li sigurni da želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteku?</item>
- <item quantity="few">Jeste li sigurni da želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke?</item>
- <item quantity="other">Jeste li sigurni da želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteku?</item>
+ <item quantity="few">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke?</item>
+ <item quantity="other">Želite li izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one">Odabrano: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="few">Odabrano: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="other">Odabrano: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index ac40f87..b7e74e0 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Egyes fájlokat konvertált a rendszer"</string>
<string name="allow" msgid="7225948811296386551">"Engedélyezés"</string>
<string name="deny" msgid="2081879885755434506">"Elutasítás"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Törli a fájlokat?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Biztosan töröl <xliff:g id="COUNT_1">%1$d</xliff:g> fájlt?</item>
- <item quantity="one">Biztosan töröl <xliff:g id="COUNT_0">%1$d</xliff:g> fájlt?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Töröl <xliff:g id="COUNT_1">%1$d</xliff:g> fájlt?</item>
+ <item quantity="one">Töröl <xliff:g id="COUNT_0">%1$d</xliff:g> fájlt?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> kiválasztva</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> kiválasztva</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 3598934..4dbae9d 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Որոշ ֆայլեր փոխարկվել են"</string>
<string name="allow" msgid="7225948811296386551">"Թույլատրել"</string>
<string name="deny" msgid="2081879885755434506">"Մերժել"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Ջնջե՞լ ֆայլերը:"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
<item quantity="one">Ջնջե՞լ <xliff:g id="COUNT_1">%1$d</xliff:g> ֆայլ:</item>
<item quantity="other">Ջնջե՞լ <xliff:g id="COUNT_1">%1$d</xliff:g> ֆայլ:</item>
</plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="other">Ընտրված է՝ <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index d87fad3..73ed8bc 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Beberapa file dikonversi"</string>
<string name="allow" msgid="7225948811296386551">"Izinkan"</string>
<string name="deny" msgid="2081879885755434506">"Tolak"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Hapus file?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Yakin ingin menghapus <xliff:g id="COUNT_1">%1$d</xliff:g> file?</item>
- <item quantity="one">Yakin ingin menghapus <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Hapus <xliff:g id="COUNT_1">%1$d</xliff:g> file?</item>
+ <item quantity="one">Hapus <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dipilih</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dipilih</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index 5b2531d..75831ed 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sumum skrám var umbreytt"</string>
<string name="allow" msgid="7225948811296386551">"Leyfa"</string>
<string name="deny" msgid="2081879885755434506">"Hafna"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Eyða skrám?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Ertu viss um að þú viljir eyða <xliff:g id="COUNT_1">%1$d</xliff:g> skrá?</item>
- <item quantity="other">Ertu viss um að þú viljir eyða <xliff:g id="COUNT_1">%1$d</xliff:g> skrám?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Eyða <xliff:g id="COUNT_1">%1$d</xliff:g> skrá?</item>
+ <item quantity="other">Eyða <xliff:g id="COUNT_1">%1$d</xliff:g> skrám?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> valið</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> valin</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index 4785ffc..cfd5611 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alcuni file sono stati convertiti"</string>
<string name="allow" msgid="7225948811296386551">"Consenti"</string>
<string name="deny" msgid="2081879885755434506">"Nega"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Eliminare i file?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Vuoi eliminare <xliff:g id="COUNT_1">%1$d</xliff:g> file?</item>
- <item quantity="one">Vuoi eliminare <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Eliminare <xliff:g id="COUNT_1">%1$d</xliff:g> file?</item>
+ <item quantity="one">Eliminare <xliff:g id="COUNT_0">%1$d</xliff:g> file?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> elementi selezionati</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> elemento selezionato</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index 1fb81dd..7d4cb9e9 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -126,11 +126,16 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"קבצים מסוימים הומרו"</string>
<string name="allow" msgid="7225948811296386551">"אפשר"</string>
<string name="deny" msgid="2081879885755434506">"דחה"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"האם למחוק את הקבצים?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="two">האם אתה בטוח שברצונך למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים?</item>
- <item quantity="many">האם אתה בטוח שברצונך למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים?</item>
- <item quantity="other">האם אתה בטוח שברצונך למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים?</item>
- <item quantity="one">האם אתה בטוח שברצונך למחוק קובץ <xliff:g id="COUNT_0">%1$d</xliff:g>?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="two">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים?</item>
+ <item quantity="many">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים?</item>
+ <item quantity="other">האם למחוק <xliff:g id="COUNT_1">%1$d</xliff:g> קבצים?</item>
+ <item quantity="one">האם למחוק קובץ <xliff:g id="COUNT_0">%1$d</xliff:g>?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="two"><xliff:g id="COUNT_1">%1$d</xliff:g> נבחרו</item>
+ <item quantity="many"><xliff:g id="COUNT_1">%1$d</xliff:g> נבחרו</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> נבחרו</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> נבחר</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 32bc414..9618d36 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"一部のファイルが変換されました"</string>
<string name="allow" msgid="7225948811296386551">"許可"</string>
<string name="deny" msgid="2081879885755434506">"拒否"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ファイルを削除しますか?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> 個のファイルを削除してもよろしいですか?</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> 個のファイルを削除してもよろしいですか?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> 個のファイルを削除しますか?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> 個のファイルを削除しますか?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> 個を選択中</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> 個を選択中</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index ce32f23..ac8d267 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ზოგიერთი ფაილი გარდაქმნილია"</string>
<string name="allow" msgid="7225948811296386551">"უფლების მიცემა"</string>
<string name="deny" msgid="2081879885755434506">"აკრძალვა"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"გსურთ ფაილების წაშლა?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">ნამდვილად გსურთ <xliff:g id="COUNT_1">%1$d</xliff:g> ფაილის წაშლა?</item>
- <item quantity="one">ნამდვილად გსურთ <xliff:g id="COUNT_0">%1$d</xliff:g> ფაილის წაშლა?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">გსურთ <xliff:g id="COUNT_1">%1$d</xliff:g> ფაილის წაშლა?</item>
+ <item quantity="one">გსურთ <xliff:g id="COUNT_0">%1$d</xliff:g> ფაილის წაშლა?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">არჩეულია <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">არჩეულია <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index ad10920..759506b 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Кейбір файлдар түрлендірілді"</string>
<string name="allow" msgid="7225948811296386551">"Рұқсат беру"</string>
<string name="deny" msgid="2081879885755434506">"Бас тарту"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Файлдарды жою керек пе?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
<item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> файлды жою керек пе?</item>
<item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> файлды жою керек пе?</item>
</plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> таңдалды</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> таңдалды</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index a084f81..d83c1d3 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ឯកសារមួយចំនួនត្រូវបានបម្លែង"</string>
<string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
<string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"លុបឯកសារឬទេ?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">តើអ្នកប្រាកដថាអ្នកចង់លុប <xliff:g id="COUNT_1">%1$d</xliff:g> ឯកសារឬទេ?</item>
- <item quantity="one">តើអ្នកប្រាកដថាអ្នកចង់លុប <xliff:g id="COUNT_0">%1$d</xliff:g> ឯកសារឬទេ?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">លុបឯកសារ <xliff:g id="COUNT_1">%1$d</xliff:g> ច្បាប់ឬ?</item>
+ <item quantity="one">លុបឯកសារ <xliff:g id="COUNT_0">%1$d</xliff:g> ច្បាប់ឬ?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">បានជ្រើស <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">បានជ្រើស <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index 55c9697..57ddd0b 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ಕೆಲವು ಫೈಲ್ಗಳನ್ನು ಪರಿವರ್ತಿಸಲಾಗಿದೆ"</string>
<string name="allow" msgid="7225948811296386551">"ಅನುಮತಿಸು"</string>
<string name="deny" msgid="2081879885755434506">"ನಿರಾಕರಿಸು"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ಫೈಲ್ಗಳನ್ನು ಅಳಿಸುವುದೇ?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ?</item>
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಅಳಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one"> <xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಅಳಿಸುವುದೇ?</item>
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> ಫೈಲ್ಗಳನ್ನು ಅಳಿಸುವುದೇ?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index 37642d1..907802d 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"일부 파일이 변환되었습니다."</string>
<string name="allow" msgid="7225948811296386551">"허용"</string>
<string name="deny" msgid="2081879885755434506">"거부"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"파일을 삭제하시겠습니까?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
<item quantity="other">파일 <xliff:g id="COUNT_1">%1$d</xliff:g>개를 삭제하시겠습니까?</item>
<item quantity="one">파일 <xliff:g id="COUNT_0">%1$d</xliff:g>개를 삭제하시겠습니까?</item>
</plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>개 선택됨</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g>개 선택됨</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index 134b5b8..699e76a 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Айрым файлдардын форматы өзгөртүлдү"</string>
<string name="allow" msgid="7225948811296386551">"Уруксат берүү"</string>
<string name="deny" msgid="2081879885755434506">"Жок"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Файлдар жок кылынсынбы?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Бул <xliff:g id="COUNT_1">%1$d</xliff:g> файлды чын эле жок кылгыңыз келеби?</item>
- <item quantity="one">Бул <xliff:g id="COUNT_0">%1$d</xliff:g> файлды чын эле жок кылгыңыз келеби?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> файл жок кылынсынбы?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> файл жок кылынсынбы?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> тандалды</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> тандалды</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index edfca5c..468853b 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ປ່ຽນແປງບາງໄຟລ໌ແລ້ວ"</string>
<string name="allow" msgid="7225948811296386551">"ອະນຸຍາດ"</string>
<string name="deny" msgid="2081879885755434506">"ປະຕິເສດ"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ລຶບໄຟລ໌ອອກບໍ?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບ <xliff:g id="COUNT_1">%1$d</xliff:g> ໄຟລ໌?</item>
- <item quantity="one">ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບ <xliff:g id="COUNT_0">%1$d</xliff:g> ໄຟລ໌?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">ລຶບ <xliff:g id="COUNT_1">%1$d</xliff:g> ໄຟລ໌ອອກບໍ?</item>
+ <item quantity="one">ລຶບ <xliff:g id="COUNT_0">%1$d</xliff:g> ໄຟລ໌ອອກບໍ?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">ເລືອກ <xliff:g id="COUNT_1">%1$d</xliff:g> ແລ້ວ</item>
+ <item quantity="one">ເລືອກ <xliff:g id="COUNT_0">%1$d</xliff:g> ແລ້ວ</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 194e5900..a6297be 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -126,11 +126,16 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Kai kurie failai buvo konvertuoti"</string>
<string name="allow" msgid="7225948811296386551">"Leisti"</string>
<string name="deny" msgid="2081879885755434506">"Atmesti"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Ištrinti failus?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">]Ar tikrai norite ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failą?</item>
- <item quantity="few">]Ar tikrai norite ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failus?</item>
- <item quantity="many">]Ar tikrai norite ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failo?</item>
- <item quantity="other">]Ar tikrai norite ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failų?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failą?</item>
+ <item quantity="few">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failus?</item>
+ <item quantity="many">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failo?</item>
+ <item quantity="other">Ištrinti <xliff:g id="COUNT_1">%1$d</xliff:g> failų?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one">Pasirinktas <xliff:g id="COUNT_1">%1$d</xliff:g> elementas</item>
+ <item quantity="few">Pasirinkti <xliff:g id="COUNT_1">%1$d</xliff:g> elementai</item>
+ <item quantity="many">Pasirinkta <xliff:g id="COUNT_1">%1$d</xliff:g> elemento</item>
+ <item quantity="other">Pasirinkta <xliff:g id="COUNT_1">%1$d</xliff:g> elementų</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index cd4cb5e..1a9b77c0 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -119,10 +119,14 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Daži faili tika pārveidoti."</string>
<string name="allow" msgid="7225948811296386551">"Atļaut"</string>
<string name="deny" msgid="2081879885755434506">"Noraidīt"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Vai dzēst failus?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="zero">Vai tiešām vēlaties dzēst <xliff:g id="COUNT_1">%1$d</xliff:g> failus?</item>
- <item quantity="one">Vai tiešām vēlaties dzēst <xliff:g id="COUNT_1">%1$d</xliff:g> failu?</item>
- <item quantity="other">Vai tiešām vēlaties dzēst <xliff:g id="COUNT_1">%1$d</xliff:g> failus?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="zero">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> failus?</item>
+ <item quantity="one">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> failu?</item>
+ <item quantity="other">Vai izdzēst <xliff:g id="COUNT_1">%1$d</xliff:g> failus?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="zero"><xliff:g id="COUNT_1">%1$d</xliff:g> atlasīti</item>
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> atlasīts</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> atlasīti</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index 129119b..5bbf8c5 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -67,7 +67,7 @@
<string name="share_via" msgid="8966594246261344259">"Сподели преку"</string>
<string name="copy_notification_title" msgid="6374299806748219777">"Се копираат датотеки"</string>
<string name="move_notification_title" msgid="6193835179777284805">"Датотеките се преместуваат"</string>
- <string name="delete_notification_title" msgid="3329403967712437496">"Се бришат датотеки"</string>
+ <string name="delete_notification_title" msgid="3329403967712437496">"Се бришат датотеките"</string>
<string name="copy_remaining" msgid="6283790937387975095">"Уште <xliff:g id="DURATION">%s</xliff:g>"</string>
<plurals name="copy_begin" formatted="false" msgid="9071199452634086365">
<item quantity="one">Се копира <xliff:g id="COUNT_1">%1$d</xliff:g> датотека.</item>
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Некои датотеки беа конвертирани"</string>
<string name="allow" msgid="7225948811296386551">"Дозволи"</string>
<string name="deny" msgid="2081879885755434506">"Одбиј"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Избриши датотеки?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Дали сте сигурни дека сакате да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотека?</item>
- <item quantity="other">Дали сте сигурни дека сакате да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотеки?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Да се избрише <xliff:g id="COUNT_1">%1$d</xliff:g> датотека?</item>
+ <item quantity="other">Да се избришат <xliff:g id="COUNT_1">%1$d</xliff:g> датотеки?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> е избрана</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> се избрани</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index 966ddbb..264d196 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ചില ഫയലുകൾ പരിവർത്തനം ചെയ്യപ്പെട്ടു"</string>
<string name="allow" msgid="7225948811296386551">"അനുവദിക്കുക"</string>
<string name="deny" msgid="2081879885755434506">"നിരസിക്കുക"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ഫയലുകൾ ഇല്ലാതാക്കണോ?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ഫയലുകൾ ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് തീർച്ചയാണോ?</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ഫയൽ ഇല്ലാതാക്കണമെന്ന് നിങ്ങൾക്ക് തീർച്ചയാണോ?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ഫയലുകൾ ഇല്ലാതാക്കണോ?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ഫയൽ ഇല്ലാതാക്കണോ?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> തിരഞ്ഞെടുത്തു</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> തിരഞ്ഞെടുത്തു</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index a2617f9..02c818b 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Зарим файлыг хөрвүүлсэн"</string>
<string name="allow" msgid="7225948811296386551">"Зөвшөөрөх"</string>
<string name="deny" msgid="2081879885755434506">"Татгалзах"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Эдгээр файлыг устгах уу?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Та <xliff:g id="COUNT_1">%1$d</xliff:g> файлыг устгахдаа итгэлтэй байна уу?</item>
- <item quantity="one">Та <xliff:g id="COUNT_0">%1$d</xliff:g> файлыг устгахдаа итгэлтэй байна уу?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> файлыг устгах уу?</item>
+ <item quantity="one"> <xliff:g id="COUNT_0">%1$d</xliff:g> файлыг устгах уу?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> сонгосон</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> сонгосон</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index c44b481..6fae578 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"काही फायली रूपांतरित केल्या होत्या"</string>
<string name="allow" msgid="7225948811296386551">"अनुमती द्या"</string>
<string name="deny" msgid="2081879885755434506">"नकार द्या"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"फायली हटवायच्या?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">आपल्याला खात्री आहे की आपण <xliff:g id="COUNT_1">%1$d</xliff:g> फाईल हटवू इच्छिता?</item>
- <item quantity="other">आपल्याला खात्री आहे की आपण <xliff:g id="COUNT_1">%1$d</xliff:g> फायली हटवू इच्छिता?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one"> <xliff:g id="COUNT_1">%1$d</xliff:g> फाईल हटवायची?</item>
+ <item quantity="other"> <xliff:g id="COUNT_1">%1$d</xliff:g> फायली हटवायच्या?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> निवडला</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> निवडले</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 17cd348..6f7c525 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sesetengah fail telah ditukarkan"</string>
<string name="allow" msgid="7225948811296386551">"Benarkan"</string>
<string name="deny" msgid="2081879885755434506">"Nafi"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Padamkan fail?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Adakah anda pasti mahu memadamkan <xliff:g id="COUNT_1">%1$d</xliff:g> fail?</item>
- <item quantity="one">Adakah anda pasti mahu memadamkan <xliff:g id="COUNT_0">%1$d</xliff:g> fail?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Padamkan <xliff:g id="COUNT_1">%1$d</xliff:g> fail?</item>
+ <item quantity="one">Padamkan <xliff:g id="COUNT_0">%1$d</xliff:g> fail?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dipilih</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dipilih</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index 13392fc..9fb7f84 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"အချို့ဖိုင်များကို ပြောင်းလဲထားသည်"</string>
<string name="allow" msgid="7225948811296386551">"ခွင့်ပြုသည်"</string>
<string name="deny" msgid="2081879885755434506">"ငြင်းပယ်သည်"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ဖိုင်များကို ဖျက်မလား။"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ဖိုင်များကိုဖျက်ရန် သေချာပါသလား။</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ဖိုင်ကိုဖျက်ရန် သေချာပါသလား။</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">ဖိုင် <xliff:g id="COUNT_1">%1$d</xliff:g> ခုကိုဖျက်မလား။</item>
+ <item quantity="one">ဖိုင် <xliff:g id="COUNT_0">%1$d</xliff:g> ခုကိုဖျက်မလား။</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ခုရွေးချယ်ထားသည်</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ခုရွေးချယ်ထားသည်</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index d42c98e..0e48f0f 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Noen filer er konvertert"</string>
<string name="allow" msgid="7225948811296386551">"Tillat"</string>
<string name="deny" msgid="2081879885755434506">"Avslå"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Vil du slette filene?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Er du sikker på at du vil slette <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
- <item quantity="one">Er du sikker på at du vil slette <xliff:g id="COUNT_0">%1$d</xliff:g> fil?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Vil du slette <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
+ <item quantity="one">Vil du slette <xliff:g id="COUNT_0">%1$d</xliff:g> fil?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> er valgt</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> er valgt</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index d0bc737..cc70c91 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"केही फाइलहरू परिवर्तन गरिएका थिए"</string>
<string name="allow" msgid="7225948811296386551">"अनुमति दिनुहोस्"</string>
<string name="deny" msgid="2081879885755434506">"अस्वीकार गर्नुहोस्"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"फाइलहरूलाई मेट्ने हो?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">तपाईँ निश्चित रूपमा <xliff:g id="COUNT_1">%1$d</xliff:g> फाइलहरूलाई मेट्न चाहनुहुन्छ?</item>
- <item quantity="one">तपाईँ निश्चित रूपमा <xliff:g id="COUNT_0">%1$d</xliff:g> फाइललाई मेट्न चाहनुहुन्छ?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> फाइलहरूलाई मेट्ने हो?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> फाइललाई मेट्ने हो?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> लाई चयन गरियो</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> लाई चयन गरियो</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index 257e7e4..ebddf54 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Sommige bestanden zijn geconverteerd"</string>
<string name="allow" msgid="7225948811296386551">"Toestaan"</string>
<string name="deny" msgid="2081879885755434506">"Weigeren"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Bestanden verwijderen?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Weet je zeker dat je <xliff:g id="COUNT_1">%1$d</xliff:g> bestanden wilt verwijderen?</item>
- <item quantity="one">Weet je zeker dat je <xliff:g id="COUNT_0">%1$d</xliff:g> bestand wilt verwijderen?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> bestanden verwijderen?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> bestand verwijderen?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> geselecteerd</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> geselecteerd</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index 7dca3cf..a8c65e7 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"ਕੁਝ ਫ਼ਾਈਲਾਂ ਤਬਦੀਲ ਕੀਤੀਆਂ ਗਈਆਂ ਸਨ"</string>
<string name="allow" msgid="7225948811296386551">"ਆਗਿਆ ਦਿਓ"</string>
<string name="deny" msgid="2081879885755434506">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ਕੀ ਫ਼ਾਈਲਾਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">ਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ \'ਤੇ <xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਨੂੰ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?</item>
- <item quantity="other">ਕੀ ਤੁਸੀਂ ਯਕੀਨੀ ਤੌਰ \'ਤੇ <xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਨੂੰ ਮਿਟਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">ਕੀ <xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?</item>
+ <item quantity="other">ਕੀ <xliff:g id="COUNT_1">%1$d</xliff:g> ਫ਼ਾਈਲਾਂ ਨੂੰ ਮਿਟਾਉਣਾ ਹੈ?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣੀ ਗਈ</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ਚੁਣੀਆਂ ਗਈਆਂ</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index d334194..e888fd7 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -126,11 +126,16 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Niektóre pliki zostały przekonwertowane"</string>
<string name="allow" msgid="7225948811296386551">"Zezwól"</string>
<string name="deny" msgid="2081879885755434506">"Odmów"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Usunąć pliki?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="few">Na pewno chcesz usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> pliki?</item>
- <item quantity="many">Na pewno chcesz usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> plików?</item>
- <item quantity="other">Na pewno chcesz usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> pliku?</item>
- <item quantity="one">Na pewno chcesz usunąć <xliff:g id="COUNT_0">%1$d</xliff:g> plik?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="few">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> pliki?</item>
+ <item quantity="many">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> plików?</item>
+ <item quantity="other">Usunąć <xliff:g id="COUNT_1">%1$d</xliff:g> pliku?</item>
+ <item quantity="one">Usunąć <xliff:g id="COUNT_0">%1$d</xliff:g> plik?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="few">Wybrano <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="many">Wybrano <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="other">Wybrano <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">Wybrano <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index cc55006..213e76a 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alguns arquivos foram convertidos"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Negar"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Excluir arquivos?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Tem certeza de que deseja excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
- <item quantity="other">Tem certeza de que deseja excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ <item quantity="other">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index 1e51019..2c74e67 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alguns ficheiros foram convertidos"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Recusar"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Pretende eliminar ficheiros?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Tem a certeza de que pretende eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros?</item>
- <item quantity="one">Tem a certeza de que pretende eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Pretende eliminar <xliff:g id="COUNT_1">%1$d</xliff:g> ficheiros?</item>
+ <item quantity="one">Pretende eliminar <xliff:g id="COUNT_0">%1$d</xliff:g> ficheiro?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selecionado</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index cc55006..213e76a 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Alguns arquivos foram convertidos"</string>
<string name="allow" msgid="7225948811296386551">"Permitir"</string>
<string name="deny" msgid="2081879885755434506">"Negar"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Excluir arquivos?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Tem certeza de que deseja excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
- <item quantity="other">Tem certeza de que deseja excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ <item quantity="other">Excluir <xliff:g id="COUNT_1">%1$d</xliff:g> arquivos?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index 9932bd8..e57d1ab 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -119,10 +119,14 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Unele fișiere au fost convertite"</string>
<string name="allow" msgid="7225948811296386551">"Permiteți"</string>
<string name="deny" msgid="2081879885755434506">"Refuzați"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Ștergeți fișierele?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="few">Sigur doriți să ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> fișiere?</item>
- <item quantity="other">Sigur doriți să ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> de fișiere?</item>
- <item quantity="one">Sigur doriți să ștergeți <xliff:g id="COUNT_0">%1$d</xliff:g> fișier?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="few">Ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> fișiere?</item>
+ <item quantity="other">Ștergeți <xliff:g id="COUNT_1">%1$d</xliff:g> de fișiere?</item>
+ <item quantity="one">Ștergeți <xliff:g id="COUNT_0">%1$d</xliff:g> fișier?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> selectate</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selectate</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selectat</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index ebddb27..9c0f031 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -126,11 +126,16 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Формат некоторых файлов изменен"</string>
<string name="allow" msgid="7225948811296386551">"Разрешить"</string>
<string name="deny" msgid="2081879885755434506">"Отклонить"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Удаление файлов"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
<item quantity="one">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> файл?</item>
<item quantity="few">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> файла?</item>
<item quantity="many">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> файлов?</item>
<item quantity="other">Удалить <xliff:g id="COUNT_1">%1$d</xliff:g> файла?</item>
</plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="few">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="many">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="other">Выбрано: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index ae85f32..a34aa88 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"සමහර ගොනු පරිවර්තනය කරන ලදී"</string>
<string name="allow" msgid="7225948811296386551">"අවසර දෙන්න"</string>
<string name="deny" msgid="2081879885755434506">"ප්රතික්ෂේප කරන්න"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ගොනු මකන්නද?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">ඔබට ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g>ක් මැකීමට අවශ්ය බව විශ්වාසද?</item>
- <item quantity="other">ඔබට ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g>ක් මැකීමට අවශ්ය බව විශ්වාසද?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g>ක් මකන්නද?</item>
+ <item quantity="other">ගොනු <xliff:g id="COUNT_1">%1$d</xliff:g>ක් මකන්නද?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g>ක් තෝරන ලදී</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g>ක් තෝරන ලදී</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 7b3c213..1133815 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -126,11 +126,16 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Niektoré súbory boli konvertované"</string>
<string name="allow" msgid="7225948811296386551">"Povoliť"</string>
<string name="deny" msgid="2081879885755434506">"Zamietnuť"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Odstrániť súbory?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="few">Naozaj chcete odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> súbory?</item>
- <item quantity="many">Naozaj chcete odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> súboru?</item>
- <item quantity="other">Naozaj chcete odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> súborov?</item>
- <item quantity="one">Naozaj chcete odstrániť <xliff:g id="COUNT_0">%1$d</xliff:g> súbor?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="few">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> súbory?</item>
+ <item quantity="many">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> súboru?</item>
+ <item quantity="other">Odstrániť <xliff:g id="COUNT_1">%1$d</xliff:g> súborov?</item>
+ <item quantity="one">Odstrániť <xliff:g id="COUNT_0">%1$d</xliff:g> súbor?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="few">Vybraté: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="many">Vybraté: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="other">Vybraté: <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">Vybraté: <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index afcdb6a..f2c691d 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -126,11 +126,16 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Nekatere datoteke so bile pretvorjene"</string>
<string name="allow" msgid="7225948811296386551">"Dovoli"</string>
<string name="deny" msgid="2081879885755434506">"Zavrni"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Ali želite izbrisati datoteke?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Ali res želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteko?</item>
- <item quantity="two">Ali res želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteki?</item>
- <item quantity="few">Ali res želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke?</item>
- <item quantity="other">Ali res želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datotek?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteko?</item>
+ <item quantity="two">Želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteki?</item>
+ <item quantity="few">Želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke?</item>
+ <item quantity="other">Želite izbrisati <xliff:g id="COUNT_1">%1$d</xliff:g> datotek?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> izbran</item>
+ <item quantity="two"><xliff:g id="COUNT_1">%1$d</xliff:g> izbrana</item>
+ <item quantity="few"><xliff:g id="COUNT_1">%1$d</xliff:g> izbrani</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> izbranih</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index 7934b16..80ae000 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Disa skedarë u konvertuan"</string>
<string name="allow" msgid="7225948811296386551">"Lejo"</string>
<string name="deny" msgid="2081879885755434506">"Moho"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Të fshihen skedarët?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Je i sigurt se dëshiron të fshish <xliff:g id="COUNT_1">%1$d</xliff:g> skedarë?</item>
- <item quantity="one">Je i sigurt se dëshiron të fshish <xliff:g id="COUNT_0">%1$d</xliff:g> skedar?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Të fshihen <xliff:g id="COUNT_1">%1$d</xliff:g> skedarë?</item>
+ <item quantity="one">Të fshihet <xliff:g id="COUNT_0">%1$d</xliff:g> skedar?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> të zgjedhur</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> i zgjedhur</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index 7575ff8..be96db6 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -119,10 +119,14 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Неке датотеке су конвертоване"</string>
<string name="allow" msgid="7225948811296386551">"Дозволи"</string>
<string name="deny" msgid="2081879885755434506">"Одбиј"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Желите ли да избришете датотеке?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Желите ли стварно да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотеку?</item>
- <item quantity="few">Желите ли стварно да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотеке?</item>
- <item quantity="other">Желите ли стварно да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотека?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотеку?</item>
+ <item quantity="few">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотеке?</item>
+ <item quantity="other">Желите ли да избришете <xliff:g id="COUNT_1">%1$d</xliff:g> датотека?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one">Изабрана је <xliff:g id="COUNT_1">%1$d</xliff:g> ставка</item>
+ <item quantity="few">Изабране су <xliff:g id="COUNT_1">%1$d</xliff:g> ставке</item>
+ <item quantity="other">Изабрано је <xliff:g id="COUNT_1">%1$d</xliff:g> ставки</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index 97e6ab0..7fd4be0 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Vissa filer konverterades"</string>
<string name="allow" msgid="7225948811296386551">"Tillåt"</string>
<string name="deny" msgid="2081879885755434506">"Neka"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Vill du ta bort filerna?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Vill du ta bort <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
- <item quantity="one">Vill du ta bort <xliff:g id="COUNT_0">%1$d</xliff:g> fil?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Radera <xliff:g id="COUNT_1">%1$d</xliff:g> filer?</item>
+ <item quantity="one">Radera <xliff:g id="COUNT_0">%1$d</xliff:g> fil?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> har valts</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> har valts</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index c50920b..2730ce9 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Baadhi ya faili zimebadilishwa muundo"</string>
<string name="allow" msgid="7225948811296386551">"Ruhusu"</string>
<string name="deny" msgid="2081879885755434506">"Kataza"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Ungependa kufuta faili?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Una uhakika kuwa ungependa kufuta faili <xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
- <item quantity="one">Una uhakika kuwa ungependa kufuta faili <xliff:g id="COUNT_0">%1$d</xliff:g>?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Ungependa kufuta faili <xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ <item quantity="one">Ungependa kufuta faili <xliff:g id="COUNT_0">%1$d</xliff:g>?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">Imechagua <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">Imechagua <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp-land/config.xml b/packages/DocumentsUI/res/values-sw720dp-land/config.xml
index 8d9526d..6893d7a 100644
--- a/packages/DocumentsUI/res/values-sw720dp-land/config.xml
+++ b/packages/DocumentsUI/res/values-sw720dp-land/config.xml
@@ -15,5 +15,4 @@
-->
<resources>
- <bool name="always_show_summary">true</bool>
</resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp-land/dimens.xml b/packages/DocumentsUI/res/values-sw720dp-land/dimens.xml
index fa11244..1b67ee5 100644
--- a/packages/DocumentsUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/DocumentsUI/res/values-sw720dp-land/dimens.xml
@@ -20,4 +20,5 @@
<dimen name="list_divider_inset">80dp</dimen>
+ <dimen name="max_drawer_width">320dp</dimen>
</resources>
diff --git a/packages/DocumentsUI/res/values-sw720dp/dimens.xml b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
index b5d1150e..982b204 100644
--- a/packages/DocumentsUI/res/values-sw720dp/dimens.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/dimens.xml
@@ -20,4 +20,5 @@
<dimen name="list_item_padding">24dp</dimen>
+ <dimen name="max_drawer_width">320dp</dimen>
</resources>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index 08dc5ba..881e05a 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"சில கோப்புகள் மாற்றப்பட்டன"</string>
<string name="allow" msgid="7225948811296386551">"அனுமதி"</string>
<string name="deny" msgid="2081879885755434506">"நிராகரி"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"கோப்புகளை நீக்கவா?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> கோப்புகளை நிச்சயமாக நீக்கவா?</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> கோப்பை நிச்சயமாக நீக்கவா?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> கோப்புகளை நீக்கவா?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> கோப்பை நீக்கவா?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> தேர்ந்தெடுக்கப்பட்டன</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> தேர்ந்தெடுக்கப்பட்டது</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index ad8c840..0043ddc 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"కొన్ని పైల్లు మార్చబడ్డాయి"</string>
<string name="allow" msgid="7225948811296386551">"అనుమతించండి"</string>
<string name="deny" msgid="2081879885755434506">"తిరస్కరించండి"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ఫైల్లను తొలగించాలా?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">మీరు ఖచ్చితంగా <xliff:g id="COUNT_1">%1$d</xliff:g> ఫైల్లను తొలగించాలనుకుంటున్నారా?</item>
- <item quantity="one">మీరు ఖచ్చితంగా <xliff:g id="COUNT_0">%1$d</xliff:g> ఫైల్ను తొలగించాలనుకుంటున్నారా?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ఫైల్లను తొలగించాలా?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఫైల్ను తొలగించాలా?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ఎంచుకోబడ్డాయి</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఎంచుకోబడింది</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 8881fcd..8b24210 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"แปลงบางไฟล์แล้ว"</string>
<string name="allow" msgid="7225948811296386551">"อนุญาต"</string>
<string name="deny" msgid="2081879885755434506">"ปฏิเสธ"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"ลบไฟล์เหล่านี้ไหม"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">คุณแน่ใจไหมว่าต้องการลบ <xliff:g id="COUNT_1">%1$d</xliff:g> ไฟล์</item>
- <item quantity="one">คุณแน่ใจไหมว่าต้องการลบ <xliff:g id="COUNT_0">%1$d</xliff:g> ไฟล์</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">ลบ <xliff:g id="COUNT_1">%1$d</xliff:g> ไฟล์ใช่ไหม</item>
+ <item quantity="one">ลบ <xliff:g id="COUNT_0">%1$d</xliff:g> ไฟล์ใช่ไหม</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">เลือกไว้ <xliff:g id="COUNT_1">%1$d</xliff:g> รายการ</item>
+ <item quantity="one">เลือกไว้ <xliff:g id="COUNT_0">%1$d</xliff:g> รายการ</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index 1639425..9849b85 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Na-convert ang ilang file"</string>
<string name="allow" msgid="7225948811296386551">"Payagan"</string>
<string name="deny" msgid="2081879885755434506">"Tanggihan"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"I-delete ang mga file?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Sigurado ka bang gusto mong i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> file?</item>
- <item quantity="other">Sigurado ka bang gusto mong i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> na file?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Gusto mo bang i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> file?</item>
+ <item quantity="other">Gusto mo bang i-delete ang <xliff:g id="COUNT_1">%1$d</xliff:g> na file?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> ang napili</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ang napili</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index 822a9ed..8df52d2 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Bazı dosyalar dönüştürüldü"</string>
<string name="allow" msgid="7225948811296386551">"İzin Ver"</string>
<string name="deny" msgid="2081879885755434506">"Reddet"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Dosyalar silinsin mi?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dosyayı silmek istediğinizden emin misiniz?</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dosyayı silmek istediğinizden emin misiniz?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> dosya silinsin mi?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> dosya silinsin mi?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> öğe seçildi</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> öğe seçildi</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index 1886ca3..d5a1fcd 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -126,11 +126,16 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Деякі файли конвертовано"</string>
<string name="allow" msgid="7225948811296386551">"Дозвол."</string>
<string name="deny" msgid="2081879885755434506">"Забор."</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Видалити файли?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
<item quantity="one">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> файл?</item>
<item quantity="few">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> файли?</item>
<item quantity="many">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> файлів?</item>
<item quantity="other">Видалити <xliff:g id="COUNT_1">%1$d</xliff:g> файлу?</item>
</plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="few">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="many">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="other">Вибрано <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 82d20e3..846555a 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"کچھ فائلوں کو تبدیل کیا گیا تھا"</string>
<string name="allow" msgid="7225948811296386551">"اجازت دیں"</string>
<string name="deny" msgid="2081879885755434506">"مسترد کریں"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"فائلوں کو حذف کریں؟"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">کیا آپ واقعی <xliff:g id="COUNT_1">%1$d</xliff:g> فائلیں حذف کرنا چاہتے ہیں؟</item>
- <item quantity="one">کیا آپ واقعی <xliff:g id="COUNT_0">%1$d</xliff:g> فائل حذف کرنا چاہتے ہیں؟</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> فائلیں حذف کریں؟</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> فائل حذف کریں؟</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> منتخب کردہ</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> منتخب کردہ</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index ce43fc2..0e1ac5a 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Bir nechta fayllar o‘girildi"</string>
<string name="allow" msgid="7225948811296386551">"Ruxsat berish"</string>
<string name="deny" msgid="2081879885755434506">"Rad qilish"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Fayllar o‘chirib tashlansinmi?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta fayl o‘chirib tashlansinmi?</item>
- <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta fayl o‘chirib tashlansinmi?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta fayl o‘chirilsinmi?</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta fayl o‘chirilsinmi?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> ta tanlandi</item>
+ <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ta tanlandi</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index 0abb389..c3c16f4 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Đã chuyển đổi một số tệp"</string>
<string name="allow" msgid="7225948811296386551">"Cho phép"</string>
<string name="deny" msgid="2081879885755434506">"Từ chối"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Xóa tệp?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">Bạn có chắc chắn muốn xóa <xliff:g id="COUNT_1">%1$d</xliff:g> tệp không?</item>
- <item quantity="one">Bạn có chắc chắn muốn xóa <xliff:g id="COUNT_0">%1$d</xliff:g> tệp không?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">Xóa <xliff:g id="COUNT_1">%1$d</xliff:g> tệp?</item>
+ <item quantity="one">Xóa <xliff:g id="COUNT_0">%1$d</xliff:g> tệp?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">Đã chọn <xliff:g id="COUNT_1">%1$d</xliff:g></item>
+ <item quantity="one">Đã chọn <xliff:g id="COUNT_0">%1$d</xliff:g></item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index 6e87c81..7f479ff 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"部分文件已转换成其他格式"</string>
<string name="allow" msgid="7225948811296386551">"允许"</string>
<string name="deny" msgid="2081879885755434506">"拒绝"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"要删除文件吗?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">您确定要删除 <xliff:g id="COUNT_1">%1$d</xliff:g> 个文件吗?</item>
- <item quantity="one">您确定要删除 <xliff:g id="COUNT_0">%1$d</xliff:g> 个文件吗?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">删除 <xliff:g id="COUNT_1">%1$d</xliff:g> 个文件?</item>
+ <item quantity="one">删除 <xliff:g id="COUNT_0">%1$d</xliff:g> 个文件?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">已选择 <xliff:g id="COUNT_1">%1$d</xliff:g> 项</item>
+ <item quantity="one">已选择 <xliff:g id="COUNT_0">%1$d</xliff:g> 项</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index 345bba4..f22e27e 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"部分檔案已轉換成其他格式"</string>
<string name="allow" msgid="7225948811296386551">"允許"</string>
<string name="deny" msgid="2081879885755434506">"拒絕"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"要刪除檔案嗎?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">您確定要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案嗎?</item>
- <item quantity="one">您確定要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案嗎?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案嗎?</item>
+ <item quantity="one">要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案嗎?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">已選取 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目</item>
+ <item quantity="one">已選取 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index c799fb3..a5ede47 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"部分檔案已轉換成其他格式"</string>
<string name="allow" msgid="7225948811296386551">"允許"</string>
<string name="deny" msgid="2081879885755434506">"拒絕"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"要刪除檔案嗎?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="other">確定要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案嗎?</item>
- <item quantity="one">確定要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案嗎?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="other">要刪除 <xliff:g id="COUNT_1">%1$d</xliff:g> 個檔案嗎?</item>
+ <item quantity="one">要刪除 <xliff:g id="COUNT_0">%1$d</xliff:g> 個檔案嗎?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="other">已選取 <xliff:g id="COUNT_1">%1$d</xliff:g> 個項目</item>
+ <item quantity="one">已選取 <xliff:g id="COUNT_0">%1$d</xliff:g> 個項目</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index eda4f7b..b99ab8c 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -112,9 +112,12 @@
<string name="notification_copy_files_converted_title" msgid="3153573223054275181">"Amanye amafayela aguqulelwe"</string>
<string name="allow" msgid="7225948811296386551">"Vumela"</string>
<string name="deny" msgid="2081879885755434506">"Yala"</string>
- <string name="delete_confirmation_title" msgid="1958369150786342998">"Susa amafayela?"</string>
- <plurals name="delete_confirmation_message" formatted="false" msgid="6608317554854868128">
- <item quantity="one">Ingabe uqinisekile ukuthi ufuna ukususa amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
- <item quantity="other">Ingabe uqinisekile ukuthi ufuna ukususa amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ <plurals name="delete_confirmation_message" formatted="false" msgid="3519107568984207772">
+ <item quantity="one">Sula amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ <item quantity="other">Sula amafayela angu-<xliff:g id="COUNT_1">%1$d</xliff:g>?</item>
+ </plurals>
+ <plurals name="elements_selected" formatted="false" msgid="1376955402452875047">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$d</xliff:g> okukhethiwe</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> okukhethiwe</item>
</plurals>
</resources>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 3785adf..04b7fee 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -23,8 +23,6 @@
<color name="window_background">#fff1f1f1</color>
<color name="drawer_background">#fff1f1f1</color>
<color name="directory_background">#fff7f7f7</color>
- <color name="item_doc_background">#fffafafa</color>
- <color name="item_doc_background_selected">#ffe0f2f1</color>
<color name="menu_search_background">#ff676f74</color>
<color name="primary_dark">@*android:color/primary_dark_material_dark</color>
@@ -35,4 +33,11 @@
<color name="band_select_background">#88ffffff</color>
<color name="band_select_border">#44000000</color>
+
+ <color name="item_doc_background_disabled">#fff4f4f4</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>
+
</resources>
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index 8a76540..86087c3 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -21,5 +21,4 @@
<!-- Intentionally unset. Vendors should set this in an overlay. -->
<string name="trusted_quick_viewer_package" translatable="false"></string>
<bool name="list_divider_inset_left">true</bool>
- <bool name="always_show_summary">false</bool>
</resources>
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index 9fc8a73..5af7da3 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -37,4 +37,5 @@
<dimen name="dir_elevation">8dp</dimen>
<dimen name="drag_shadow_size">120dp</dimen>
<dimen name="grid_item_elevation">2dp</dimen>
+ <dimen name="max_drawer_width">280dp</dimen>
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 6e1b30e..e2d1870 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -213,4 +213,9 @@
<item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> file?</item>
<item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> files?</item>
</plurals>
+ <!-- Label text showing user how many items are selected. Can be one or more elements. -->
+ <plurals name="elements_selected">
+ <item quantity="one"><xliff:g id="count" example="1">%1$d</xliff:g> selected</item>
+ <item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item>
+ </plurals>
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 6efe9d1..1a8ce18 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -18,10 +18,6 @@
import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.State.MODE_GRID;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_ENTER;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_LEAVE;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_SIDE;
import android.app.Activity;
import android.app.Fragment;
@@ -48,6 +44,7 @@
import com.android.documentsui.SearchViewManager.SearchManagerListener;
import com.android.documentsui.State.ViewMode;
+import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
@@ -154,7 +151,7 @@
final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
// Search uses backend ranking; no sorting, recents doesn't support sort.
- sort.setVisible(!inRecents && !mSearchManager.isSearching());
+ sort.setEnabled(!inRecents && !mSearchManager.isSearching());
sortSize.setVisible(mState.showSize); // Only sort by size when file sizes are visible
fileSize.setVisible(!mState.forceSize);
@@ -225,7 +222,7 @@
// Otherwise we delegate loading data from disk to a task
// to ensure a responsive ui.
if (mRoots.isRecentsRoot(root)) {
- refreshCurrentRootAndDirectory(ANIM_NONE);
+ refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
} else {
new PickRootTask(this, root).executeOnExecutor(getExecutorForCurrentDirectory());
}
@@ -244,6 +241,7 @@
return true;
case R.id.menu_search:
+ // SearchViewManager listens for this directly.
return false;
case R.id.menu_sort_name:
@@ -326,7 +324,7 @@
// previous directory. Especially after opening a root document, pressing
// back, wouldn't go to the previous root, but close the activity.
final int anim = (mState.hasLocationChanged() && mState.stack.size() > 1)
- ? ANIM_ENTER : ANIM_NONE;
+ ? AnimationView.ANIM_ENTER : AnimationView.ANIM_NONE;
refreshCurrentRootAndDirectory(anim);
}
@@ -366,6 +364,7 @@
assert(canSearchRoot());
reloadSearch(query);
+ invalidateOptionsMenu();
}
private void reloadSearch(String query) {
@@ -541,7 +540,7 @@
// Update the restored stack to ensure we have freshest data
stack.updateDocuments(getContentResolver());
mState.setStack(stack);
- refreshCurrentRootAndDirectory(ANIM_SIDE);
+ refreshCurrentRootAndDirectory(AnimationView.ANIM_SIDE);
} catch (FileNotFoundException e) {
Log.w(mTag, "Failed to restore stack: " + e);
@@ -642,7 +641,7 @@
private boolean popDir() {
if (mState.stack.size() > 1) {
mState.stack.pop();
- refreshCurrentRootAndDirectory(ANIM_LEAVE);
+ refreshCurrentRootAndDirectory(AnimationView.ANIM_LEAVE);
return true;
}
return false;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BootReceiver.java b/packages/DocumentsUI/src/com/android/documentsui/BootReceiver.java
new file mode 100644
index 0000000..cdea9d7
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/BootReceiver.java
@@ -0,0 +1,34 @@
+/*
+ * 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.documentsui;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Prime {@link RootsCache} when the system is booted.
+ */
+public class BootReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // We already spun up our application object before getting here, which
+ // kicked off a task to load roots, so this broadcast is finished once
+ // that first pass is done.
+ DocumentsApplication.getRootsCache(context).setBootCompletedResult(goAsync());
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
deleted file mode 100644
index 000b92a..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryView.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.documentsui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.LinearLayout;
-
-public class DirectoryView extends LinearLayout {
- private float mPosition = 0f;
-
- private int mWidth;
-
- public DirectoryView(Context context) {
- super(context);
- }
-
- public DirectoryView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mWidth = w;
- setPosition(mPosition);
- }
-
- public float getPosition() {
- return mPosition;
- }
-
- public void setPosition(float position) {
- mPosition = position;
- setX((mWidth > 0) ? (mPosition * mWidth) : 0);
-
- if (mPosition != 0) {
- setTranslationZ(getResources().getDimensionPixelSize(R.dimen.dir_elevation));
- } else {
- setTranslationZ(0);
- }
- }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Display.java b/packages/DocumentsUI/src/com/android/documentsui/Display.java
new file mode 100644
index 0000000..bae2d58
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Display.java
@@ -0,0 +1,56 @@
+/*
+ * 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.documentsui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Point;
+import android.util.TypedValue;
+
+/*
+ * Convenience class for getting display related attributes
+ */
+public final class Display {
+ /*
+ * Returns the screen width in pixels.
+ */
+ public static float screenWidth(Activity activity) {
+ Point size = new Point();
+ activity.getWindowManager().getDefaultDisplay().getSize(size);
+ return size.x;
+ }
+
+ /*
+ * Returns logical density of the display.
+ */
+ public static float density(Context context) {
+ return context.getResources().getDisplayMetrics().density;
+ }
+
+ /*
+ * Returns action bar height in pixels.
+ */
+ public static float actionBarHeight(Context context) {
+ int actionBarHeight = 0;
+ TypedValue tv = new TypedValue();
+ if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
+ actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
+ context.getResources().getDisplayMetrics());
+ }
+ return actionBarHeight;
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 12a4186..ba593dc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -22,7 +22,6 @@
import static com.android.documentsui.State.ACTION_OPEN;
import static com.android.documentsui.State.ACTION_OPEN_TREE;
import static com.android.documentsui.State.ACTION_PICK_COPY_DESTINATION;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import android.app.Activity;
import android.app.Fragment;
@@ -46,6 +45,7 @@
import com.android.documentsui.RecentsProvider.RecentColumns;
import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
@@ -95,7 +95,7 @@
}
if (mState.restored) {
- refreshCurrentRootAndDirectory(ANIM_NONE);
+ if (DEBUG) Log.d(TAG, "Stack already resolved");
} else {
// We set the activity title in AsyncTask.onPostExecute().
// To prevent talkback from reading aloud the default title, we clear it here.
@@ -108,9 +108,7 @@
// we restore the stack as last used from that app.
if (mState.action == ACTION_PICK_COPY_DESTINATION) {
if (DEBUG) Log.d(TAG, "Launching directly into Home directory.");
- Uri homeUri = DocumentsContract.buildHomeUri();
- new LoadRootTask(this, homeUri).executeOnExecutor(
- ProviderExecutor.forAuthority(homeUri.getAuthority()));
+ loadRoot(DocumentsContract.buildHomeUri());
} else {
if (DEBUG) Log.d(TAG, "Attempting to load last used stack for calling package.");
new LoadLastUsedStackTask(this).execute();
@@ -156,30 +154,6 @@
}
}
- private void onStackRestored(boolean restored, boolean external) {
- // Show drawer when no stack restored, but only when requesting
- // non-visual content. However, if we last used an external app,
- // drawer is always shown.
-
- boolean showDrawer = false;
- if (!restored) {
- showDrawer = true;
- }
- if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
- showDrawer = false;
- }
- if (external && mState.action == ACTION_GET_CONTENT) {
- showDrawer = true;
- }
- if (mState.action == ACTION_PICK_COPY_DESTINATION) {
- showDrawer = true;
- }
-
- if (showDrawer) {
- mNavigator.revealRootsDrawer(true);
- }
- }
-
public void onAppPicked(ResolveInfo info) {
final Intent intent = new Intent(getIntent());
intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
@@ -517,8 +491,8 @@
@Override
protected void finish(Void result) {
mState.restored = true;
- mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
- mOwner.onStackRestored(mRestoredStack, mExternal);
+ mState.external = mExternal;
+ mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
deleted file mode 100644
index 2f784cb..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/DownloadsActivity.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.documentsui;
-
-import static com.android.documentsui.State.ACTION_MANAGE;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.ActivityNotFoundException;
-import android.content.ClipData;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.DocumentsContract;
-import android.support.design.widget.Snackbar;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.Toolbar;
-
-import com.android.documentsui.dirlist.DirectoryFragment;
-import com.android.documentsui.dirlist.Model;
-import com.android.documentsui.model.DocumentInfo;
-import com.android.documentsui.model.RootInfo;
-
-import java.util.Arrays;
-import java.util.List;
-
-// Let's face it. MANAGE_ROOT is used almost exclusively
-// for downloads, and is specialized for this purpose.
-// So it is now thusly christened.
-public class DownloadsActivity extends BaseActivity {
- private static final String TAG = "DownloadsActivity";
-
- public DownloadsActivity() {
- super(R.layout.downloads_activity, TAG);
- }
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- final Context context = this;
-
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- toolbar.setTitleTextAppearance(context,
- android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
-
- if (!mState.restored) {
- // In this case, we set the activity title in AsyncTask.onPostExecute(). To prevent
- // talkback from reading aloud the default title, we clear it here.
- setTitle("");
- final Uri rootUri = getIntent().getData();
- new LoadRootTask(this, rootUri).executeOnExecutor(getExecutorForCurrentDirectory());
- } else {
- refreshCurrentRootAndDirectory(ANIM_NONE);
- }
- }
-
- @Override
- void includeState(State state) {
- state.action = ACTION_MANAGE;
- state.acceptMimes = new String[] { "*/*" };
- state.allowMultiple = true;
- state.showSize = true;
- state.excludedAuthorities = getExcludedAuthorities();
- }
-
- @Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
- mNavigator.update();
- }
-
- @Override
- public String getDrawerTitle() {
- return null; // being and nothingness
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- super.onPrepareOptionsMenu(menu);
-
- final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
- final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
- final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
-
- createDir.setVisible(false);
- pasteFromCb.setEnabled(false);
- fileSize.setVisible(false);
-
- Menus.disableHiddenItems(menu);
- return true;
- }
-
- @Override
- void refreshDirectory(int anim) {
- final FragmentManager fm = getFragmentManager();
- final RootInfo root = getCurrentRoot();
- final DocumentInfo cwd = getCurrentDirectory();
-
- assert(!mSearchManager.isSearching());
-
- // If started in manage roots mode, there has to be a cwd (i.e. the root dir of the managed
- // root).
- assert(cwd != null);
-
- // Normal boring directory
- DirectoryFragment.showDirectory(fm, root, cwd, anim);
- }
-
- @Override
- public void onDocumentPicked(DocumentInfo doc, Model model) {
- assert(!doc.isDirectory());
-
- // First try managing the document; we expect manager to filter
- // based on authority, so we don't grant.
- final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
- manage.setData(doc.derivedUri);
-
- try {
- startActivity(manage);
- } catch (ActivityNotFoundException ex) {
- // Fall back to viewing.
- final Intent view = new Intent(Intent.ACTION_VIEW);
- view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- view.setData(doc.derivedUri);
-
- try {
- startActivity(view);
- } catch (ActivityNotFoundException ex2) {
- Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT)
- .show();
- }
- }
- }
-
- @Override
- public void onDocumentsPicked(List<DocumentInfo> docs) {}
-
- @Override
- void onTaskFinished(Uri... uris) {
- Log.d(TAG, "onFinished() " + Arrays.toString(uris));
-
- final Intent intent = new Intent();
- if (uris.length == 1) {
- intent.setData(uris[0]);
- } else if (uris.length > 1) {
- final ClipData clipData = new ClipData(
- null, mState.acceptMimes, new ClipData.Item(uris[0]));
- for (int i = 1; i < uris.length; i++) {
- clipData.addItem(new ClipData.Item(uris[i]));
- }
- intent.setClipData(clipData);
- }
-
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
-
- setResult(Activity.RESULT_OK, intent);
- finish();
- }
-
- public static DownloadsActivity get(Fragment fragment) {
- return (DownloadsActivity) fragment.getActivity();
- }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
index 9fceff5..020f2c0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
@@ -16,10 +16,14 @@
package com.android.documentsui;
+import static com.android.documentsui.Shared.DEBUG;
+
import android.app.Activity;
+import android.content.Context;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.DrawerListener;
+import android.util.Log;
import android.view.View;
import android.widget.Toolbar;
@@ -30,6 +34,8 @@
*/
abstract class DrawerController implements DrawerListener {
+ public static final String TAG = "DrawerController";
+
abstract void setOpen(boolean open);
abstract boolean isPresent();
abstract boolean isOpen();
@@ -50,6 +56,8 @@
View drawer = activity.findViewById(R.id.drawer_roots);
Toolbar toolbar = (Toolbar) activity.findViewById(R.id.roots_toolbar);
+ drawer.getLayoutParams().width = calculateDrawerWidth(activity);
+
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
activity,
layout,
@@ -67,6 +75,19 @@
return new DummyDrawerController();
}
+ private static int calculateDrawerWidth(Activity activity) {
+ // Material design specification for navigation drawer:
+ // https://www.google.com/design/spec/patterns/navigation-drawer.html
+ float width = Display.screenWidth(activity) - Display.actionBarHeight(activity);
+ float maxWidth = activity.getResources().getDimension(R.dimen.max_drawer_width);
+ int finalWidth = (int) ((width > maxWidth ? maxWidth : width));
+
+ if (DEBUG)
+ Log.d(TAG, "Calculated drawer width:" + (finalWidth / Display.density(activity)));
+
+ return finalWidth;
+ }
+
/**
* Runtime controller that manages a real drawer.
*/
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index c17be06..99f306a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -18,7 +18,6 @@
import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_UNKNOWN;
import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_NONE;
import android.app.Activity;
import android.app.FragmentManager;
@@ -39,6 +38,7 @@
import com.android.documentsui.OperationDialogFragment.DialogType;
import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.model.DocumentInfo;
@@ -97,7 +97,7 @@
if (DEBUG) Log.d(TAG, "Launching with non-empty stack.");
assert(uri == null || uri.getAuthority() == null ||
LauncherActivity.isLaunchUri(uri));
- refreshCurrentRootAndDirectory(ANIM_NONE);
+ refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
} else if (intent.getAction() == Intent.ACTION_VIEW) {
assert(uri != null);
new OpenUriForViewTask(this).executeOnExecutor(
@@ -108,10 +108,10 @@
// authority. That way a misbehaving provider won't result in an ANR.
loadRoot(uri);
} else {
- if (DEBUG) Log.d(TAG, "Launching into Home directory.");
- // If all else fails, try to load "Home" directory.
- final Uri homeUri = DocumentsContract.buildHomeUri();
- loadRoot(homeUri);
+ if (DEBUG) Log.d(TAG, "All other means skipped. Launching into default directory.");
+ Uri defaultUri = DocumentsContract.buildRootUri(
+ "com.android.providers.downloads.documents", "downloads");
+ loadRoot(defaultUri);
}
final @DialogType int dialogType = intent.getIntExtra(
@@ -282,6 +282,30 @@
* Launches an intent to view the specified document.
*/
private void openDocument(DocumentInfo doc, Model model) {
+
+ // Anything on downloads goes through the back through downloads manager
+ // (that's the MANAGE_DOCUMENT bit).
+ // This is done for two reasons:
+ // 1) The file in question might be a failed/queued or otherwise have some
+ // specialized download handling.
+ // 2) For APKs, the download manager will add on some important security stuff
+ // like origin URL.
+ // All other files not on downloads, event APKs, would get no benefit from this
+ // treatment, thusly the "isDownloads" check.
+ if (getCurrentRoot().isDownloads()) {
+ // First try managing the document; we expect manager to filter
+ // based on authority, so we don't grant.
+ final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
+ manage.setData(doc.derivedUri);
+
+ try {
+ startActivity(manage);
+ return;
+ } catch (ActivityNotFoundException ex) {
+ // fall back to regular handling below.
+ }
+ }
+
Intent intent = new QuickViewIntentBuilder(
getPackageManager(), getResources(), doc, model).build();
@@ -296,7 +320,7 @@
}
}
- // Fallback to traditional VIEW action...
+ // Fall back to traditional VIEW action...
intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setData(doc.derivedUri);
@@ -452,7 +476,7 @@
@Override
protected void finish(Void result) {
- mOwner.refreshCurrentRootAndDirectory(ANIM_NONE);
+ mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
index dcaea15..afd308c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -18,6 +18,7 @@
import static android.os.Environment.STANDARD_DIRECTORIES;
import static com.android.documentsui.Shared.DEBUG;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.StringDef;
@@ -58,7 +59,7 @@
private static final String COUNT_CREATE_MIME = "docsui_create_mime";
private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime";
private static final String COUNT_BROWSE_ROOT = "docsui_browse_root";
- private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
+ @Deprecated private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
private static final String COUNT_FILEOP_SYSTEM = "docsui_fileop_system";
private static final String COUNT_FILEOP_EXTERNAL = "docsui_fileop_external";
@@ -194,7 +195,7 @@
private static final int ACTION_CREATE = 3;
private static final int ACTION_GET_CONTENT = 4;
private static final int ACTION_OPEN_TREE = 5;
- private static final int ACTION_MANAGE = 6;
+ @Deprecated private static final int ACTION_MANAGE = 6;
private static final int ACTION_BROWSE = 7;
private static final int ACTION_PICK_COPY_DESTINATION = 8;
@@ -246,9 +247,6 @@
case State.ACTION_GET_CONTENT:
logHistogram(context, COUNT_GET_CONTENT_MIME, sanitizeMime(intent.getType()));
break;
- case State.ACTION_MANAGE:
- logHistogram(context, COUNT_MANAGE_ROOT, sanitizeRoot(uri));
- break;
case State.ACTION_BROWSE:
logHistogram(context, COUNT_BROWSE_ROOT, sanitizeRoot(uri));
break;
@@ -641,8 +639,6 @@
return ACTION_GET_CONTENT;
case State.ACTION_OPEN_TREE:
return ACTION_OPEN_TREE;
- case State.ACTION_MANAGE:
- return ACTION_MANAGE;
case State.ACTION_BROWSE:
return ACTION_BROWSE;
case State.ACTION_PICK_COPY_DESTINATION:
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
index 9df55a0..2f202e7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
@@ -16,12 +16,15 @@
package com.android.documentsui;
+import android.annotation.Nullable;
+
import com.android.documentsui.model.DocumentInfo;
import com.android.internal.util.Predicate;
public class MimePredicate implements Predicate<DocumentInfo> {
private final String[] mFilters;
+ private static final String APK_TYPE = "application/vnd.android.package-archive";
/**
* MIME types that are visual in nature. For example, they should always be
* shown as thumbnails in list mode.
@@ -92,4 +95,8 @@
return false;
}
}
+
+ public static boolean isApkType(@Nullable String mimeType) {
+ return APK_TYPE.equals(mimeType);
+ }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
index 4cba135..30c1020 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/NavigationView.java
@@ -19,7 +19,6 @@
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.dirlist.DirectoryFragment.ANIM_LEAVE;
import android.annotation.Nullable;
import android.graphics.drawable.Drawable;
@@ -30,10 +29,10 @@
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
-import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
+import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
@@ -105,7 +104,7 @@
while (mState.stack.size() > position + 1) {
mState.popDocument();
}
- mEnv.refreshCurrentRootAndDirectory(ANIM_LEAVE);
+ mEnv.refreshCurrentRootAndDirectory(AnimationView.ANIM_LEAVE);
}
void update() {
@@ -220,17 +219,14 @@
.inflate(R.layout.item_subdir, parent, false);
}
- final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
final TextView title = (TextView) convertView.findViewById(android.R.id.title);
final DocumentInfo doc = getItem(position);
if (position == 0) {
final RootInfo root = mEnv.getCurrentRoot();
title.setText(root.title);
- subdir.setVisibility(View.GONE);
} else {
title.setText(doc.displayName);
- subdir.setVisibility(View.VISIBLE);
}
return convertView;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index a77a9b3..8fcd9d1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -113,8 +113,8 @@
}
private int collectViewableUris(ArrayList<Uri> uris) {
- final List<String> siblingIds = mModel.getModelIds();
- uris.ensureCapacity(siblingIds.size());
+ final String[] siblingIds = mModel.getModelIds();
+ uris.ensureCapacity(siblingIds.length);
int documentLocation = 0;
Cursor cursor;
@@ -124,8 +124,8 @@
Uri uri;
// Cursor's are not guaranteed to be immutable. Hence, traverse it only once.
- for (int i = 0; i < siblingIds.size(); i++) {
- cursor = mModel.getItem(siblingIds.get(i));
+ for (int i = 0; i < siblingIds.length; i++) {
+ cursor = mModel.getItem(siblingIds[i]);
mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 2b7294a..6efe9c8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -18,6 +18,7 @@
import static com.android.documentsui.Shared.DEBUG;
+import android.content.BroadcastReceiver.PendingResult;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,6 +31,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.DocumentsContract;
@@ -40,11 +42,11 @@
import com.android.documentsui.model.RootInfo;
import com.android.internal.annotations.GuardedBy;
+import libcore.io.IoUtils;
+
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
-import libcore.io.IoUtils;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -63,6 +65,8 @@
private static final String TAG = "RootsCache";
+ private static final boolean ENABLE_SYSTEM_CACHE = true;
+
private final Context mContext;
private final ContentObserver mObserver;
private OnCacheUpdateListener mCacheUpdateListener;
@@ -73,6 +77,11 @@
private final CountDownLatch mFirstLoad = new CountDownLatch(1);
@GuardedBy("mLock")
+ private boolean mFirstLoadDone;
+ @GuardedBy("mLock")
+ private PendingResult mBootCompletedResult;
+
+ @GuardedBy("mLock")
private Multimap<String, RootInfo> mRoots = ArrayListMultimap.create();
@GuardedBy("mLock")
private HashSet<String> mStoppedAuthorities = new HashSet<>();
@@ -118,7 +127,7 @@
public void updateAsync() {
// NOTE: This method is called when the UI language changes.
- // For that reason we upadte our RecentsRoot to reflect
+ // For that reason we update our RecentsRoot to reflect
// the current language.
mRecentsRoot.title = mContext.getString(R.string.root_recent);
@@ -152,7 +161,25 @@
}
}
- private void waitForFirstLoad() {
+ public void setBootCompletedResult(PendingResult result) {
+ synchronized (mLock) {
+ // Quickly check if we've already finished loading, otherwise hang
+ // out until first pass is finished.
+ if (mFirstLoadDone) {
+ result.finish();
+ } else {
+ mBootCompletedResult = result;
+ }
+ }
+ }
+
+ /**
+ * Block until the first {@link UpdateTask} pass has finished.
+ *
+ * @return {@code true} if cached roots is ready to roll, otherwise
+ * {@code false} if we timed out while waiting.
+ */
+ private boolean waitForFirstLoad() {
boolean success = false;
try {
success = mFirstLoad.await(15, TimeUnit.SECONDS);
@@ -161,6 +188,7 @@
if (!success) {
Log.w(TAG, "Timeout waiting for first update");
}
+ return success;
}
/**
@@ -222,9 +250,11 @@
final long start = SystemClock.elapsedRealtime();
if (mFilterPackage != null) {
- // Need at least first load, since we're going to be using
- // previously cached values for non-matching packages.
- waitForFirstLoad();
+ // We must have previously cached values to fill in non-matching
+ // packages, so wait around for successful first load.
+ if (!waitForFirstLoad()) {
+ return null;
+ }
}
mTaskRoots.put(mRecentsRoot.authority, mRecentsRoot);
@@ -243,6 +273,11 @@
if (DEBUG)
Log.d(TAG, "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
synchronized (mLock) {
+ mFirstLoadDone = true;
+ if (mBootCompletedResult != null) {
+ mBootCompletedResult.finish();
+ mBootCompletedResult = null;
+ }
mRoots = mTaskRoots;
mStoppedAuthorities = mTaskStoppedAuthorities;
}
@@ -300,9 +335,18 @@
}
}
- final List<RootInfo> roots = new ArrayList<>();
final Uri rootsUri = DocumentsContract.buildRootsUri(authority);
+ if (ENABLE_SYSTEM_CACHE) {
+ // Look for roots data that we might have cached for ourselves in the
+ // long-lived system process.
+ final Bundle systemCache = resolver.getCache(rootsUri);
+ if (systemCache != null) {
+ if (DEBUG) Log.d(TAG, "System cache hit for " + authority);
+ return systemCache.getParcelableArrayList(TAG);
+ }
+ }
+ final ArrayList<RootInfo> roots = new ArrayList<>();
ContentProviderClient client = null;
Cursor cursor = null;
try {
@@ -318,6 +362,16 @@
IoUtils.closeQuietly(cursor);
ContentProviderClient.releaseQuietly(client);
}
+
+ if (ENABLE_SYSTEM_CACHE) {
+ // Cache these freshly parsed roots over in the long-lived system
+ // process, in case our process goes away. The system takes care of
+ // invalidating the cache if the package or Uri changes.
+ final Bundle systemCache = new Bundle();
+ systemCache.putParcelableArrayList(TAG, roots);
+ resolver.putCache(rootsUri, systemCache);
+ }
+
return roots;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index 26900a7..b539421 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -86,12 +86,6 @@
*/
public static final String EXTRA_IGNORE_STATE = "ignoreState";
-
- /**
- * String prefix used to indicate the document is a directory.
- */
- public static final char DIR_PREFIX = '\001';
-
private static final Collator sCollator;
static {
@@ -150,12 +144,6 @@
if (leftEmpty) return -1;
if (rightEmpty) return 1;
- final boolean leftDir = (lhs.charAt(0) == DIR_PREFIX);
- final boolean rightDir = (rhs.charAt(0) == DIR_PREFIX);
-
- if (leftDir && !rightDir) return -1;
- if (rightDir && !leftDir) return 1;
-
return sCollator.compare(lhs, rhs);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index 4f460b4..16b7660 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -43,13 +43,15 @@
private static final String TAG = "State";
- public static final int ACTION_OPEN = 1;
- public static final int ACTION_CREATE = 2;
- public static final int ACTION_GET_CONTENT = 3;
- public static final int ACTION_OPEN_TREE = 4;
- public static final int ACTION_MANAGE = 5;
- public static final int ACTION_BROWSE = 6;
- public static final int ACTION_PICK_COPY_DESTINATION = 8;
+ // File manager and related private picking activity.
+ public static final int ACTION_BROWSE = 1;
+ public static final int ACTION_PICK_COPY_DESTINATION = 2;
+
+ // All public picking activities
+ public static final int ACTION_OPEN = 3;
+ public static final int ACTION_CREATE = 4;
+ public static final int ACTION_GET_CONTENT = 5;
+ public static final int ACTION_OPEN_TREE = 6;
@IntDef(flag = true, value = {
MODE_UNKNOWN,
@@ -83,6 +85,10 @@
public boolean showSize;
public boolean localOnly;
public boolean restored;
+ /*
+ * Indicates handler was an external app, like photos.
+ */
+ public boolean external;
// Indicates that a copy operation (or move) includes a directory.
// Why? Directory creation isn't supported by some roots (like Downloads).
@@ -180,6 +186,7 @@
out.writeInt(showSize ? 1 : 0);
out.writeInt(localOnly ? 1 : 0);
out.writeInt(restored ? 1 : 0);
+ out.writeInt(external ? 1 : 0);
DurableUtils.writeToParcel(out, stack);
out.writeMap(dirState);
out.writeParcelable(selectedDocuments, 0);
@@ -208,6 +215,7 @@
state.showSize = in.readInt() != 0;
state.localOnly = in.readInt() != 0;
state.restored = in.readInt() != 0;
+ state.external = in.readInt() != 0;
DurableUtils.readFromParcel(in, state.stack);
in.readMap(state.dirState, loader);
state.selectedDocuments = in.readParcelable(loader);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/AnimationView.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/AnimationView.java
new file mode 100644
index 0000000..a6664565
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/AnimationView.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.documentsui.dirlist;
+
+import android.annotation.IntDef;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import com.android.documentsui.R;
+import com.android.documentsui.Shared;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class exists solely to support animated transition of our directory fragment.
+ * The structure of this class is tightly coupled with the static animations defined in
+ * res/animator, specifically the "position" property referenced by
+ * res/animator/dir_{enter,leave}.xml.
+ */
+public class AnimationView extends LinearLayout {
+
+ @IntDef(flag = true, value = {
+ ANIM_NONE,
+ ANIM_SIDE,
+ ANIM_LEAVE,
+ ANIM_ENTER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AnimationType {}
+ public static final int ANIM_NONE = 1;
+ public static final int ANIM_SIDE = 2;
+ public static final int ANIM_LEAVE = 3;
+ public static final int ANIM_ENTER = 4;
+
+ private float mPosition = 0f;
+
+ // The distance the animation will cover...currently matches the height of the
+ // content area.
+ private int mSpan;
+
+ public AnimationView(Context context) {
+ super(context);
+ }
+
+ public AnimationView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mSpan = h;
+ setPosition(mPosition);
+ }
+
+ public float getPosition() {
+ return mPosition;
+ }
+
+ public void setPosition(float position) {
+ mPosition = position;
+ // Warning! If we ever decide to switch this to setX (slide left/right)
+ // please remember to add RLT variations of the animations under res/animator-ldrtl.
+ setY((mSpan > 0) ? (mPosition * mSpan) : 0);
+
+ if (mPosition != 0) {
+ setTranslationZ(getResources().getDimensionPixelSize(R.dimen.dir_elevation));
+ } else {
+ setTranslationZ(0);
+ }
+ }
+
+ /**
+ * Configures custom animations on the transaction according to the specified
+ * @AnimationType.
+ */
+ static void setupAnimations(
+ FragmentTransaction ft, @AnimationType int anim, Bundle args) {
+ switch (anim) {
+ case AnimationView.ANIM_SIDE:
+ args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
+ break;
+ case AnimationView.ANIM_ENTER:
+ // TODO: Document which behavior is being tailored
+ // by passing this bit. Remove if possible.
+ args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
+ ft.setCustomAnimations(R.animator.dir_enter, R.animator.fade_out);
+ break;
+ case AnimationView.ANIM_LEAVE:
+ ft.setCustomAnimations(R.animator.fade_in, R.animator.dir_leave);
+ break;
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 83838d3..bfc8d71 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -17,7 +17,6 @@
package com.android.documentsui.dirlist;
import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.State.ACTION_MANAGE;
import static com.android.documentsui.State.MODE_GRID;
import static com.android.documentsui.State.MODE_LIST;
import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
@@ -64,6 +63,7 @@
import android.view.ActionMode;
import android.view.DragEvent;
import android.view.GestureDetector;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -84,7 +84,6 @@
import com.android.documentsui.Events.MotionInputEvent;
import com.android.documentsui.Menus;
import com.android.documentsui.MessageBar;
-import com.android.documentsui.MimePredicate;
import com.android.documentsui.R;
import com.android.documentsui.RecentsLoader;
import com.android.documentsui.RootsCache;
@@ -125,19 +124,6 @@
public static final int TYPE_RECENT_OPEN = 2;
@IntDef(flag = true, value = {
- ANIM_NONE,
- ANIM_SIDE,
- ANIM_LEAVE,
- ANIM_ENTER
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AnimationType {}
- public static final int ANIM_NONE = 1;
- public static final int ANIM_SIDE = 2;
- public static final int ANIM_LEAVE = 3;
- public static final int ANIM_ENTER = 4;
-
- @IntDef(flag = true, value = {
REQUEST_COPY_DESTINATION
})
@Retention(RetentionPolicy.SOURCE)
@@ -281,16 +267,6 @@
mTuner = FragmentTuner.pick(getContext(), state);
mClipper = new DocumentClipper(context);
- boolean hideGridTitles;
- if (mType == TYPE_RECENT_OPEN) {
- // Hide titles when showing recents for picking images/videos
- hideGridTitles = MimePredicate.mimeMatches(
- MimePredicate.VISUAL_MIMES, state.acceptMimes);
- } else {
- hideGridTitles = (mDocument != null) && mDocument.isGridTitlesHidden();
- }
- GridDocumentHolder.setHideTitles(hideGridTitles);
-
final ActivityManager am = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
boolean svelte = am.isLowRamDevice() && (mType == TYPE_RECENT_OPEN);
@@ -527,7 +503,8 @@
getActivity().getWindow().setStatusBarColor(color.data);
if (mActionMode != null) {
- mActionMode.setTitle(String.valueOf(mSelected.size()));
+ mActionMode.setTitle(Shared.getQuantityString(getActivity(),
+ R.plurals.elements_selected, mSelected.size()));
}
}
@@ -545,6 +522,8 @@
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mRecView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+
int size = mSelectionManager.getSelection().size();
mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
mode.setTitle(TextUtils.formatSelectedCount(size));
@@ -587,18 +566,17 @@
case R.id.menu_share:
shareDocuments(selection);
- mode.finish();
return true;
case R.id.menu_delete:
- // Exit selection mode first, so we avoid deselecting deleted documents.
- mode.finish();
- deleteDocuments(selection);
+ // Pass mode along to the delete function so it can
+ // end action mode when documents are deleted.
+ // It won't end action mode if user cancels the delete.
+ deleteDocuments(selection, mode);
return true;
case R.id.menu_copy_to:
transferDocuments(selection, FileOperationService.OPERATION_COPY);
- mode.finish();
return true;
case R.id.menu_move_to:
@@ -616,8 +594,10 @@
return true;
case R.id.menu_rename:
- renameDocuments(selection);
+ // Exit selection mode first, so we avoid deselecting deleted
+ // (renamed) documents.
mode.finish();
+ renameDocuments(selection);
return true;
default:
@@ -701,7 +681,7 @@
}.execute(selected);
}
- private void deleteDocuments(final Selection selected) {
+ private void deleteDocuments(final Selection selected, final ActionMode mode) {
assert(!selected.isEmpty());
final DocumentInfo srcParent = getDisplayState().stack.peek();
@@ -733,7 +713,15 @@
android.R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- // Hide the files in the UI.
+ // Finish selection mode first which clears selection so we
+ // don't end up trying to deselect deleted documents.
+ // This is done here, rather in the onActionItemClicked
+ // so we can avoid de-selecting items in the case where
+ // the user cancels the delete.
+ mode.finish();
+ // Hide the files in the UI...since the operation
+ // might be queued up on FileOperationService.
+ // We're walking a line here.
mAdapter.hide(selected.getAll());
FileOperations.delete(
getActivity(), docs, srcParent, getDisplayState().stack);
@@ -1483,18 +1471,7 @@
args.putParcelable(Shared.EXTRA_SELECTION, new Selection());
final FragmentTransaction ft = fm.beginTransaction();
- switch (anim) {
- case ANIM_SIDE:
- args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
- break;
- case ANIM_ENTER:
- args.putBoolean(Shared.EXTRA_IGNORE_STATE, true);
- ft.setCustomAnimations(R.animator.dir_enter, R.animator.dir_frozen);
- break;
- case ANIM_LEAVE:
- ft.setCustomAnimations(R.animator.dir_frozen, R.animator.dir_leave);
- break;
- }
+ AnimationView.setupAnimations(ft, anim, args);
final DirectoryFragment fragment = new DirectoryFragment();
fragment.setArguments(args);
@@ -1535,7 +1512,7 @@
mRoot.authority, mRoot.rootId, mQuery)
: DocumentsContract.buildChildDocumentsUri(
mDocument.authority, mDocument.documentId);
- if (state.action == ACTION_MANAGE) {
+ if (mTuner.enableManagedMode()) {
contentsUri = DocumentsContract.setManageMode(contentsUri);
}
return new DirectoryLoader(
@@ -1544,6 +1521,7 @@
case TYPE_RECENT_OPEN:
final RootsCache roots = DocumentsApplication.getRootsCache(context);
return new RecentsLoader(context, roots, state);
+
default:
throw new IllegalStateException("Unknown type " + mType);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
index 3b5ce87..5edda38 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
@@ -16,6 +16,7 @@
package com.android.documentsui.dirlist;
+import android.annotation.ColorInt;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Rect;
@@ -35,17 +36,19 @@
extends RecyclerView.ViewHolder
implements View.OnKeyListener {
+ static final float DISABLED_ALPHA = 0.3f;
+
public @Nullable String modelId;
- final int mSelectedItemColor;
- final int mDefaultItemColor;
- final boolean mAlwaysShowSummary;
final Context mContext;
+ final @ColorInt int mDefaultBgColor;
+ final @ColorInt int mSelectedBgColor;
DocumentHolder.EventListener mEventListener;
private View.OnKeyListener mKeyListener;
private View mSelectionHotspot;
+
public DocumentHolder(Context context, ViewGroup parent, int layout) {
this(context, inflateLayout(context, parent, layout));
}
@@ -57,9 +60,8 @@
mContext = context;
- mDefaultItemColor = context.getColor(R.color.item_doc_background);
- mSelectedItemColor = context.getColor(R.color.item_doc_background_selected);
- mAlwaysShowSummary = context.getResources().getBoolean(R.bool.always_show_summary);
+ mDefaultBgColor = context.getColor(R.color.item_doc_background);
+ mSelectedBgColor = context.getColor(R.color.item_doc_background_selected);
mSelectionHotspot = itemView.findViewById(R.id.icon_check);
}
@@ -80,7 +82,7 @@
*/
public void setSelected(boolean selected) {
itemView.setActivated(selected);
- itemView.setBackgroundColor(selected ? mSelectedItemColor : mDefaultItemColor);
+ itemView.setBackgroundColor(selected ? mSelectedBgColor : mDefaultBgColor);
}
/**
@@ -88,7 +90,11 @@
* @param highlighted
*/
public void setHighlighted(boolean highlighted) {
- itemView.setBackgroundColor(highlighted ? mSelectedItemColor : mDefaultItemColor);
+ itemView.setBackgroundColor(highlighted ? mSelectedBgColor : mDefaultBgColor);
+ }
+
+ public void setEnabled(boolean enabled) {
+ setEnabledRecursive(itemView, enabled);
}
@Override
@@ -111,10 +117,6 @@
mKeyListener = listener;
}
- public void setEnabled(boolean enabled) {
- setEnabledRecursive(itemView, enabled);
- }
-
public boolean onSingleTapUp(MotionEvent event) {
if (Events.isMouseEvent(event)) {
// Mouse clicks select.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index f99ec85c..0ee7623 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -19,9 +19,9 @@
import static com.android.documentsui.State.ACTION_BROWSE;
import static com.android.documentsui.State.ACTION_CREATE;
import static com.android.documentsui.State.ACTION_GET_CONTENT;
-import static com.android.documentsui.State.ACTION_MANAGE;
import static com.android.documentsui.State.ACTION_OPEN;
import static com.android.documentsui.State.ACTION_OPEN_TREE;
+import static com.android.documentsui.State.ACTION_PICK_COPY_DESTINATION;
import android.content.Context;
import android.provider.DocumentsContract.Document;
@@ -53,8 +53,6 @@
switch (state.action) {
case ACTION_BROWSE:
return new FilesTuner(context, state);
- case ACTION_MANAGE:
- return new DownloadsTuner(context, state);
default:
return new DocumentsTuner(context, state);
}
@@ -82,6 +80,12 @@
abstract void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch);
/**
+ * When managed mode is enabled, active downloads will be visible in the UI.
+ * Presumably this should only be true when in the downloads directory.
+ */
+ abstract boolean enableManagedMode();
+
+ /**
* Provides support for Platform specific specializations of DirectoryFragment.
*/
private static final class DocumentsTuner extends FragmentTuner {
@@ -151,47 +155,36 @@
@Override
void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {
+ boolean showDrawer = false;
+
+ if (mState.restored) {
+ showDrawer = true;
+ }
+ if (MimePredicate.mimeMatches(MimePredicate.VISUAL_MIMES, mState.acceptMimes)) {
+ showDrawer = false;
+ }
+ if (mState.external && mState.action == ACTION_GET_CONTENT) {
+ showDrawer = true;
+ }
+ if (mState.action == ACTION_PICK_COPY_DESTINATION) {
+ showDrawer = true;
+ }
+
// When launched into empty root, open drawer.
- if (model.isEmpty() && !mState.hasInitialLocationChanged() && !isSearch) {
+ if (model.isEmpty()) {
+ showDrawer = true;
+ }
+
+ if (showDrawer && !mState.hasInitialLocationChanged() && !isSearch) {
// This noops on layouts without drawer, so no need to guard.
((BaseActivity) mContext).setRootsDrawerOpen(true);
}
}
- }
-
- /**
- * Provides support for Platform specific specializations of DirectoryFragment.
- */
- private static final class DownloadsTuner extends FragmentTuner {
-
- public DownloadsTuner(Context context, State state) {
- super(context, state);
- }
@Override
- public void updateActionMenu(
- Menu menu, @ResultType int resultType, boolean canDelete, boolean canRename) {
- assert(resultType != DirectoryFragment.TYPE_RECENT_OPEN);
-
- MenuItem open = menu.findItem(R.id.menu_open);
- MenuItem delete = menu.findItem(R.id.menu_delete);
- MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
- MenuItem moveTo = menu.findItem(R.id.menu_move_to);
- MenuItem rename = menu.findItem(R.id.menu_rename);
- MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard);
-
- open.setVisible(false);
- delete.setVisible(canDelete);
- copy.setEnabled(true); // to clipboard
- copyTo.setVisible(true);
- copyTo.setEnabled(true);
- moveTo.setVisible(true);
- moveTo.setEnabled(true);
- rename.setVisible(false);
+ public boolean enableManagedMode() {
+ return false;
}
-
- @Override
- void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {}
}
/**
@@ -233,6 +226,17 @@
((BaseActivity) mContext).setRootsDrawerOpen(true);
}
}
+
+ @Override
+ public boolean enableManagedMode() {
+ // When in downloads top level directory, we also show active downloads.
+ // And while we don't allow folders in Downloads, we do allow Zip files in
+ // downloads that themselves can be opened and viewed like directories.
+ // This method helps us understand when to kick in on those special behaviors.
+ return mState.stack.root != null
+ && mState.stack.root.isDownloads()
+ && mState.stack.size() == 1;
+ }
}
private static boolean isDirectory(String mimeType) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDocumentHolder.java
index a4bce16..c8641a8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/GridDocumentHolder.java
@@ -20,6 +20,7 @@
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
+import android.annotation.ColorInt;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
@@ -37,6 +38,7 @@
import com.android.documentsui.State;
final class GridDocumentHolder extends DocumentHolder {
+
private static boolean mHideTitles;
final TextView mTitle;
@@ -48,9 +50,13 @@
final ImageView mIconCheck;
final IconHelper mIconHelper;
+ private final @ColorInt int mDisabledBgColor;
+
public GridDocumentHolder(Context context, ViewGroup parent, IconHelper iconHelper) {
super(context, parent, R.layout.item_doc_grid);
+ mDisabledBgColor = context.getColor(R.color.item_doc_background_disabled);
+
mTitle = (TextView) itemView.findViewById(android.R.id.title);
mDate = (TextView) itemView.findViewById(R.id.date);
mSize = (TextView) itemView.findViewById(R.id.size);
@@ -64,13 +70,35 @@
@Override
public void setSelected(boolean selected) {
- super.setSelected(selected);
+ // We always want to make sure our check box disappears if we're not selected,
+ // even if the item is disabled. This is because this object can be reused
+ // and this method will be called to setup initial state.
float checkAlpha = selected ? 1f : 0f;
-
mIconCheck.animate().alpha(checkAlpha).start();
+
+ // But it should be an error to be set to selected && be disabled.
+ if (!itemView.isEnabled()) {
+ assert(!selected);
+ return;
+ }
+
+ super.setSelected(selected);
+
mIconMimeSm.animate().alpha(1f - checkAlpha).start();
}
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+
+ // Text colors enabled/disabled is handle via a color set.
+ itemView.setBackgroundColor(enabled ? mDefaultBgColor : mDisabledBgColor);
+ float imgAlpha = enabled ? 1f : DISABLED_ALPHA;
+
+ mIconMimeLg.setAlpha(imgAlpha);
+ mIconMimeSm.setAlpha(imgAlpha);
+ mIconThumb.setAlpha(imgAlpha);
+ }
+
/**
* Bind this view to the given document for display.
* @param cursor Pointing to the item to be bound.
@@ -122,12 +150,4 @@
mSize.setText(Formatter.formatFileSize(mContext, docSize));
}
}
-
- /**
- * Sets whether to hide titles on subsequently created GridDocumentHolder items.
- * @param hideTitles
- */
- public static void setHideTitles(boolean hideTitles) {
- mHideTitles = hideTitles;
- }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
index 8b619b6..3a1be11 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ListDocumentHolder.java
@@ -52,27 +52,47 @@
super(context, parent, R.layout.item_doc_list);
mTitle = (TextView) itemView.findViewById(android.R.id.title);
- mDetails = (LinearLayout) itemView.findViewById(R.id.line2);
mDate = (TextView) itemView.findViewById(R.id.date);
mSize = (TextView) itemView.findViewById(R.id.size);
mSummary = (TextView) itemView.findViewById(android.R.id.summary);
mIconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
mIconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);
mIconCheck = (ImageView) itemView.findViewById(R.id.icon_check);
+ // Warning: mDetails view doesn't exists in layout-sw720dp-land layout
+ mDetails = (LinearLayout) itemView.findViewById(R.id.line2);
mIconHelper = iconHelper;
}
@Override
public void setSelected(boolean selected) {
- super.setSelected(selected);
+ // We always want to make sure our check box disappears if we're not selected,
+ // even if the item is disabled. But it should be an error (see assert below)
+ // to be set to selected && be disabled.
float checkAlpha = selected ? 1f : 0f;
-
mIconCheck.animate().alpha(checkAlpha).start();
+
+ if (!itemView.isEnabled()) {
+ assert(!selected);
+ return;
+ }
+
+ super.setSelected(selected);
+
mIconMime.animate().alpha(1f - checkAlpha).start();
mIconThumb.animate().alpha(1f - checkAlpha).start();
}
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+
+ // Text colors enabled/disabled is handle via a color set.
+ final float imgAlpha = enabled ? 1f : DISABLED_ALPHA;
+ mIconMime.setAlpha(imgAlpha);
+ mIconThumb.setAlpha(imgAlpha);
+ }
+
/**
* Bind this view to the given document for display.
* @param cursor Pointing to the item to be bound.
@@ -110,11 +130,11 @@
mTitle.setVisibility(View.VISIBLE);
- // Note, we don't show any details for any directory...ever.
+ boolean hasDetails = false;
if (isDirectory) {
- mDetails.setVisibility(View.GONE);
+ // Note, we don't show any details for any directory...ever.
+ hasDetails = false;
} else {
- boolean hasDetails = false;
if (docSummary != null) {
hasDetails = true;
mSummary.setText(docSummary);
@@ -136,17 +156,12 @@
mSize.setText(Formatter.formatFileSize(mContext, docSize));
} else {
mSize.setVisibility(View.GONE);
- mDetails.setVisibility(View.GONE);
}
+ }
+
+ // mDetails view doesn't exists in layout-sw720dp-land layout
+ if (mDetails != null) {
mDetails.setVisibility(hasDetails ? View.VISIBLE : View.GONE);
}
}
-
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- final float iconAlpha = enabled ? 1f : 0.5f;
- mIconMime.setAlpha(iconAlpha);
- mIconThumb.setAlpha(iconAlpha);
- }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
index 8170e2a..c5ee592 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java
@@ -59,7 +59,7 @@
* A sorted array of model IDs for the files currently in the Model. Sort order is determined
* by {@link #mSortOrder}
*/
- private List<String> mIds = new ArrayList<>();
+ private String mIds[] = new String[0];
private int mSortOrder = SORT_ORDER_DISPLAY_NAME;
@Nullable String info;
@@ -108,7 +108,7 @@
if (result == null) {
mCursor = null;
mCursorCount = 0;
- mIds.clear();
+ mIds = new String[0];
mPositions.clear();
info = null;
error = null;
@@ -152,74 +152,81 @@
*/
private void updateModelData() {
int[] positions = new int[mCursorCount];
- mIds.clear();
- String[] stringValues = new String[mCursorCount];
+ mIds = new String[mCursorCount];
+ boolean[] isDirs = new boolean[mCursorCount];
+ String[] displayNames = null;
long[] longValues = null;
- if (mSortOrder == SORT_ORDER_LAST_MODIFIED || mSortOrder == SORT_ORDER_SIZE) {
- longValues = new long[mCursorCount];
+ switch (mSortOrder) {
+ case SORT_ORDER_DISPLAY_NAME:
+ displayNames = new String[mCursorCount];
+ break;
+ case SORT_ORDER_LAST_MODIFIED:
+ case SORT_ORDER_SIZE:
+ longValues = new long[mCursorCount];
+ break;
}
+ String mimeType;
+
mCursor.moveToPosition(-1);
for (int pos = 0; pos < mCursorCount; ++pos) {
mCursor.moveToNext();
positions[pos] = pos;
- mIds.add(createModelId(mCursor));
+ mIds[pos] = createModelId(mCursor);
+
+ mimeType = getCursorString(mCursor, Document.COLUMN_MIME_TYPE);
+ isDirs[pos] = Document.MIME_TYPE_DIR.equals(mimeType);
switch(mSortOrder) {
case SORT_ORDER_DISPLAY_NAME:
- final String mimeType = getCursorString(mCursor, Document.COLUMN_MIME_TYPE);
final String displayName = getCursorString(
mCursor, Document.COLUMN_DISPLAY_NAME);
- if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- stringValues[pos] = Shared.DIR_PREFIX + displayName;
- } else {
- stringValues[pos] = displayName;
- }
+ displayNames[pos] = displayName;
break;
case SORT_ORDER_LAST_MODIFIED:
longValues[pos] = getLastModified(mCursor);
- stringValues[pos] = getCursorString(mCursor, Document.COLUMN_MIME_TYPE);
break;
case SORT_ORDER_SIZE:
longValues[pos] = getCursorLong(mCursor, Document.COLUMN_SIZE);
- stringValues[pos] = getCursorString(mCursor, Document.COLUMN_MIME_TYPE);
break;
}
}
switch (mSortOrder) {
case SORT_ORDER_DISPLAY_NAME:
- binarySort(stringValues, positions, mIds);
+ binarySort(displayNames, isDirs, positions, mIds);
break;
case SORT_ORDER_LAST_MODIFIED:
case SORT_ORDER_SIZE:
- binarySort(longValues, stringValues, positions, mIds);
+ binarySort(longValues, isDirs, positions, mIds);
break;
}
// Populate the positions.
mPositions.clear();
for (int i = 0; i < mCursorCount; ++i) {
- mPositions.put(mIds.get(i), positions[i]);
+ mPositions.put(mIds[i], positions[i]);
}
}
/**
* Sorts model data. Takes three columns of index-corresponded data. The first column is the
- * sort key. Rows are sorted in ascending alphabetical order on the sort key. This code is based
- * on TimSort.binarySort().
+ * sort key. Rows are sorted in ascending alphabetical order on the sort key.
+ * Directories are always shown first. This code is based on TimSort.binarySort().
*
* @param sortKey Data is sorted in ascending alphabetical order.
+ * @param isDirs Array saying whether an item is a directory or not.
* @param positions Cursor positions to be sorted.
* @param ids Model IDs to be sorted.
*/
- private static void binarySort(String[] sortKey, int[] positions, List<String> ids) {
+ private static void binarySort(String[] sortKey, boolean[] isDirs, int[] positions, String[] ids) {
final int count = positions.length;
for (int start = 1; start < count; start++) {
final int pivotPosition = positions[start];
final String pivotValue = sortKey[start];
- final String pivotId = ids.get(start);
+ final boolean pivotIsDir = isDirs[start];
+ final String pivotId = ids[start];
int left = 0;
int right = start;
@@ -227,9 +234,18 @@
while (left < right) {
int mid = (left + right) >>> 1;
- final String lhs = pivotValue;
- final String rhs = sortKey[mid];
- final int compare = Shared.compareToIgnoreCaseNullable(lhs, rhs);
+ // Directories always go in front.
+ int compare = 0;
+ final boolean rhsIsDir = isDirs[mid];
+ if (pivotIsDir && !rhsIsDir) {
+ compare = -1;
+ } else if (!pivotIsDir && rhsIsDir) {
+ compare = 1;
+ } else {
+ final String lhs = pivotValue;
+ final String rhs = sortKey[mid];
+ compare = Shared.compareToIgnoreCaseNullable(lhs, rhs);
+ }
if (compare < 0) {
right = mid;
@@ -243,23 +259,25 @@
case 2:
positions[left + 2] = positions[left + 1];
sortKey[left + 2] = sortKey[left + 1];
- ids.set(left + 2, ids.get(left + 1));
+ isDirs[left + 2] = isDirs[left + 1];
+ ids[left + 2] = ids[left + 1];
case 1:
positions[left + 1] = positions[left];
sortKey[left + 1] = sortKey[left];
- ids.set(left + 1, ids.get(left));
+ isDirs[left + 1] = isDirs[left];
+ ids[left + 1] = ids[left];
break;
default:
System.arraycopy(positions, left, positions, left + 1, n);
System.arraycopy(sortKey, left, sortKey, left + 1, n);
- for (int i = n; i >= 1; --i) {
- ids.set(left + i, ids.get(left + i - 1));
- }
+ System.arraycopy(isDirs, left, isDirs, left + 1, n);
+ System.arraycopy(ids, left, ids, left + 1, n);
}
positions[left] = pivotPosition;
sortKey[left] = pivotValue;
- ids.set(left, pivotId);
+ isDirs[left] = pivotIsDir;
+ ids[left] = pivotId;
}
}
@@ -270,18 +288,18 @@
* numerical order on the sort key. This code is based on TimSort.binarySort().
*
* @param sortKey Data is sorted in descending numerical order.
- * @param mimeTypes Corresponding mime types. Directories will be sorted ahead of documents.
+ * @param isDirs Array saying whether an item is a directory or not.
* @param positions Cursor positions to be sorted.
* @param ids Model IDs to be sorted.
*/
private static void binarySort(
- long[] sortKey, String[] mimeTypes, int[] positions, List<String> ids) {
+ long[] sortKey, boolean[] isDirs, int[] positions, String[] ids) {
final int count = positions.length;
for (int start = 1; start < count; start++) {
final int pivotPosition = positions[start];
final long pivotValue = sortKey[start];
- final String pivotMime = mimeTypes[start];
- final String pivotId = ids.get(start);
+ final boolean pivotIsDir = isDirs[start];
+ final String pivotId = ids[start];
int left = 0;
int right = start;
@@ -289,13 +307,12 @@
while (left < right) {
int mid = ((left + right) >>> 1);
- // First bucket by mime type. Directories always go in front.
+ // Directories always go in front.
int compare = 0;
- final boolean lhsIsDir = Document.MIME_TYPE_DIR.equals(pivotMime);
- final boolean rhsIsDir = Document.MIME_TYPE_DIR.equals(mimeTypes[mid]);
- if (lhsIsDir && !rhsIsDir) {
+ final boolean rhsIsDir = isDirs[mid];
+ if (pivotIsDir && !rhsIsDir) {
compare = -1;
- } else if (!lhsIsDir && rhsIsDir) {
+ } else if (!pivotIsDir && rhsIsDir) {
compare = 1;
} else {
final long lhs = pivotValue;
@@ -310,7 +327,7 @@
// have identical numerical sort keys. One common example of this scenario is seen
// when sorting a set of active downloads by mod time.
if (compare == 0) {
- compare = pivotId.compareTo(ids.get(mid));
+ compare = pivotId.compareTo(ids[mid]);
}
if (compare < 0) {
@@ -325,27 +342,25 @@
case 2:
positions[left + 2] = positions[left + 1];
sortKey[left + 2] = sortKey[left + 1];
- mimeTypes[left + 2] = mimeTypes[left + 1];
- ids.set(left + 2, ids.get(left + 1));
+ isDirs[left + 2] = isDirs[left + 1];
+ ids[left + 2] = ids[left + 1];
case 1:
positions[left + 1] = positions[left];
sortKey[left + 1] = sortKey[left];
- mimeTypes[left + 1] = mimeTypes[left];
- ids.set(left + 1, ids.get(left));
+ isDirs[left + 1] = isDirs[left];
+ ids[left + 1] = ids[left];
break;
default:
System.arraycopy(positions, left, positions, left + 1, n);
System.arraycopy(sortKey, left, sortKey, left + 1, n);
- System.arraycopy(mimeTypes, left, mimeTypes, left + 1, n);
- for (int i = n; i >= 1; --i) {
- ids.set(left + i, ids.get(left + i - 1));
- }
+ System.arraycopy(isDirs, left, isDirs, left + 1, n);
+ System.arraycopy(ids, left, ids, left + 1, n);
}
positions[left] = pivotPosition;
sortKey[left] = pivotValue;
- mimeTypes[left] = pivotMime;
- ids.set(left, pivotId);
+ isDirs[left] = pivotIsDir;
+ ids[left] = pivotId;
}
}
@@ -413,7 +428,7 @@
* @return An ordered array of model IDs representing the documents in the model. It is sorted
* according to the current sort order, which was set by the last model update.
*/
- public List<String> getModelIds() {
+ public String[] getModelIds() {
return mIds;
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
index 2b07339..149ecdd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapter.java
@@ -118,8 +118,13 @@
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
+ boolean enabled = mEnv.isDocumentEnabled(docMimeType, docFlags);
+ boolean selected = mEnv.isSelected(modelId);
+ if (!enabled) {
+ assert(!selected);
+ }
+ holder.setEnabled(enabled);
holder.setSelected(mEnv.isSelected(modelId));
- holder.setEnabled(mEnv.isDocumentEnabled(docMimeType, docFlags));
mEnv.onBindDocumentHolder(holder, cursor);
}
@@ -135,8 +140,8 @@
Log.d(TAG, "Updating model with hidden ids: " + mHiddenIds);
}
- List<String> modelIds = model.getModelIds();
- mModelIds = new ArrayList<>(modelIds.size());
+ String[] modelIds = model.getModelIds();
+ mModelIds = new ArrayList<>(modelIds.length);
for (String id : modelIds) {
if (!mHiddenIds.contains(id)) {
mModelIds.add(id);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index d74121e5..d5327f91 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -245,10 +245,6 @@
return (flags & Document.FLAG_SUPPORTS_RENAME) != 0;
}
- public boolean isGridTitlesHidden() {
- return (flags & Document.FLAG_DIR_HIDE_GRID_TITLES) != 0;
- }
-
public boolean isArchive() {
return (flags & Document.FLAG_ARCHIVE) != 0;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 3eaf10a..3960475 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -55,7 +55,7 @@
private static final int VERSION_DROP_TYPE = 2;
// The values of these constants determine the sort order of various roots in the RootsFragment.
- @IntDef(flag = true, value = {
+ @IntDef(flag = false, value = {
TYPE_IMAGES,
TYPE_VIDEO,
TYPE_AUDIO,
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
index ad48a70..9ed2abf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
@@ -299,18 +299,15 @@
if ((src.flags & Document.FLAG_SUPPORTS_COPY) != 0) {
try {
if (DocumentsContract.copyDocument(getClient(src), src.derivedUri,
- dstDirInfo.derivedUri) == null) {
- throw new ResourceException("Provider side copy failed for document %s.",
- src.derivedUri);
+ dstDirInfo.derivedUri) != null) {
+ return;
}
- } catch (ResourceException e) {
- throw e;
} catch (RemoteException | RuntimeException e) {
- throw new ResourceException(
- "Provider side copy failed for document %s due to an exception.",
- src.derivedUri, e);
+ Log.e(TAG, "Provider side copy failed for: " + src.derivedUri
+ + " due to an exception: " + e);
}
- return;
+ // If optimized copy fails, then fallback to byte-by-byte copy.
+ if (DEBUG) Log.d(TAG, "Fallback to byte-by-byte copy for: " + src.derivedUri);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
index dc39235..aaa7596 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
@@ -16,6 +16,7 @@
package com.android.documentsui.services;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE;
import android.app.Notification;
@@ -24,6 +25,7 @@
import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
+import android.util.Log;
import com.android.documentsui.R;
import com.android.documentsui.model.DocumentInfo;
@@ -34,6 +36,8 @@
// TODO: Stop extending CopyJob.
final class MoveJob extends CopyJob {
+ private static final String TAG = "MoveJob";
+
final DocumentInfo mSrcParent;
/**
@@ -68,7 +72,7 @@
@Override
public Notification getProgressNotification() {
- return getProgressNotification(R.string.copy_preparing);
+ return getProgressNotification(R.string.copy_remaining);
}
@Override
@@ -89,16 +93,15 @@
try {
if (DocumentsContract.moveDocument(getClient(src), src.derivedUri,
srcParent != null ? srcParent.derivedUri : mSrcParent.derivedUri,
- dest.derivedUri) == null) {
- throw new ResourceException("Provider side move failed for document %s.",
- src.derivedUri);
+ dest.derivedUri) != null) {
+ return;
}
- } catch (RuntimeException | RemoteException e) {
- throw new ResourceException(
- "Provider side move failed for document %s due to an exception.",
- src.derivedUri, e);
+ } catch (RemoteException | RuntimeException e) {
+ Log.e(TAG, "Provider side move failed for: " + src.derivedUri
+ + " due to an exception: " + e);
}
- return;
+ // If optimized move fails, then fallback to byte-by-byte copy.
+ if (DEBUG) Log.d(TAG, "Fallback to byte-by-byte move for: " + src.derivedUri);
}
}
diff --git a/packages/DocumentsUI/tests/Android.mk b/packages/DocumentsUI/tests/Android.mk
index b65ac98..3983f78 100644
--- a/packages/DocumentsUI/tests/Android.mk
+++ b/packages/DocumentsUI/tests/Android.mk
@@ -7,8 +7,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target ub-uiautomator
+LOCAL_JAVA_LIBRARIES := android-support-v4 android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator
LOCAL_PACKAGE_NAME := DocumentsUITests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
diff --git a/packages/DocumentsUI/tests/AndroidManifest.xml b/packages/DocumentsUI/tests/AndroidManifest.xml
index a312427..b986285 100644
--- a/packages/DocumentsUI/tests/AndroidManifest.xml
+++ b/packages/DocumentsUI/tests/AndroidManifest.xml
@@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.documentsui.tests">
+ <uses-permission android:name="android.permission.INTERNET" />
+
<application>
<uses-library android:name="android.test.runner" />
<provider
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
index af478ea..16ed2d9 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
@@ -106,13 +106,15 @@
return createDocument(root.documentId, mimeType, name);
}
- public Uri createDocumentWithFlags(String documentId, String mimeType, String name, int flags)
+ public Uri createDocumentWithFlags(String documentId, String mimeType, String name, int flags,
+ String... streamTypes)
throws RemoteException {
Bundle in = new Bundle();
in.putInt(StubProvider.EXTRA_FLAGS, flags);
in.putString(StubProvider.EXTRA_PARENT_ID, documentId);
in.putString(Document.COLUMN_MIME_TYPE, mimeType);
in.putString(Document.COLUMN_DISPLAY_NAME, name);
+ in.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes));
Bundle out = mClient.call("createDocumentWithFlags", null, in);
Uri uri = out.getParcelable(DocumentsContract.EXTRA_URI);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
deleted file mode 100644
index 79d7887..0000000
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DownloadsActivityUiTest.java
+++ /dev/null
@@ -1,102 +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.documentsui;
-
-import static com.android.documentsui.StubProvider.ROOT_0_ID;
-
-import android.content.Intent;
-import android.os.RemoteException;
-import android.provider.DocumentsContract;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.Until;
-import android.test.suitebuilder.annotation.LargeTest;
-
-@LargeTest
-public class DownloadsActivityUiTest extends ActivityTest<DownloadsActivity> {
-
- public DownloadsActivityUiTest() {
- super(DownloadsActivity.class);
- }
-
- @Override
- void launchActivity() {
- final Intent intent = new Intent(DocumentsContract.ACTION_MANAGE_ROOT);
- intent.setDataAndType(rootDir0.getUri(), DocumentsContract.Root.MIME_TYPE_ITEM);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- setActivityIntent(intent);
- getActivity(); // Launch the activity.
- }
-
- @Override
- void initTestFiles() throws RemoteException {
- mDocsHelper.createDocument(rootDir0, "text/plain", "file0.log");
- mDocsHelper.createDocument(rootDir0, "image/png", "file1.png");
- mDocsHelper.createDocument(rootDir0, "text/csv", "file2.csv");
- }
-
- public void testWindowTitle() throws Exception {
- initTestFiles();
-
- bots.main.assertWindowTitle(ROOT_0_ID);
- }
-
- public void testFilesListed() throws Exception {
- initTestFiles();
-
- bots.directory.assertDocumentsPresent("file0.log", "file1.png", "file2.csv");
- }
-
- public void testFilesList_LiveUpdate() throws Exception {
- initTestFiles();
-
- mDocsHelper.createDocument(rootDir0, "yummers/sandwich", "Ham & Cheese.sandwich");
-
- bots.directory.waitForDocument("Ham & Cheese.sandwich");
- bots.directory.assertDocumentsPresent(
- "file0.log", "file1.png", "file2.csv", "Ham & Cheese.sandwich");
- }
-
- public void testDeleteDocument() throws Exception {
- initTestFiles();
-
- bots.directory.clickDocument("file1.png");
- device.waitForIdle();
- bots.main.menuDelete().click();
-
- bots.directory.waitForDeleteSnackbar();
- bots.directory.assertDocumentsAbsent("file1.png");
-
- bots.directory.waitForDeleteSnackbarGone();
- bots.directory.assertDocumentsAbsent("file1.png");
- }
-
- public void testSupportsShare() throws Exception {
- initTestFiles();
-
- bots.directory.clickDocument("file1.png");
- device.waitForIdle();
- assertNotNull(bots.main.menuShare());
- }
-
- public void testClosesOnBack() throws Exception {
- DownloadsActivity activity = getActivity();
- device.pressBack();
- device.wait(Until.gone(By.text(ROOT_0_ID)), TIMEOUT); // wait for the window to go away
- assertTrue(activity.isDestroyed());
- }
-}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 498471e..e4afc3d 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -19,9 +19,17 @@
import static com.android.documentsui.StubProvider.ROOT_0_ID;
import static com.android.documentsui.StubProvider.ROOT_1_ID;
+import android.app.DownloadManager;
+import android.app.DownloadManager.Request;
+import android.content.Context;
+import android.net.Uri;
import android.os.RemoteException;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiObject;
import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import com.android.documentsui.model.RootInfo;
@@ -71,11 +79,11 @@
bots.directory.assertDocumentsPresent("file0.log", "file1.png", "file2.csv");
}
- public void testLoadsHomeDirectoryByDefault() throws Exception {
+ public void testLoadsDownloadsDirectoryByDefault() throws Exception {
initTestFiles();
device.waitForIdle();
- bots.main.assertWindowTitle("Documents");
+ bots.main.assertWindowTitle("Downloads");
}
public void testRootClickSetsWindowTitle() throws Exception {
@@ -139,6 +147,7 @@
}
// Tests that pressing tab switches focus between the roots and directory listings.
+ @Suppress
public void testKeyboard_tab() throws Exception {
bots.main.pressKey(KeyEvent.KEYCODE_TAB);
bots.roots.assertHasFocus();
@@ -147,6 +156,7 @@
}
// Tests that arrow keys do not switch focus away from the dir list.
+ @Suppress
public void testKeyboard_arrowsDirList() throws Exception {
for (int i = 0; i < 10; i++) {
bots.main.pressKey(KeyEvent.KEYCODE_DPAD_LEFT);
@@ -170,4 +180,39 @@
bots.roots.assertHasFocus();
}
}
+
+ // We don't really need to test the entirety of download support
+ // since downloads is (almost) just another provider.
+ @Suppress
+ public void testDownload_Queued() throws Exception {
+ DownloadManager dm = (DownloadManager) context.getSystemService(
+ Context.DOWNLOAD_SERVICE);
+ // This downloads ends up being queued (because DNS can't be resolved).
+ // We'll still see an entry in the downloads UI with a "Queued" label.
+ dm.enqueue(new Request(Uri.parse("http://hammychamp.toodles")));
+
+ bots.roots.openRoot("Downloads");
+ bots.directory.assertDocumentsPresent("Queued");
+ }
+
+ @Suppress
+ public void testDownload_RetryUnsuccessful() throws Exception {
+ DownloadManager dm = (DownloadManager) context.getSystemService(
+ Context.DOWNLOAD_SERVICE);
+ // This downloads fails! But it'll still show up.
+ dm.enqueue(new Request(Uri.parse("http://www.google.com/hamfancy")));
+
+ bots.roots.openRoot("Downloads");
+ UiObject doc = bots.directory.findDocument("Unsuccessful");
+ doc.waitForExists(TIMEOUT);
+
+ int toolType = Configurator.getInstance().getToolType();
+ Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
+ doc.click();
+ Configurator.getInstance().setToolType(toolType);
+
+ assertTrue(bots.main.findDownloadRetryDialog().exists());
+
+ device.pressBack(); // to clear the dialog.
+ }
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
index 5f33b32..9a06807 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RenameDocumentUiTest.java
@@ -16,9 +16,8 @@
package com.android.documentsui;
-import static com.android.documentsui.StubProvider.ROOT_0_ID;
-
import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
@LargeTest
public class RenameDocumentUiTest extends ActivityTest<FilesActivity> {
@@ -73,6 +72,7 @@
device.pressBack();
}
+ @Suppress
public void testRenameFile_OkButton() throws Exception {
bots.directory.selectDocument(fileName1);
bots.main.openOverflowMenu();
@@ -101,6 +101,7 @@
bots.directory.assertDocumentsCount(4);
}
+ @Suppress
public void testRenameFile_Cancel() throws Exception {
bots.directory.selectDocument(fileName1);
bots.main.openOverflowMenu();
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java
index 73c1c5f..621410a 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java
@@ -20,6 +20,7 @@
import static com.android.documentsui.StubProvider.ROOT_1_ID;
import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
@LargeTest
public class RootsUiTest extends ActivityTest<FilesActivity> {
@@ -44,6 +45,7 @@
assertDefaultContentOfTestDir0();
}
+ @Suppress
public void testRootChanged_ClearSelection() throws Exception {
bots.directory.selectDocument(fileName1);
bots.main.assertInActionMode(true);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
index 8c3fd90..a9451a6 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
@@ -20,6 +20,7 @@
import static com.android.documentsui.StubProvider.ROOT_1_ID;
import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
@LargeTest
public class SearchViewUiTest extends ActivityTest<FilesActivity> {
@@ -28,6 +29,7 @@
super(FilesActivity.class);
}
+ @Suppress
public void testSearchView_ExpandsOnClick() throws Exception {
bots.main.openSearchView();
bots.main.assertSearchTextFiledAndIcon(true, false);
@@ -41,6 +43,7 @@
bots.main.assertSearchTextFiledAndIcon(false, true);
}
+ @Suppress
public void testSearchView_ClearsTextOnBack() throws Exception {
String query = "file2";
bots.main.openSearchView();
@@ -51,6 +54,7 @@
bots.main.assertSearchTextFiledAndIcon(false, true);
}
+ @Suppress
public void testSearch_ResultsFound() throws Exception {
initTestFiles();
assertDefaultContentOfTestDir0();
@@ -68,6 +72,7 @@
bots.main.assertSearchTextField(false, query);
}
+ @Suppress
public void testSearchResultsFound_ClearsOnBack() throws Exception {
initTestFiles();
assertDefaultContentOfTestDir0();
@@ -82,6 +87,7 @@
assertDefaultContentOfTestDir0();
}
+ @Suppress
public void testSearch_NoResults() throws Exception {
initTestFiles();
assertDefaultContentOfTestDir0();
@@ -101,6 +107,7 @@
bots.main.assertSearchTextField(false, query);
}
+ @Suppress
public void testSearchNoResults_ClearsOnBack() throws Exception {
initTestFiles();
assertDefaultContentOfTestDir0();
@@ -116,6 +123,7 @@
assertDefaultContentOfTestDir0();
}
+ @Suppress
public void testSearchResultsFound_ClearsOnDirectoryChange() throws Exception {
initTestFiles();
assertDefaultContentOfTestDir0();
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 2527650..f71ce5d 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -518,28 +518,29 @@
String rootId = extras.getString(EXTRA_PARENT_ID);
String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
String name = extras.getString(Document.COLUMN_DISPLAY_NAME);
+ List<String> streamTypes = extras.getStringArrayList(EXTRA_STREAM_TYPES);
int flags = extras.getInt(EXTRA_FLAGS);
Bundle out = new Bundle();
String documentId = null;
try {
- documentId = createDocument(rootId, mimeType, name, flags);
+ documentId = createDocument(rootId, mimeType, name, flags, streamTypes);
Uri uri = DocumentsContract.buildDocumentUri(mAuthority, documentId);
out.putParcelable(DocumentsContract.EXTRA_URI, uri);
} catch (FileNotFoundException e) {
- Log.d(TAG, "Cretaing document with flags failed" + name);
+ Log.d(TAG, "Creating document with flags failed" + name);
}
return out;
}
- public String createDocument(String parentId, String mimeType, String displayName, int flags)
- throws FileNotFoundException {
+ public String createDocument(String parentId, String mimeType, String displayName, int flags,
+ List<String> streamTypes) throws FileNotFoundException {
StubDocument parent = mStorage.get(parentId);
File file = createFile(parent, mimeType, displayName);
final StubDocument document = StubDocument.createDocumentWithFlags(file, mimeType, parent,
- flags);
+ flags, streamTypes);
mStorage.put(document.documentId, document);
Log.d(TAG, "Created document " + document.documentId);
notifyParentChanged(document.parentId);
@@ -787,8 +788,9 @@
}
public static StubDocument createDocumentWithFlags(
- File file, String mimeType, StubDocument parent, int flags) {
- return new StubDocument(file, mimeType, new ArrayList<String>(), flags, parent);
+ File file, String mimeType, StubDocument parent, int flags,
+ List<String> streamTypes) {
+ return new StubDocument(file, mimeType, streamTypes, flags, parent);
}
public static StubDocument createVirtualDocument(
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
index 11f5194..5b53caf 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/UiBot.java
@@ -173,6 +173,13 @@
return findObject("android:id/content", "android:id/text1");
}
+ public UiObject findDownloadRetryDialog() {
+ UiSelector selector = new UiSelector().text("Couldn't download");
+ UiObject title = mDevice.findObject(selector);
+ title.waitForExists(mTimeout);
+ return title;
+ }
+
public UiObject findDialogOkButton() {
UiObject object = findObject("android:id/content", "android:id/button1");
object.waitForExists(mTimeout);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java
index adc8141..7ab51ba2 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java
@@ -68,8 +68,8 @@
// Tests that the item count is correct.
public void testHide_ItemCount() {
- List<String> ids = mModel.getModelIds();
- mAdapter.hide(ids.get(0), ids.get(1));
+ String[] ids = mModel.getModelIds();
+ mAdapter.hide(ids[0], ids[1]);
assertEquals(mModel.getItemCount() - 2, mAdapter.getItemCount());
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
index 4b0bc41..c6ad511 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java
@@ -107,7 +107,7 @@
assertTrue(model.isEmpty());
assertEquals(0, model.getItemCount());
- assertEquals(0, model.getModelIds().size());
+ assertEquals(0, model.getModelIds().length);
}
// Tests that the item count is correct.
@@ -165,10 +165,10 @@
// Tests the base case for Model.getItem.
public void testGetItem() {
- List<String> ids = model.getModelIds();
- assertEquals(ITEM_COUNT, ids.size());
+ String[] ids = model.getModelIds();
+ assertEquals(ITEM_COUNT, ids.length);
for (int i = 0; i < ITEM_COUNT; ++i) {
- Cursor c = model.getItem(ids.get(i));
+ Cursor c = model.getItem(ids[i]);
assertEquals(i, c.getPosition());
}
}
@@ -292,14 +292,14 @@
r.sortOrder = State.SORT_ORDER_LAST_MODIFIED;
model.update(r);
- List<String> ids = model.getModelIds();
+ String[] ids = model.getModelIds();
// Check that all items were accounted for
- assertEquals(ITEM_COUNT + DL_COUNT, ids.size());
+ assertEquals(ITEM_COUNT + DL_COUNT, ids.length);
// Check that active downloads are sorted to the top.
for (int i = 0; i < DL_COUNT; i++) {
- assertTrue(currentDownloads.contains(ids.get(i)));
+ assertTrue(currentDownloads.contains(ids[i]));
}
}
@@ -316,11 +316,11 @@
}
private Selection positionToSelection(int... positions) {
- List<String> ids = model.getModelIds();
+ String[] ids = model.getModelIds();
Selection s = new Selection();
// Construct a selection of the given positions.
for (int p: positions) {
- s.add(ids.get(p));
+ s.add(ids[p]);
}
return s;
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/services/CopyJobTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/CopyJobTest.java
index 543396e..bb7c01a 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/CopyJobTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/CopyJobTest.java
@@ -16,6 +16,10 @@
package com.android.documentsui.services;
+import static com.google.common.collect.Lists.newArrayList;
+
+import android.net.Uri;
+import android.provider.DocumentsContract.Document;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.documentsui.model.DocumentInfo;
@@ -38,6 +42,21 @@
runCopyVirtualNonTypedFileTest();
}
+ public void testCopy_BackendSideVirtualTypedFile_Fallback() throws Exception {
+ mDocs.assertChildCount(mDestRoot, 0);
+
+ Uri testFile = mDocs.createDocumentWithFlags(
+ mSrcRoot.documentId, "virtual/mime-type", "tokyo.sth",
+ Document.FLAG_VIRTUAL_DOCUMENT | Document.FLAG_SUPPORTS_COPY
+ | Document.FLAG_SUPPORTS_MOVE, "application/pdf");
+
+ createJob(newArrayList(testFile)).run();
+
+ mJobListener.waitForFinished();
+ mDocs.assertChildCount(mDestRoot, 1);
+ mDocs.assertHasFile(mDestRoot, "tokyo.sth.pdf"); // Copy should convert file to PDF.
+ }
+
public void testCopyEmptyDir() throws Exception {
runCopyEmptyDirTest();
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/services/MoveJobTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/services/MoveJobTest.java
index 749264a..24181d6 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/services/MoveJobTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/services/MoveJobTest.java
@@ -59,6 +59,21 @@
mDocs.assertChildCount(mSrcRoot, 1);
}
+ public void testMove_BackendSideVirtualTypedFile_Fallback() throws Exception {
+ Uri testFile = mDocs.createDocumentWithFlags(
+ mSrcRoot.documentId, "virtual/mime-type", "tokyo.sth",
+ Document.FLAG_VIRTUAL_DOCUMENT | Document.FLAG_SUPPORTS_COPY
+ | Document.FLAG_SUPPORTS_MOVE, "application/pdf");
+
+ createJob(newArrayList(testFile)).run();
+ mJobListener.waitForFinished();
+
+ // Should have failed, source not deleted. Moving by bytes for virtual files
+ // is not supported.
+ mDocs.assertChildCount(mDestRoot, 0);
+ mDocs.assertChildCount(mSrcRoot, 1);
+ }
+
public void testMoveEmptyDir() throws Exception {
runCopyEmptyDirTest();
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index 100b35c..519db66 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -15,18 +15,27 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="android.ext.services"
android:versionCode="1"
android:versionName="1"
coreApp="true">
<application android:label="@string/app_name"
- android:allowBackup="false"
android:forceDeviceEncrypted="true"
android:encryptionAware="true">
<library android:name="android.ext.services"/>
+ <service android:name=".notification.Ranker"
+ android:label="@string/notification_ranker"
+ android:permission="android.permission.BIND_NOTIFICATION_RANKER_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationRankerService" />
+ </intent-filter>
+ </service>
+
</application>
</manifest>
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 531e517..0763403 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -16,4 +16,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">Android Services Library</string>
+ <string name="notification_ranker">Android Notification Ranking Service</string>
</resources>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Ranker.java b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
new file mode 100644
index 0000000..0b2b1a4
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
@@ -0,0 +1,46 @@
+/*
+ * 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.ext.services.notification;
+
+import android.service.notification.NotificationRankerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+/**
+ * Class that provides an updatable ranker module for the notification manager..
+ */
+public final class Ranker extends NotificationRankerService {
+ private static final String TAG = "RocketRanker";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);;
+
+ @Override
+ public Adjustment onNotificationEnqueued(StatusBarNotification sbn, int importance,
+ boolean user) {
+ if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey());
+ return null;
+ }
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
+ }
+
+ @Override
+ public void onListenerConnected() {
+ if (DEBUG) Log.i(TAG, "CONNECTED");
+ }
+}
\ No newline at end of file
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 409f6a7..b1d42e7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -519,6 +519,9 @@
@Override
public void showPromptReason(int reason) {
if (mCurrentSecuritySelection != SecurityMode.None) {
+ if (reason != PROMPT_REASON_NONE) {
+ Log.i(TAG, "Strong auth required, reason: " + reason);
+ }
getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index 044a061..0705214 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -276,7 +276,7 @@
static final int STATE_ERROR = 2;
final MtpDatabase mDatabase;
- int[] mOperationsSupported;
+ final int[] mOperationsSupported;
final Identifier mIdentifier;
final int[] mObjectHandles;
Date mLastNotified;
@@ -285,6 +285,8 @@
LoaderTask(MtpDatabase database, int[] operationsSupported, Identifier identifier,
int[] objectHandles) {
+ Preconditions.checkNotNull(operationsSupported);
+ Preconditions.checkNotNull(objectHandles);
mDatabase = database;
mOperationsSupported = operationsSupported;
mIdentifier = identifier;
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 203d6dc..8c73211 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -32,6 +32,7 @@
import android.media.MediaFile;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
+import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
@@ -40,8 +41,9 @@
import com.android.internal.util.Preconditions;
import java.io.FileNotFoundException;
-import java.io.IOException;
+import java.util.HashSet;
import java.util.Objects;
+import java.util.Set;
/**
* Database for MTP objects.
@@ -606,7 +608,7 @@
* @param deviceId Device to find documents.
* @return Identifier of found document or null.
*/
- public @Nullable Identifier getUnmappedDocumentsParent(int deviceId) {
+ @Nullable Identifier getUnmappedDocumentsParent(int deviceId) {
final String fromClosure =
TABLE_DOCUMENTS + " AS child INNER JOIN " +
TABLE_DOCUMENTS + " AS parent ON " +
@@ -643,6 +645,65 @@
}
}
+ /**
+ * Removes metadata except for data used by outgoingPersistedUriPermissions.
+ */
+ void cleanDatabase(Uri[] outgoingPersistedUris) {
+ mDatabase.beginTransaction();
+ try {
+ final Set<String> ids = new HashSet<>();
+ for (final Uri uri : outgoingPersistedUris) {
+ String documentId = DocumentsContract.getDocumentId(uri);
+ while (documentId != null) {
+ if (ids.contains(documentId)) {
+ break;
+ }
+ ids.add(documentId);
+ try (final Cursor cursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ strings(COLUMN_PARENT_DOCUMENT_ID),
+ SELECTION_DOCUMENT_ID,
+ strings(documentId),
+ null,
+ null,
+ null)) {
+ documentId = cursor.moveToNext() ? cursor.getString(0) : null;
+ }
+ }
+ }
+ deleteDocumentsAndRoots(
+ Document.COLUMN_DOCUMENT_ID + " NOT IN " + getIdList(ids), null);
+ mDatabase.setTransactionSuccessful();
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
+ int getLastBootCount() {
+ try (final Cursor cursor = mDatabase.query(
+ TABLE_LAST_BOOT_COUNT, strings(COLUMN_VALUE), null, null, null, null, null)) {
+ if (cursor.moveToNext()) {
+ return cursor.getInt(0);
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ void setLastBootCount(int value) {
+ Preconditions.checkArgumentNonnegative(value, "Boot count must not be negative.");
+ mDatabase.beginTransaction();
+ try {
+ final ContentValues values = new ContentValues();
+ values.put(COLUMN_VALUE, value);
+ mDatabase.delete(TABLE_LAST_BOOT_COUNT, null, null);
+ mDatabase.insert(TABLE_LAST_BOOT_COUNT, null, values);
+ mDatabase.setTransactionSuccessful();
+ } finally {
+ mDatabase.endTransaction();
+ }
+ }
+
private static class OpenHelper extends SQLiteOpenHelper {
public OpenHelper(Context context, int flags) {
super(context,
@@ -655,12 +716,14 @@
public void onCreate(SQLiteDatabase db) {
db.execSQL(QUERY_CREATE_DOCUMENTS);
db.execSQL(QUERY_CREATE_ROOT_EXTRA);
+ db.execSQL(QUERY_CREATE_LAST_BOOT_COUNT);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- db.execSQL("DROP TABLE " + TABLE_DOCUMENTS);
- db.execSQL("DROP TABLE " + TABLE_ROOT_EXTRA);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_DOCUMENTS);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_ROOT_EXTRA);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_LAST_BOOT_COUNT);
onCreate(db);
}
}
@@ -767,11 +830,21 @@
if (info.getFormat() == MtpConstants.FORMAT_ASSOCIATION) {
return DocumentsContract.Document.MIME_TYPE_DIR;
}
+
final String formatCodeMimeType = MediaFile.getMimeTypeForFormatCode(info.getFormat());
+ final String mediaFileMimeType = MediaFile.getMimeTypeForFile(info.getName());
+
+ // Format code can be mapped with multiple mime types, e.g. FORMAT_MPEG is mapped with
+ // audio/mp4 and video/mp4.
+ // As file extension contains more information than format code, returns mime type obtained
+ // from file extension if it is consistent with format code.
+ if (mediaFileMimeType != null &&
+ MediaFile.getFormatCode("", mediaFileMimeType) == info.getFormat()) {
+ return mediaFileMimeType;
+ }
if (formatCodeMimeType != null) {
return formatCodeMimeType;
}
- final String mediaFileMimeType = MediaFile.getMimeTypeForFile(info.getName());
if (mediaFileMimeType != null) {
return mediaFileMimeType;
}
@@ -818,4 +891,16 @@
}
return results;
}
+
+ private static String getIdList(Set<String> ids) {
+ String result = "(";
+ for (final String id : ids) {
+ if (result.length() > 1) {
+ result += ",";
+ }
+ result += id;
+ }
+ result += ")";
+ return result;
+ }
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index ff4b89f..6d98e34 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -30,7 +30,7 @@
* Class containing MtpDatabase constants.
*/
class MtpDatabaseConstants {
- static final int DATABASE_VERSION = 4;
+ static final int DATABASE_VERSION = 5;
static final String DATABASE_NAME = "database";
static final int FLAG_DATABASE_IN_MEMORY = 1;
@@ -48,6 +48,11 @@
static final String TABLE_ROOT_EXTRA = "RootExtra";
/**
+ * Table containing last boot count.
+ */
+ static final String TABLE_LAST_BOOT_COUNT = "LastBootCount";
+
+ /**
* 'FROM' closure of joining TABLE_DOCUMENTS and TABLE_ROOT_EXTRA.
*/
static final String JOIN_ROOTS = createJoinFromClosure(
@@ -62,7 +67,13 @@
static final String COLUMN_PARENT_DOCUMENT_ID = "parent_document_id";
static final String COLUMN_DOCUMENT_TYPE = "document_type";
static final String COLUMN_ROW_STATE = "row_state";
- static final String COLUMN_MAPPING_KEY = "column_mapping_key";
+ static final String COLUMN_MAPPING_KEY = "mapping_key";
+
+ /**
+ * Value for TABLE_LAST_BOOT_COUNT.
+ * Type: INTEGER
+ */
+ static final String COLUMN_VALUE = "value";
/**
* The state represents that the row has a valid object handle.
@@ -133,6 +144,9 @@
Root.COLUMN_CAPACITY_BYTES + " INTEGER," +
Root.COLUMN_MIME_TYPES + " TEXT NOT NULL);";
+ static final String QUERY_CREATE_LAST_BOOT_COUNT =
+ "CREATE TABLE " + TABLE_LAST_BOOT_COUNT + " (value INTEGER NOT NULL);";
+
/**
* Map for columns names to provide DocumentContract.Root compatible columns.
* @see SQLiteQueryBuilder#setProjectionMap(Map)
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index c031f34..58ff401 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -17,6 +17,7 @@
package com.android.mtp;
import android.content.ContentResolver;
+import android.content.UriPermission;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
@@ -25,6 +26,7 @@
import android.media.MediaFile;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
+import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
@@ -33,6 +35,7 @@
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
+import android.provider.Settings;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -42,7 +45,9 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeoutException;
/**
* DocumentsProvider for MTP devices.
@@ -95,6 +100,21 @@
mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
mAppFuse = new AppFuse(TAG, new AppFuseCallback());
mIntentSender = new ServiceIntentSender(getContext());
+
+ // Check boot count and cleans database if it's first time to launch MtpDocumentsProvider
+ // after booting.
+ final int bootCount = Settings.Global.getInt(mResolver, Settings.Global.BOOT_COUNT, -1);
+ final int lastBootCount = mDatabase.getLastBootCount();
+ if (bootCount != -1 && bootCount != lastBootCount) {
+ mDatabase.setLastBootCount(bootCount);
+ final List<UriPermission> permissions = mResolver.getOutgoingPersistedUriPermissions();
+ final Uri[] uris = new Uri[permissions.size()];
+ for (int i = 0; i < permissions.size(); i++) {
+ uris[i] = permissions.get(i).getUri();
+ }
+ mDatabase.cleanDatabase(uris);
+ }
+
// TODO: Mount AppFuse on demands.
try {
mAppFuse.mount(getContext().getSystemService(StorageManager.class));
@@ -122,6 +142,7 @@
mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
mAppFuse = new AppFuse(TAG, new AppFuseCallback());
mIntentSender = intentSender;
+
// TODO: Mount AppFuse on demands.
try {
mAppFuse.mount(storageManager);
@@ -405,7 +426,7 @@
closeDeviceInternal(id);
}
mRootScanner.pause();
- } catch (InterruptedException|IOException e) {
+ } catch (InterruptedException | IOException | TimeoutException e) {
// It should fail unit tests by throwing runtime exception.
throw new RuntimeException(e);
} finally {
@@ -440,12 +461,9 @@
if (DEBUG) {
Log.d(TAG, "Close device " + deviceId);
}
- getDeviceToolkit(deviceId).mDocumentLoader.close();
+ getDeviceToolkit(deviceId).close();
mDeviceToolkits.remove(deviceId);
mMtpManager.closeDevice(deviceId);
- if (mDeviceToolkits.size() == 0) {
- mRootScanner.pause();
- }
}
private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException {
@@ -496,7 +514,7 @@
return cursor;
}
- private static class DeviceToolkit {
+ private static class DeviceToolkit implements AutoCloseable {
public final PipeManager mPipeManager;
public final DocumentLoader mDocumentLoader;
public final MtpDeviceRecord mDeviceRecord;
@@ -509,6 +527,12 @@
mDocumentLoader = new DocumentLoader(record, manager, resolver, database);
mDeviceRecord = record;
}
+
+ @Override
+ public void close() throws InterruptedException {
+ mPipeManager.close();
+ mDocumentLoader.close();
+ }
}
private class AppFuseCallback implements AppFuse.Callback {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 1966e61..0202343 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -143,7 +143,12 @@
throws IOException {
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
- return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
+ final int[] handles =
+ device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
+ if (handles == null) {
+ throw new IOException("Failed to fetch object handles.");
+ }
+ return handles;
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
index 73042c4..c10a65e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
@@ -26,8 +26,14 @@
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
class PipeManager {
+ /**
+ * Milliseconds we wait for background thread when pausing.
+ */
+ private final static long AWAIT_TERMINATION_TIMEOUT = 2000;
+
final ExecutorService mExecutor;
final MtpDatabase mDatabase;
@@ -203,7 +209,8 @@
}
}
- void close() {
- mExecutor.shutdown();
+ boolean close() throws InterruptedException {
+ mExecutor.shutdownNow();
+ return mExecutor.awaitTermination(AWAIT_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS);
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index 2f66c5c..2e9133b 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -27,6 +27,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
final class RootScanner {
/**
@@ -95,15 +96,19 @@
* Stops background thread and wait for its termination.
* @throws InterruptedException
*/
- synchronized void pause() throws InterruptedException {
+ synchronized void pause() throws InterruptedException, TimeoutException {
if (mExecutor == null) {
return;
}
mExecutor.shutdownNow();
- if (!mExecutor.awaitTermination(AWAIT_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS)) {
- Log.e(MtpDocumentsProvider.TAG, "Failed to terminate RootScanner's background thread.");
+ try {
+ if (!mExecutor.awaitTermination(AWAIT_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException(
+ "Timeout for terminating RootScanner's background thread.");
+ }
+ } finally {
+ mExecutor = null;
}
- mExecutor = null;
}
/**
@@ -173,6 +178,9 @@
}
mFirstScanCompleted.countDown();
pollingCount++;
+ if (devices.length == 0) {
+ break;
+ }
try {
// Use SHORT_POLLING_PERIOD for the first SHORT_POLLING_TIMES because it is
// more likely to add new root just after the device is added.
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index f9e8225..b74069a 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -17,8 +17,11 @@
package com.android.mtp;
import android.database.Cursor;
+import android.media.MediaFile;
+import android.media.MediaFile.MediaFileType;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
+import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
@@ -26,6 +29,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import java.io.FileNotFoundException;
+import java.util.Arrays;
import static android.provider.DocumentsContract.Document.*;
import static com.android.mtp.MtpDatabase.strings;
@@ -1023,6 +1027,99 @@
assertFalse(mDatabase.getMapper().stopAddingDocuments(null));
}
+ public void testSetBootCount() {
+ assertEquals(0, mDatabase.getLastBootCount());
+ mDatabase.setLastBootCount(10);
+ assertEquals(10, mDatabase.getLastBootCount());
+ try {
+ mDatabase.setLastBootCount(-1);
+ fail();
+ } catch (IllegalArgumentException e) {}
+ }
+
+ public void testCleanDatabase() throws FileNotFoundException {
+ // Add tree.
+ addTestDevice();
+ addTestStorage("1");
+ mDatabase.getMapper().startAddingDocuments("2");
+ mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
+ createDocument(100, "apple.txt", MtpConstants.FORMAT_TEXT, 1024),
+ createDocument(101, "orange.txt", MtpConstants.FORMAT_TEXT, 1024),
+ });
+ mDatabase.getMapper().stopAddingDocuments("2");
+
+ // Disconnect the device.
+ mDatabase.getMapper().startAddingDocuments(null);
+ mDatabase.getMapper().stopAddingDocuments(null);
+
+ // Clean database.
+ mDatabase.cleanDatabase(new Uri[] {
+ DocumentsContract.buildDocumentUri(MtpDocumentsProvider.AUTHORITY, "3")
+ });
+
+ // Add tree again.
+ addTestDevice();
+ addTestStorage("1");
+ mDatabase.getMapper().startAddingDocuments("2");
+ mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
+ createDocument(100, "apple.txt", MtpConstants.FORMAT_TEXT, 1024),
+ createDocument(101, "orange.txt", MtpConstants.FORMAT_TEXT, 1024),
+ });
+ mDatabase.getMapper().stopAddingDocuments("2");
+
+ try (final Cursor cursor = mDatabase.queryChildDocuments(
+ strings(COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME), "2")) {
+ assertEquals(2, cursor.getCount());
+
+ // Persistent uri uses the same ID.
+ cursor.moveToNext();
+ assertEquals("3", cursor.getString(0));
+ assertEquals("apple.txt", cursor.getString(1));
+
+ // Others does not.
+ cursor.moveToNext();
+ assertEquals("5", cursor.getString(0));
+ assertEquals("orange.txt", cursor.getString(1));
+ }
+ }
+
+ public void testFormatCodeForMpeg() throws FileNotFoundException {
+ addTestDevice();
+ addTestStorage("1");
+ mDatabase.getMapper().startAddingDocuments("2");
+ mDatabase.getMapper().putChildDocuments(0, "2", OPERATIONS_SUPPORTED, new MtpObjectInfo[] {
+ createDocument(100, "audio.m4a", MtpConstants.FORMAT_MPEG, 1000),
+ createDocument(101, "video.m4v", MtpConstants.FORMAT_MPEG, 1000),
+ createDocument(102, "unknown.mp4", MtpConstants.FORMAT_MPEG, 1000),
+ createDocument(103, "inconsistent.txt", MtpConstants.FORMAT_MPEG, 1000),
+ createDocument(104, "noext", MtpConstants.FORMAT_UNDEFINED, 1000),
+ });
+ mDatabase.getMapper().stopAddingDocuments("2");
+ try (final Cursor cursor = mDatabase.queryChildDocuments(
+ strings(COLUMN_DISPLAY_NAME, COLUMN_MIME_TYPE),
+ "2")) {
+ assertEquals(5, cursor.getCount());
+ cursor.moveToNext();
+ assertEquals("audio.m4a", cursor.getString(0));
+ assertEquals("audio/mp4", cursor.getString(1));
+ cursor.moveToNext();
+ assertEquals("video.m4v", cursor.getString(0));
+ assertEquals("video/mp4", cursor.getString(1));
+ cursor.moveToNext();
+ // Assume that the file is video as we don't have any hints to find out if the file is
+ // video or audio.
+ assertEquals("unknown.mp4", cursor.getString(0));
+ assertEquals("video/mp4", cursor.getString(1));
+ // Don't return mime type that is inconsistent with format code.
+ cursor.moveToNext();
+ assertEquals("inconsistent.txt", cursor.getString(0));
+ assertEquals("video/mp4", cursor.getString(1));
+ cursor.moveToNext();
+ assertEquals("noext", cursor.getString(0));
+ assertEquals("application/octet-stream", cursor.getString(1));
+ }
+ }
+
private void addTestDevice() throws FileNotFoundException {
TestUtil.addTestDevice(mDatabase);
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 3dfa4ad..afcb457 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -262,11 +262,9 @@
null));
{
mProvider.openDevice(0);
- mProvider.resumeRootScanner();
mResolver.waitForNotification(ROOTS_URI, 1);
mProvider.openDevice(1);
- mProvider.resumeRootScanner();
mResolver.waitForNotification(ROOTS_URI, 2);
final Cursor cursor = mProvider.queryRoots(null);
@@ -596,7 +594,7 @@
0,
"Device A",
"device key",
- true /* unopened */,
+ true /* opened */,
new MtpRoot[] {
new MtpRoot(
0 /* deviceId */,
@@ -606,7 +604,7 @@
2048 /* total space */,
"" /* no volume identifier */)
},
- null,
+ OPERATIONS_SUPPORTED,
null));
final String[] names = strings("Directory A", "Directory B", "Directory C");
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
index 919ac39..a08d9ee 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/PipeManagerTest.java
@@ -45,6 +45,12 @@
mPipeManager = new PipeManager(mDatabase, mExecutor);
}
+ @Override
+ protected void tearDown() throws Exception {
+ assertTrue(mPipeManager.close());
+ mDatabase.close();
+ }
+
public void testReadDocument_basic() throws Exception {
mtpManager.setImportFileBytes(0, 1, HELLO_BYTES);
final ParcelFileDescriptor descriptor = mPipeManager.readDocument(
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 5e0ee1e..5171bd2 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -106,7 +106,7 @@
mDevices.put(
deviceId,
new MtpDeviceRecord(device.deviceId, device.name, device.deviceKey, false,
- device.roots, null, null));
+ device.roots, device.operationsSupported, device.eventsSupported));
}
@Override
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index af9d251..1bdb6d8 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -63,7 +63,7 @@
android:name=".ui.PrintActivity"
android:configChanges="screenSize|smallestScreenSize|orientation"
android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE"
- android:theme="@style/PrintActivity">
+ android:theme="@style/Theme.PrintActivity">
<intent-filter>
<action android:name="android.print.PRINT_DIALOG" />
<category android:name="android.intent.category.DEFAULT" />
@@ -74,7 +74,14 @@
<activity
android:name=".ui.SelectPrinterActivity"
android:label="@string/all_printers_label"
- android:theme="@android:style/Theme.Material.Settings"
+ android:theme="@style/Theme.SelectPrinterActivity"
+ android:exported="false">
+ </activity>
+
+ <activity
+ android:name=".ui.AddPrinterActivity"
+ android:label="@string/print_add_printer"
+ android:theme="@style/Theme.AddPrinterActivity"
android:exported="false">
</activity>
diff --git a/packages/PrintSpooler/res/drawable/ic_add.xml b/packages/PrintSpooler/res/drawable/ic_add.xml
index 1442b1b..f728e7d 100644
--- a/packages/PrintSpooler/res/drawable/ic_add.xml
+++ b/packages/PrintSpooler/res/drawable/ic_add.xml
@@ -21,5 +21,5 @@
android:viewportHeight="24.0">
<path
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
- android:fillColor="#FFFFFF"/>
+ android:fillColor="?android:attr/colorAccent"/>
</vector>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/drawable/ic_print.xml b/packages/PrintSpooler/res/drawable/ic_print.xml
index dc6e0fb..e5e4d075 100644
--- a/packages/PrintSpooler/res/drawable/ic_print.xml
+++ b/packages/PrintSpooler/res/drawable/ic_print.xml
@@ -16,4 +16,4 @@
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@*android:drawable/ic_print"
- android:tint="@color/promoted_action_background_color" />
+ android:tint="?android:attr/colorAccent" />
diff --git a/packages/PrintSpooler/res/drawable/page_selector_background.xml b/packages/PrintSpooler/res/drawable/page_selector_background.xml
index 7f1da31..4d32328 100644
--- a/packages/PrintSpooler/res/drawable/page_selector_background.xml
+++ b/packages/PrintSpooler/res/drawable/page_selector_background.xml
@@ -22,14 +22,14 @@
android:state_selected="true">
<bitmap
android:src="@drawable/ic_check_circle"
- android:tint="@color/promoted_action_background_color">
+ android:tint="?android:attr/colorAccent">
</bitmap>
</item>
<item>
<bitmap
android:src="@drawable/ic_remove_circle"
- android:tint="@color/promoted_action_background_color">
+ android:tint="?android:attr/colorAccent">
</bitmap>
</item>
diff --git a/packages/PrintSpooler/res/drawable/print_button_background.xml b/packages/PrintSpooler/res/drawable/print_button_background.xml
index aec8474..ad16547 100644
--- a/packages/PrintSpooler/res/drawable/print_button_background.xml
+++ b/packages/PrintSpooler/res/drawable/print_button_background.xml
@@ -18,7 +18,7 @@
android:shape="oval">
<solid
- android:color="@color/promoted_action_background_color">
+ android:color="?android:attr/colorAccent">
</solid>
<size
diff --git a/packages/PrintSpooler/res/layout/add_printer_activity.xml b/packages/PrintSpooler/res/layout/add_printer_activity.xml
new file mode 100644
index 0000000..117bba1
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/add_printer_activity.xml
@@ -0,0 +1,29 @@
+<?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_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="16dip"
+ android:paddingBottom="16dip">
+
+ <ListView android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-16dip"
+ android:id="@android:id/list" />
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/add_printer_list_header.xml b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
new file mode 100644
index 0000000..ff342cb
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
@@ -0,0 +1,28 @@
+<?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="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
+
+ <TextView android:id="@+id/text"
+ style="?android:attr/listSeparatorTextViewStyle" />
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/add_printer_list_item.xml b/packages/PrintSpooler/res/layout/add_printer_list_item.xml
new file mode 100644
index 0000000..1a2e774
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/add_printer_list_item.xml
@@ -0,0 +1,46 @@
+<?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="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
+
+ <ImageView
+ android:layout_width="24dip"
+ android:layout_height="24dip"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@null"
+ android:layout_marginEnd="16dip"
+ android:src="@drawable/ic_add" />
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:text="@string/print_add_printer" />
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/all_print_services_list_item.xml b/packages/PrintSpooler/res/layout/all_print_services_list_item.xml
new file mode 100644
index 0000000..a2471c6
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/all_print_services_list_item.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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="56dip">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:text="@string/all_services_title" />
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml b/packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml
new file mode 100644
index 0000000..73d09333
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml
@@ -0,0 +1,55 @@
+<?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="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="40dip"
+ android:layout_height="40dip"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@null" />
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:singleLine="true"
+ android:ellipsize="end" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:text="@string/enable_print_service" />
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml b/packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml
new file mode 100644
index 0000000..c13487a
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml
@@ -0,0 +1,54 @@
+<?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="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="40dip"
+ android:layout_height="40dip"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@null" />
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:singleLine="true"
+ android:ellipsize="end" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary" />
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index defbf8d..103c157 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -16,10 +16,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingStart="8dip"
- android:paddingEnd="8dip"
- android:minHeight="56dip"
+ android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ style="?android:attr/spinnerItemStyle"
android:orientation="horizontal"
android:gravity="start|center_vertical">
@@ -33,10 +31,9 @@
android:visibility="invisible">
</ImageView>
- <LinearLayout
+ <RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="vertical"
android:layout_marginStart="8dip"
android:duplicateParentState="true">
@@ -47,8 +44,6 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:singleLine="true"
android:ellipsize="end"
- android:textIsSelectable="false"
- android:gravity="top|start"
android:textColor="?android:attr/textColorPrimary"
android:duplicateParentState="true">
</TextView>
@@ -57,15 +52,15 @@
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_below="@id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:singleLine="true"
android:ellipsize="end"
- android:textIsSelectable="false"
android:visibility="gone"
android:textColor="?android:attr/textColorSecondary"
android:duplicateParentState="true">
</TextView>
- </LinearLayout>
+ </RelativeLayout>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
index 11fef2d..60f7088 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
@@ -16,13 +16,10 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
+ android:layout_height="?android:attr/listPreferredItemHeightSmall"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:textIsSelectable="false"
android:textColor="?android:attr/textColorPrimary"
android:paddingStart="20dip"
- android:paddingEnd="8dip"
- android:minHeight="56dip"
- android:orientation="horizontal"
+ style="?android:attr/spinnerItemStyle"
android:text="@string/destination_default_text"
android:gravity="start|center_vertical" />
diff --git a/packages/PrintSpooler/res/layout/printer_list_item.xml b/packages/PrintSpooler/res/layout/printer_list_item.xml
index 1209aa6..0784bab 100644
--- a/packages/PrintSpooler/res/layout/printer_list_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_list_item.xml
@@ -16,10 +16,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
+ android:layout_height="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:gravity="start|center_vertical">
@@ -28,18 +27,15 @@
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_gravity="center_vertical"
- android:layout_marginTop="8dip"
- android:layout_marginBottom="8dip"
android:duplicateParentState="true"
android:contentDescription="@null"
android:visibility="invisible">
</ImageView>
<RelativeLayout
- android:layout_width="0dip"
+ android:layout_width="fill_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
android:layout_marginStart="16dip"
android:duplicateParentState="true">
@@ -47,15 +43,9 @@
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textAppearance="?android:attr/textAppearanceListItem"
android:singleLine="true"
android:ellipsize="end"
- android:textIsSelectable="false"
- android:layout_alignParentTop="true"
- android:layout_alignParentStart="true"
- android:fadingEdge="horizontal"
- android:textAlignment="viewStart"
- android:textColor="?android:attr/textColorPrimary"
android:duplicateParentState="true">
</TextView>
@@ -64,30 +54,31 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
- android:layout_alignParentStart="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:singleLine="true"
android:ellipsize="end"
- android:textIsSelectable="false"
android:visibility="gone"
- android:textColor="?android:attr/textColorSecondary"
- android:textAlignment="viewStart"
android:duplicateParentState="true">
</TextView>
</RelativeLayout>
- <ImageView
+ <!-- wrapper for image view to increase the touch target size -->
+ <LinearLayout
android:id="@+id/more_info"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingLeft="16dip"
- android:contentDescription="@string/printer_info_desc"
- android:src="@drawable/ic_info"
- android:tint="?android:attr/colorControlNormal"
- android:tintMode="src_in"
+ android:layout_height="fill_parent"
android:visibility="gone">
- </ImageView>
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingLeft="16dip"
+ android:contentDescription="@string/printer_info_desc"
+ android:src="@drawable/ic_info"
+ android:tint="?android:attr/colorControlNormal"
+ android:tintMode="src_in" />
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 77c500a..564802a 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -22,11 +22,7 @@
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:scrollbarStyle="outsideOverlay"
- android:cacheColorHint="@android:color/transparent"
- android:scrollbarAlwaysDrawVerticalTrack="true" >
- </ListView>
+ android:layout_height="fill_parent" />
<FrameLayout
android:id="@+id/empty_print_state"
@@ -63,9 +59,18 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
- style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Horizontal">
+ style="?android:attr/progressBarStyleHorizontal">
</ProgressBar>
+ <Button
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="@string/print_add_printer"
+ android:textAllCaps="true" />
+
</LinearLayout>
</FrameLayout>
diff --git a/packages/PrintSpooler/res/menu/select_printer_activity.xml b/packages/PrintSpooler/res/menu/select_printer_activity.xml
index 15cc139..60dfdca 100644
--- a/packages/PrintSpooler/res/menu/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/menu/select_printer_activity.xml
@@ -26,12 +26,4 @@
android:imeOptions="actionSearch">
</item>
- <item
- android:id="@+id/action_add_printer"
- android:title="@string/print_add_printer"
- android:icon="@drawable/ic_add"
- android:showAsAction="ifRoom"
- android:alphabeticShortcut="a">
- </item>
-
</menu>
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index 57ba2b6..c8478f2 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Meer inligting oor hierdie drukker"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Sommige drukdienste is gedeaktiveer."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Kies drukdiens"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Sommige drukdienste is gedeaktiveer"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Soek tans vir drukkers"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Geen drukdienste is geaktiveer nie"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Geen drukkers gekry nie"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Kan nie drukkers byvoeg nie"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Kies om drukker by te voeg"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Kies om te aktiveer"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Geaktiveerde dienste"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Aanbevole dienste"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Gedeaktiveerde dienste"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Alle dienste"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Druk tans <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kanselleer tans <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Drukkerfout by <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index a2182fb..d4426cc 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"ተጨማሪ የዚህ አታሚ መረጃ"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"አንዳንድ የህትመት አገልግሎቶች ተሰናክለዋል።"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"የህትመት አገልግሎት ይምረጡ"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"አንዳንድ የህትመት አገልግሎቶች ተሰናክለዋል"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"አታሚዎችን በመፈለግ ላይ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ምንም የህትመት አገልግሎቶች አልነቁም"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ምንም አታሚዎች አልተገኙም"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"አታሚዎችን ማከል አልተቻለም"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"አታሚን ለማከል ይምረጡ"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"ለማንቃት ይምረጡ"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"የነቁ አገልግሎቶች"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"የሚመከሩ አገልግሎቶች"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"የተሰናከሉ አገልግሎቶች"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"ሁሉም አገልግሎቶች"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ን በማተም ላይ"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index eab1339..2e1b6d0 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -65,11 +65,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"مزيد من المعلومات حول هذه الطابعة"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"بعض خدمات الطباعة معطَّلة."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"اختر خدمة طباعة"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"بعض خدمات الطباعة معطَّلة"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"البحث عن طابعات"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"لم يتم تمكين أي خدمات طباعة"</string>
<string name="print_no_printers" msgid="4869403323900054866">"لم يتم العثور على طابعات"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"تعذرت إضافة طابعات"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"اختر لإضافة طابعة"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"حدد للتمكين"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"الخدمات الممكنة"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"الخدمات الموصى بها"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"الخدمات المعطَّلة"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"جميع الخدمات"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"جارٍ طباعة <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-az-rAZ/strings.xml b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
index bff477d..5490b84 100644
--- a/packages/PrintSpooler/res/values-az-rAZ/strings.xml
+++ b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Bu printer haqqında daha ətraflı məlumat"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Bəzi çap xidmətləri deaktiv edilib."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Çap xidmətini seçin"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Bəzi çap xidmətləri deaktiv edilib."</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printer axtarılır"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Heç bir çap xidməti aktiv edilməyib"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Heç bir printer tapılmadı"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Printerlər əlavə edilmədi"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Printer əlavə etmək üçün seçin"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Aktiv etmək üçün seçin"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Aktiv edilmiş xidmətlər"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Tövsiyə olunan xidmətlər"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Deaktiv edilmiş xidmətlər"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Bütün xidmətlər"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> çap edilir"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ləğv edilir"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printer xətası <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
index b28aa29..0574dae 100644
--- a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
+++ b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
@@ -62,11 +62,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Još informacija o ovom štampaču"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Neke usluge štampanja su onemogućene."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Izaberite uslugu štampanja"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Neke usluge štampanja su onemogućene"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Pretraga štampača"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nijedna usluga štampanja nije omogućena"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nije pronađen nijedan štampač"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Nije moguće dodati štampače"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Izaberite da biste dodali štampač"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Izaberite da biste omogućili"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Omogućene usluge"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Preporučene usluge"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Onemogućene usluge"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Sve usluge"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Štampa se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazuje se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Greška štampača <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index e8de8ea..88af8e4 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Още информация за този принтер"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Някои услуги за отпечатване са деактивирани."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Избиране на услуга за отпечатване"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Някои услуги за отпечатване са деактивирани"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Търсене на принтери"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Няма активирани услуги за отпечатване"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Няма намерени принтери"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Не могат да се добавят принтери"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Изберете, за да добавите принтер"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Изберете, за да активирате"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Активирани услуги"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Препоръчителни услуги"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Деактивирани услуги"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Всички услуги"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ се отпечатва"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-bn-rBD/strings.xml b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
index a1ca494..c61ef74 100644
--- a/packages/PrintSpooler/res/values-bn-rBD/strings.xml
+++ b/packages/PrintSpooler/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="4469836075319831821">"মুদ্রণ স্পোলার"</string>
+ <string name="app_label" msgid="4469836075319831821">"প্রিন্ট স্পোলার"</string>
<string name="more_options_button" msgid="2243228396432556771">"আরো বিকল্প"</string>
<string name="label_destination" msgid="9132510997381599275">"গন্তব্য"</string>
<string name="label_copies" msgid="3634531042822968308">"প্রতিলিপিগুলি"</string>
@@ -31,22 +31,22 @@
<string name="template_all_pages" msgid="3322235982020148762">"সমস্ত <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g> এর পরিসর"</string>
<string name="pages_range_example" msgid="8558694453556945172">"যেমন, ১—৫,৮,১১—১৩"</string>
- <string name="print_preview" msgid="8010217796057763343">"মুদ্রণ পূর্বরূপ"</string>
+ <string name="print_preview" msgid="8010217796057763343">"প্রিন্ট পূর্বরূপ"</string>
<string name="install_for_print_preview" msgid="6366303997385509332">"পূর্বরূপ দেখার জন্য PDF ভিউয়ার ইনস্টল করুন"</string>
- <string name="printing_app_crashed" msgid="854477616686566398">"মুদ্রণ অ্যাপ্লিকেশান ক্র্যাশ করছে"</string>
- <string name="generating_print_job" msgid="3119608742651698916">"মুদ্রণ কার্য তৈরি করা হচ্ছে"</string>
+ <string name="printing_app_crashed" msgid="854477616686566398">"প্রিন্ট অ্যাপ্লিকেশান ক্র্যাশ করছে"</string>
+ <string name="generating_print_job" msgid="3119608742651698916">"প্রিন্ট কার্য তৈরি করা হচ্ছে"</string>
<string name="save_as_pdf" msgid="5718454119847596853">"PDF হিসাবে সংরক্ষণ করুন"</string>
<string name="all_printers" msgid="5018829726861876202">"সমস্ত মুদ্রক…"</string>
- <string name="print_dialog" msgid="32628687461331979">"মুদ্রণ ডায়লগ"</string>
+ <string name="print_dialog" msgid="32628687461331979">"প্রিন্ট ডায়লগ"</string>
<string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
<string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="PAGE_COUNT">%2$d</xliff:g>টির মধ্যে <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> নম্বর পৃষ্ঠা"</string>
<string name="summary_template" msgid="8899734908625669193">"সারাংশ, <xliff:g id="COPIES">%1$s</xliff:g>টি অনুলিপি, কাগজের আকার <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
<string name="expand_handle" msgid="7282974448109280522">"প্রসারিত করার হ্যান্ডেল"</string>
<string name="collapse_handle" msgid="6886637989442507451">"সঙ্কুচিত করার হ্যান্ডেল"</string>
- <string name="print_button" msgid="645164566271246268">"মুদ্রণ করুন"</string>
+ <string name="print_button" msgid="645164566271246268">"প্রিন্ট করুন"</string>
<string name="savetopdf_button" msgid="2976186791686924743">"PDF হিসাবে সংরক্ষণ করুন"</string>
- <string name="print_options_expanded" msgid="6944679157471691859">"মুদ্রণ বিকল্প প্রসারিত হয়েছে"</string>
- <string name="print_options_collapsed" msgid="7455930445670414332">"মুদ্রণ বিকল্প সংকুচিত হয়েছে"</string>
+ <string name="print_options_expanded" msgid="6944679157471691859">"প্রিন্ট বিকল্প প্রসারিত হয়েছে"</string>
+ <string name="print_options_collapsed" msgid="7455930445670414332">"প্রিন্ট বিকল্প সংকুচিত হয়েছে"</string>
<string name="search" msgid="5421724265322228497">"অনুসন্ধান করুন"</string>
<string name="all_printers_label" msgid="3178848870161526399">"সমস্ত মুদ্রক"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"পরিষেবা যোগ করুন"</string>
@@ -61,12 +61,18 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"এই মুদ্রকটির বিষয়ে আরো তথ্য"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"কিছু মুদ্রণ পরিষেবা অক্ষম করা হয়েছে৷"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"মুদ্রণ পরিষেবা চয়ন করুন"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"কিছু মুদ্রণ পরিষেবা অক্ষম করা আছে"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"মুদ্রকগুলি অনুসন্ধান করা হচ্ছে"</string>
- <string name="print_no_print_services" msgid="8561247706423327966">"মুদ্রণ পরিষেবা সক্ষম নেই"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"প্রিন্ট পরিষেবা সক্ষম নেই"</string>
<string name="print_no_printers" msgid="4869403323900054866">"কোনো মুদ্রক পাওয়া যায়নি"</string>
- <string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> মুদ্রণ করা হচ্ছে"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"মুদ্রকগুলি যোগ করা যাবে না"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"মুদ্রক যোগ করতে নির্বাচন করুন"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"সক্ষম করতে নির্বাচন করুন"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"সক্ষম করা পরিষেবাগুলি"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"প্রস্তাবিত পরিষেবাগুলি"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"অক্ষম করা পরিষেবাগুলি"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"সমস্ত পরিষেবা"</string>
+ <string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> প্রিন্ট করা হচ্ছে"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-bs-rBA/strings.xml b/packages/PrintSpooler/res/values-bs-rBA/strings.xml
new file mode 100644
index 0000000..7465c3c
--- /dev/null
+++ b/packages/PrintSpooler/res/values-bs-rBA/strings.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4469836075319831821">"Štampanje na čekanju"</string>
+ <string name="more_options_button" msgid="2243228396432556771">"Više opcija"</string>
+ <string name="label_destination" msgid="9132510997381599275">"Odredište"</string>
+ <string name="label_copies" msgid="3634531042822968308">"Kopije"</string>
+ <string name="label_copies_summary" msgid="3861966063536529540">"Primjeraka:"</string>
+ <string name="label_paper_size" msgid="908654383827777759">"Veličina papira"</string>
+ <string name="label_paper_size_summary" msgid="5668204981332138168">"Veličina papira:"</string>
+ <string name="label_color" msgid="1108690305218188969">"U boji"</string>
+ <string name="label_duplex" msgid="5370037254347072243">"Dvostrano"</string>
+ <string name="label_orientation" msgid="2853142581990496477">"Orijentacija"</string>
+ <string name="label_pages" msgid="7768589729282182230">"Stranicā"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Odaberite štampač"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Sve stranice (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Opseg od <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="pages_range_example" msgid="8558694453556945172">"npr. 1—5,8,11—13"</string>
+ <string name="print_preview" msgid="8010217796057763343">"Pregled prije štampanja"</string>
+ <string name="install_for_print_preview" msgid="6366303997385509332">"Instaliraj PDF preglednik za prikaz"</string>
+ <string name="printing_app_crashed" msgid="854477616686566398">"Aplikacija za štampanje je prestala raditi"</string>
+ <string name="generating_print_job" msgid="3119608742651698916">"Kreiranje zadatka za štampu"</string>
+ <string name="save_as_pdf" msgid="5718454119847596853">"Sačuvaj kao PDF"</string>
+ <string name="all_printers" msgid="5018829726861876202">"Svi štampači…"</string>
+ <string name="print_dialog" msgid="32628687461331979">"Dijaloški okvir za štampanje"</string>
+ <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g> /<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+ <string name="page_description_template" msgid="6831239682256197161">"Strana <xliff:g id="CURRENT_PAGE">%1$d</xliff:g> od <xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+ <string name="summary_template" msgid="8899734908625669193">"Rezime, primjeraka <xliff:g id="COPIES">%1$s</xliff:g>, veličina papira <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+ <string name="expand_handle" msgid="7282974448109280522">"Regulator za proširivanje"</string>
+ <string name="collapse_handle" msgid="6886637989442507451">"Regulator za skupljanje"</string>
+ <string name="print_button" msgid="645164566271246268">"Štampaj"</string>
+ <string name="savetopdf_button" msgid="2976186791686924743">"Sačuvaj u PDF"</string>
+ <string name="print_options_expanded" msgid="6944679157471691859">"Opcije za štampanje su proširene"</string>
+ <string name="print_options_collapsed" msgid="7455930445670414332">"Opcije za štampanje su skupljene"</string>
+ <string name="search" msgid="5421724265322228497">"Traži"</string>
+ <string name="all_printers_label" msgid="3178848870161526399">"Svi štampači"</string>
+ <string name="add_print_service_label" msgid="5356702546188981940">"Dodaj uslugu"</string>
+ <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Okvir za pretraživanje je prikazan"</string>
+ <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Okvir za pretraživanje je skriven"</string>
+ <string name="print_add_printer" msgid="1088656468360653455">"Dodaj štampač"</string>
+ <string name="print_select_printer" msgid="7388760939873368698">"Izaberite štampač"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Zaboravi ovaj štampač"</string>
+ <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
+ <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> štampač je pronađen</item>
+ <item quantity="few"><xliff:g id="COUNT_1">%1$s</xliff:g> štampača su pronađena</item>
+ <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> štampača je pronađeno</item>
+ </plurals>
+ <string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
+ <string name="printer_info_desc" msgid="7181988788991581654">"Više informacija o ovom štampaču"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Neke usluge za štampanje su isključene"</string>
+ <string name="print_searching_for_printers" msgid="6550424555079932867">"Traženje štampača"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Usluga za štampanje nije uključena"</string>
+ <string name="print_no_printers" msgid="4869403323900054866">"Nijedan štampač nije pronađen"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Ne mogu se dodati štampači"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Odaberite da biste dodali štampač"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Odaberite da biste uključili"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Uključene usluge"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Preporučene usluge"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Isključene usluge"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Sve usluge"</string>
+ <string name="printing_notification_title_template" msgid="295903957762447362">"Štampa se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+ <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazivanje <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+ <string name="failed_notification_title_template" msgid="2256217208186530973">"Greška pri štampanju <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+ <string name="blocked_notification_title_template" msgid="1175435827331588646">"Štampač je blokirao <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+ <string name="cancel" msgid="4373674107267141885">"Otkaži"</string>
+ <string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
+ <string name="no_connection_to_printer" msgid="2159246915977282728">"Nema konekcije sa štampačem"</string>
+ <string name="reason_unknown" msgid="5507940196503246139">"nepoznat"</string>
+ <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nedostupan"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Zaista želite koristiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Moguće je da dokument prije štampanja prođe kroz jedan ili više servera."</string>
+ <string-array name="color_mode_labels">
+ <item msgid="7602948745415174937">"Crno-bijela"</item>
+ <item msgid="2762241247228983754">"U boji"</item>
+ </string-array>
+ <string-array name="duplex_mode_labels">
+ <item msgid="3882302912790928315">"Nije podržano"</item>
+ <item msgid="7296563835355641719">"Po dužoj strani"</item>
+ <item msgid="79513688117503758">"Po kraćoj strani"</item>
+ </string-array>
+ <string-array name="orientation_labels">
+ <item msgid="4061931020926489228">"Uspravno"</item>
+ <item msgid="3199660090246166812">"Vodoravno"</item>
+ </string-array>
+ <string name="print_write_error_message" msgid="5787642615179572543">"Nije moguće pisati u fajl"</string>
+ <string name="print_error_default_message" msgid="8602678405502922346">"Nažalost, nije uspjelo. Pokušajte ponovo."</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"Ponovi"</string>
+ <string name="print_error_printer_unavailable" msgid="8985614415253203381">"Štampač trenutno nije dostupan."</string>
+ <string name="print_preparing_preview" msgid="3939930735671364712">"Priprema pregleda..."</string>
+</resources>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index aa6f992..482100a 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Més informació sobre aquesta impressora"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Hi ha serveis d\'impressió que estan desactivats."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Selecció del servei d\'impressió"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Alguns serveis d\'impressió estan desactivats"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Cerca d\'impressores"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No hi ha cap servei d\'impressió activat"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No s\'ha trobat cap impressora"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"No poden afegir impressores"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Selecciona per afegir una impressora"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Selecciona\'ls per activar-los"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Serveis activats"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Serveis recomanats"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Serveis desactivats"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Tots els serveis"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"S\'està imprimint <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"S\'està cancel·lant <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Error d\'impressora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index 4bc22d4..a4c412c 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -63,11 +63,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Další informace o této tiskárně"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Některé tiskové služby nejsou aktivovány."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Zvolte službu tisku"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Některé tiskové služby nejsou aktivovány"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Vyhledávání tiskáren"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nejsou aktivovány žádné tiskové služby"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nebyly nalezeny žádné tiskárny"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Tiskárny nelze přidat"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Výběrem přidáte tiskárnu"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Výběrem službu aktivujete"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Aktivované služby"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Doporučené služby"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Deaktivované služby"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Všechny služby"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Tisk úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Rušení úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Chyba tiskárny u úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index b8be624..9ee252167 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Flere oplysninger om denne printer"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Nogle udskrivningstjenester er deaktiveret."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Vælg udskriftstjeneste"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Nogle udskrivningstjenester er deaktiveret"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Søger efter printere"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ingen udskrivningstjenester er aktiveret"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Der blev ikke fundet nogen printere"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Der kan ikke tilføjes printere"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Vælg for at tilføje en printer"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Vælg for at aktivere"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Aktiverede tjenester"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Anbefalede tjenester"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Deaktiverede tjenester"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Alle tjenester"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> udskrives"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annulleres"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Udskriften <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> mislykkedes"</string>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index bcb7e73..ef451b7 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Weitere Informationen über diesen Drucker"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Einige Druckdienste sind deaktiviert."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Druckdienst auswählen"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Einige Druckdienste sind deaktiviert"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Suche nach Druckern"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Keine Druckdienste aktiviert"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Keine Drucker gefunden"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Hinzufügen von Druckern nicht möglich"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Auswählen, um Drucker hinzuzufügen"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Zum Aktivieren auswählen"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Aktivierte Dienste"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Empfohlene Dienste"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Deaktivierte Dienste"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Alle Dienste"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> wird gedruckt..."</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> wird abgebrochen..."</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Druckerfehler <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index d9a4aeb..9be81c1 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Περισσότερες πληροφορίες σχετικά με αυτόν τον εκτυπωτή"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Κάποιες υπηρ. εκτύπωσης είναι απενεργοποιημένες."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Επιλέξτε υπηρεσία εκτύπωσης"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Ορισμένες υπηρ. εκτύπωσης είναι απενεργοποιημένες"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Αναζήτηση για εκτυπωτές"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Δεν έχουν ενεργοποιηθεί υπηρεσίες εκτύπωσης"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Δεν βρέθηκαν εκτυπωτές"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Δεν είναι δυνατή η προσθήκη εκτυπωτών"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Επιλέξτε για την προσθήκη εκτυπωτή"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Επιλέξτε για ενεργοποίηση"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Ενεργοποιημένες υπηρεσίες"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Προτεινόμενες υπηρεσίες"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Υπηρεσίες για άτομα με αναπηρία"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Όλες οι υπηρεσίες"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Εκτύπωση <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-en-rAU/strings.xml b/packages/PrintSpooler/res/values-en-rAU/strings.xml
index d8a9437..8b58011 100644
--- a/packages/PrintSpooler/res/values-en-rAU/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rAU/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"More information about this printer"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Some print services are disabled."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Choose print service"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Some print services are disabled"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No printers found"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Cannot add printers"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Select to add printer"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Select to enable"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Enabled services"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Recommended services"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Disabled services"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"All services"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Printing <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-en-rGB/strings.xml b/packages/PrintSpooler/res/values-en-rGB/strings.xml
index d8a9437..8b58011 100644
--- a/packages/PrintSpooler/res/values-en-rGB/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rGB/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"More information about this printer"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Some print services are disabled."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Choose print service"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Some print services are disabled"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No printers found"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Cannot add printers"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Select to add printer"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Select to enable"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Enabled services"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Recommended services"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Disabled services"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"All services"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Printing <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-en-rIN/strings.xml b/packages/PrintSpooler/res/values-en-rIN/strings.xml
index d8a9437..8b58011 100644
--- a/packages/PrintSpooler/res/values-en-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rIN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"More information about this printer"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Some print services are disabled."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Choose print service"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Some print services are disabled"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No printers found"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Cannot add printers"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Select to add printer"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Select to enable"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Enabled services"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Recommended services"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Disabled services"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"All services"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Printing <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelling <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printer error <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index 19cbee7..8fa6094 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Más información sobre esta impresora"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Hay servicios de impresión inhabilitados."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Elegir servicio de impresión"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Hay servicios de impresión inhabilitados"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Buscando impresoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No hay servicios de impresión habilitados"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No se encontraron impresoras"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"No es posible agregar impresoras"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Seleccionar para agregar impresoras"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Seleccionar para habilitar"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Servicios habilitados"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Servicios recomendados"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Servicios inhabilitados"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Todos los servicios"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimiendo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Error de impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index d13ccda..dba0491 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Más información sobre esta impresora"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Algunos servicios de impresión están inhabilitados."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Seleccionar servicio de impresión"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Algunos servicios de impresión están inhabilitados"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Buscando impresoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No hay servicios de impresión habilitados"</string>
<string name="print_no_printers" msgid="4869403323900054866">"No se encontraron impresoras"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"No se pueden añadir impresoras"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Selecciona para añadir una impresora"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Selecciona para habilitar"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Servicios habilitados"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Servicios recomendados"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Servicios inhabilitados"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Todos los servicios"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimiendo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Error de impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
@@ -91,7 +97,7 @@
<item msgid="3199660090246166812">"Horizontal"</item>
</string-array>
<string name="print_write_error_message" msgid="5787642615179572543">"Error al escribir en el archivo"</string>
- <string name="print_error_default_message" msgid="8602678405502922346">"No ha funcionado. Repítelo."</string>
+ <string name="print_error_default_message" msgid="8602678405502922346">"No ha funcionado. Prueba de nuevo."</string>
<string name="print_error_retry" msgid="1426421728784259538">"Reintentar"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Esta impresora no está disponible en este momento."</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Preparando vista previa…"</string>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index f03eb37..6dde083 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Lisateave selle printeri kohta"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Mõned printimisteenused on keelatud."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Prinditeenuse valimine"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Mõned printimisteenused on keelatud"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printerite otsimine"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ühtegi printimisteenust pole lubatud"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Printereid ei leitud"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Printereid ei saa lisada"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Valige printeri lisamiseks"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Valige lubamiseks"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Lubatud teenused"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Soovitatud teenused"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Keelatud teenused"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Kõik teenused"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Prinditöö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> printimine"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prinditöö <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> tühistamine"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printeri viga: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-eu-rES/strings.xml b/packages/PrintSpooler/res/values-eu-rES/strings.xml
index d4255e2..858444b 100644
--- a/packages/PrintSpooler/res/values-eu-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-eu-rES/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Informazio gehiago inprimagailuari buruz"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Desgaituta daude inprimatzeko zerbitzu batzuk."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Aukeratu inprimatze-zerbitzua"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Desgaituta daude inprimatzeko zerbitzu batzuk"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Inprimagailuak bilatzen"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ez dago gaituta inprimatzeko zerbitzurik"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Ez da inprimagailurik aurkitu"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Ezin da gehitu inprimagailurik"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Hautatu inprimagailua gehitzeko"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Hautatu gehitzeko"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Gaitutako zerbitzuak"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Gomendatutako zerbitzuak"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Desgaitutako zerbitzuak"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Zerbitzu guztiak"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> inprimatzen"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bertan behera uzten"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Errorea <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> inprimatzean"</string>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index 907123c..7c69c27 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"اطلاعات بیشتر درباره چاپگر"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"بعضی از خدمات چاپ غیرفعال هستند."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"انتخاب سرویس چاپ"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"بعضی از خدمات چاپ غیرفعال هستند"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"درحال جستجوی چاپگرها"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"هیچ خدمات چاپی فعال نیست"</string>
<string name="print_no_printers" msgid="4869403323900054866">"هیچ چاپگری یافت نشد"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"نمیتوان چاپگر اضافه کرد"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"برای افزودن چاپگر، انتخاب کنید"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"برای فعال کردن، انتخاب کنید"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"خدمات فعال"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"خدمات توصیهشده"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"خدمات غیرفعال"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"همه خدمات"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"در حال چاپ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index f57b884..dfd98f8 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Lisätietoja tästä tulostimesta"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Osa tulostuspalveluista on poistettu käytöstä."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Valitse tulostuspalvelu"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Osa tulostuspalveluista on poistettu käytöstä."</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Etsitään tulostimia"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ei käytössä olevia tulostuspalveluita"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Tulostimia ei löydy"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Tulostimien lisääminen ei onnistu."</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Valitse palvelu tulostimen lisäämistä varten."</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Valitse käyttöön otettavat palvelut."</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Käytössä olevat palvelut"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Suositellut palvelut"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Käytöstä poistetut palvelut"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Kaikki palvelut"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Tulostetaan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Peruutetaan työ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Tulostinvirhe työlle <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index 949ba55..a95d565 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Plus d\'information sur cette imprimante"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Certains services d\'impression sont désactivés."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Sélectionner le service d\'impression"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Certains services d\'impression sont désactivés"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Recherche d\'imprimantes en cours..."</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Aucun service d\'impression activé"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Aucune imprimante trouvée"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Impossible d’ajouter des imprimantes"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Sélectionnez pour ajouter une imprimante"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Sélectionner pour activer"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Services activés"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Services recommandés"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Services désactivés"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Tous les services"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Impression de <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> en cours…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> »…"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erreur impression : « <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> »"</string>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index 1fcc040..dd1f490 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Plus d\'informations sur cette imprimante"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Certains services d\'impression sont désactivés."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Sélectionner le service d\'impression"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Certains services d\'impression sont désactivés."</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Recherche d\'imprimantes en cours"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Aucun service d\'impression activé"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Aucune imprimante trouvée"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Impossible d\'ajouter des imprimantes"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Sélectionnez pour ajouter une imprimante."</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Sélectionnez pour activer."</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Services activés"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Services recommandés"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Services désactivés"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Tous les services"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Impression de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annulation de \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" en cours…"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erreur impression pour \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\""</string>
diff --git a/packages/PrintSpooler/res/values-gl-rES/strings.xml b/packages/PrintSpooler/res/values-gl-rES/strings.xml
index 2e60960..81e080e 100644
--- a/packages/PrintSpooler/res/values-gl-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-gl-rES/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Máis información sobre esta impresora"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Algúns servizos de impresión están desactivados."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Escoller servizo de impresión"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Algúns servizos de impresión están desactivados"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Busca de impresoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Non hai servizos de impresión activados"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Non se atopou ningunha impresora"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Non se poden engadir impresoras"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Selecciona un servizo para engadirlle impresora"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Selecciona un servizo para activalo"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Servizos activados"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Servizos recomendados"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Servizos desactivados"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Todos os servizos"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erro da impresora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-gu-rIN/strings.xml b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
index 4ba969c..44ede86 100644
--- a/packages/PrintSpooler/res/values-gu-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"આ પ્રિન્ટર વિશે વધુ માહિતી"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"કેટલીક છાપ સેવાઓ અક્ષમ કરેલ છે."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"પ્રિન્ટ સેવા પસંદ કરો"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"કેટલીક છાપવાની સેવાઓ અક્ષમ કરેલ છે"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"પ્રિન્ટર્સ માટે શોધી રહ્યું છે"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"કોઈ છાપ સેવાઓ સક્ષમ કરેલ નથી"</string>
<string name="print_no_printers" msgid="4869403323900054866">"કોઈ પ્રિન્ટર મળ્યા નથી"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"પ્રિન્ટર્સ ઉમેરી શકતાં નથી"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"પ્રિન્ટર ઉમેરવા માટે પસંદ કરો"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"સક્ષમ કરવા માટે પસંદ કરો"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"સક્ષમ કરેલી સેવાઓ"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"ભલામણ કરેલી સેવાઓ"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"અક્ષમ કરેલી સેવાઓ"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"બધી સેવાઓ"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> છાપી રહ્યાં છે"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index 1061346..f75630e 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"इस प्रिंटर के बारे में अधिक जानकारी"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"कुछ प्रिंट सेवाएं अक्षम हैं."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"प्रिंट सेवा चुनें"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"कुछ प्रिंट सेवाएं अक्षम हैं"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिंटर खोज रहा है"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"कोई भी प्रिंट सेवा सक्षम नहीं है"</string>
<string name="print_no_printers" msgid="4869403323900054866">"कोई प्रिंटर नहीं मिले"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"प्रिंटर जोड़े नहीं जा सकते"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"प्रिंटर जोड़ने के लिए चुनें"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"सक्षम करने के लिए चुनें"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"सक्षम सेवाएं"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"सुझाई गई सेवाएं"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"अक्षम सेवाएं"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"सभी सेवाएं"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> प्रिंट हो रहा है"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index 4a7d29f..bd29d02 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -62,11 +62,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Više informacija o ovom pisaču"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Neke su usluge ispisa onemogućene."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Odaberite uslugu ispisa"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Neke su usluge ispisa onemogućene"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Traženje pisača"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nije omogućena nijedna usluga ispisa"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nije pronađen nijedan pisač"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Ne možete dodati pisače"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Odaberite da biste dodali pisač"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Odaberite za omogućavanje"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Omogućene usluge"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Preporučene usluge"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Onemogućene usluge"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Sve usluge"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Ispisivanje <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazivanje zadatka <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Pogreška pisača <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index 5aae2e4..356cb76 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"További információ erről a nyomtatóról"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Egyes nyomtatási szolgáltatások le vannak tiltva."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Nyomtatási szolgáltatás kiválasztása"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Egyes nyomtatási szolgáltatások le vannak tiltva"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Nyomtatók keresése"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nincs engedélyezett nyomtatási szolgáltatás"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nem található nyomtató"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Nem lehet nyomtatókat hozzáadni"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Nyomtató hozzáadásához válassza ki"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Az engedélyezéshez válassza ki"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Engedélyezett szolgáltatások"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Javasolt szolgáltatások"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Letiltott szolgáltatások"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Minden szolgáltatás"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> nyomtatása"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"A(z) <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> törlése"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Nyomtatási hiba: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 179c384..2d10166 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Հավելյալ տեղեկություններ այս տպիչի մասին"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Տպելու որոշ ծառայությունները կասեցված են:"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Ընտրեք տպելու ծառայությունը"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Տպելու որոշ ծառայությունները կասեցված են"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Տպիչների որոնում"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ակտիվացված տպման ծառայություններ չկան"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Տպիչներ չեն գտնվել"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Չեն կարող ավելացնել տպիչներ"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Ընտրեք՝ տպիչ ավելացնելու համար"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Ընտրեք՝ միացնելու համար"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Միացված ծառայությունները"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Խորհուրդ տրվող ծառայությունները"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Կասեցված ծառայությունները"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Բոլոր ծառայությունները"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Տպվում է՝ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index 7286f7a..8e20d27 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Informasi selengkapnya tentang printer ini"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Beberapa layanan cetak dinonaktifkan."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Pilih layanan cetak"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Beberapa layanan cetak dinonaktifkan"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Mencari printer"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Tidak ada layanan cetak yang aktif"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Tidak ditemukan printer"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Tidak dapat menambahkan printer"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Pilih untuk menambahkan printer"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Pilih untuk mengaktifkan"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Layanan diaktifkan"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Layanan yang disarankan"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Layanan dinonaktifkan"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Semua layanan"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Mencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Ada kesalahan printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-is-rIS/strings.xml b/packages/PrintSpooler/res/values-is-rIS/strings.xml
index 9ea49a9..73660fb 100644
--- a/packages/PrintSpooler/res/values-is-rIS/strings.xml
+++ b/packages/PrintSpooler/res/values-is-rIS/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Frekari upplýsingar um þennan prentara"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Hluti prentþjónustunnar er óvirkur."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Veldu prentþjónustu"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Hluti prentþjónustunnar er óvirkur"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Leitar að prentara"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Engin prentþjónusta er virk"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Engir prentarar fundust"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Ekki er hægt að bæta við prenturum"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Veldu til að bæta prentara við"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Veldu til að virkja"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Virkjaðar þjónustur"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Þjónusta sem mælt er með"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Þjónusta við fatlaða"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Öll þjónusta"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Prentar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hættir við <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Prentaravilla <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index c19d012..46a570d8 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Ulteriori informazioni su questa stampante"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Alcuni servizi di stampa sono disattivati."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Scegli servizio di stampa"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Alcuni servizi di stampa sono disattivati"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Ricerca di stampanti"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Non è stato attivato alcun servizio di stampa"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nessuna stampante trovata"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Impossibile aggiungere stampanti"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Seleziona per aggiungere stampanti"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Seleziona per attivare"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Servizi abilitati"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Servizi consigliati"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Servizi disattivati"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Tutti i servizi"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Stampa di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Annullamento di <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Errore della stampante: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index 00bf27c..c26c3d1 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -63,11 +63,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"מידע נוסף על מדפסת זו"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"שירותי הדפסה מסוימים מושבתים."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"בחר שירות הדפסה"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"שירותי הדפסה מסוימים מושבתים"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"מחפש מדפסות"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"לא הופעלו שירותי הדפסה"</string>
<string name="print_no_printers" msgid="4869403323900054866">"לא נמצאו מדפסות"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"לא ניתן להוסיף מדפסות"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"בחר כדי להוסיף מדפסת"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"בחר כדי להפעיל"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"שירותים מופעלים"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"שירותים מומלצים"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"שירותים מושבתים"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"כל השירותים"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"מדפיס את <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index e0fc79a..a6e243f 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"このプリンタの詳細"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"一部の印刷サービスは無効になっています。"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"印刷サービスの選択"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"一部の印刷サービスは無効になっています"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"プリンタの検索中"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"使用できる印刷サービスがありません"</string>
<string name="print_no_printers" msgid="4869403323900054866">"プリンタが見つかりません"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"プリンタは追加できません"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"選択してプリンタを追加"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"選択して有効にする"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"有効になっているサービス"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"推奨されているサービス"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"無効になっているサービス"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"すべてのサービス"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>を印刷しています"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index ad1468a..2608ed48 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> — <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"დამატებითი ინფორმაცია ამ პრინტერის შესახებ"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"ბეჭდვის ზოგიერთი სერვისი გათიშულია."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"აირჩიეთ ბეჭდვის სერვისი"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"ბეჭდვის ზოგიერთი სერვისი გათიშულია"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"მიმდინარეობს პრინტერების ძიება"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ბეჭდვის სერვისები გააქტიურებული არ არის"</string>
<string name="print_no_printers" msgid="4869403323900054866">"პრინტერები ვერ მოიძებნა"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"პრინტერების დამატება ვერ მოხერხდება"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"აირჩიეთ პრინტერის დასამატებლად"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"აირჩიეთ ჩასართავად"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"ჩართული სერვისები"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"რეკომენდებული სერვისები"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"გათიშული სერვისები"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"ყველა სერვისი"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"იბეჭდება <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
index d0337a6..def0c3c 100644
--- a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
+++ b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Осы принтер туралы қосымша ақпарат"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Кейбір басып шығару қызметтері өшірілген."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Принтер қызметін таңдау"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Кейбір басып шығару қызметтері өшірілген."</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Принтерлерді іздеу"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Басып шығару қызметтері қосылмаған"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Ешқандай принтер табылмады"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Принтерлерді қосу мүмкін емес"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Принтерді қосу үшін таңдаңыз"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Қосу үшін таңдаңыз"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Қосылған қызметтер"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Ұсынылған қызметтер"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Өшірілген қызметтер"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Барлық қызметтер"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> басып шығарылуда"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index c9431e9..24048cf 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"ព័ត៌មានបន្ថែមអំពីម៉ាស៊ីបោះពុម្ពនេះ"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"សេវាកម្មបោះពុម្ពមួយចំនួនត្រូវបានបិទដំណើរការ"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"ជ្រើសសេវាបោះពុម្ព"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"សេវាកម្មបោះពុម្ពមួយចំនួនត្រូវបានបិទដំណើរការ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ស្វែងរកម៉ាស៊ីនបោះពុម្ព"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"គ្មានការបើកដំណើរការសេវាបោះពុម្ពទេ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"រកមិនឃើញម៉ាស៊ីនបោះពុម្ព"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"មិនអាចបន្ថែមម៉ាស៊ីនបោះពុម្ពបានទេ"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"ជ្រើសដើម្បីបន្ថែមម៉ាស៊ីនបោះពុម្ព"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"ជ្រើសដើម្បីបើកដំណើរការ"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"សេវាកម្មដែលបើកដំណើរការ"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"សេវាកម្មដែលបានណែនាំ"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"សេវាកម្មដែលបិទដំណើរការ"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"សេវាកម្មទាំងអស់"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"កំពុងបោះពុម្ព <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-kn-rIN/strings.xml b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
index fc5149a..af20965 100644
--- a/packages/PrintSpooler/res/values-kn-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"ಈ ಪ್ರಿಂಟರ್ ಬಗ್ಗೆ ಇನ್ನಷ್ಟು ಮಾಹಿತಿ"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"ಕೆಲವು ಮುದ್ರಣ ಸೇವೆಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"ಮುದ್ರಣ ಸೇವೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"ಕೆಲವು ಮುದ್ರಣ ಸೇವೆಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ಪ್ರಿಂಟರ್ಗಳಿಗಾಗಿ ಹುಡುಕಲಾಗುತ್ತಿದೆ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ಯಾವುದೇ ಮುದ್ರಣ ಸೇವೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿಲ್ಲ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ಯಾವುದೇ ಮುದ್ರಕಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"ಪ್ರಿಂಟರ್ಗಳನ್ನು ಸೇರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"ಪ್ರಿಂಟರ್ ಸೇರಿಸಲು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"ಸಕ್ರಿಯಗೊಳಿಸಲು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"ಸಕ್ರಿಯಗೊಳಿಸಲಾದ ಸೇವೆಗಳು"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"ಶಿಫಾರಸು ಮಾಡಲಾದ ಸೇವೆಗಳು"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾದ ಸೇವೆಗಳು"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"ಎಲ್ಲ ಸೇವೆಗಳು"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ಮುದ್ರಿಸಲಾಗುತ್ತಿದೆ"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index 2faff1f..0b297a2 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"이 프린터에 대한 정보 더보기"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"프린트 서비스 일부가 사용 중지되었습니다."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"인쇄 서비스 선택"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"프린트 서비스 일부가 사용 중지되었습니다."</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"프린터 검색 중"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"사용 가능한 프린트 서비스 없음"</string>
<string name="print_no_printers" msgid="4869403323900054866">"프린터 없음"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"프린터를 추가할 수 없음"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"프린터를 추가하려면 선택하세요."</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"사용 설정하려면 선택하세요."</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"사용 설정된 서비스"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"권장 서비스"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"사용 중지된 서비스"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"모든 서비스"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> 인쇄 중"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-ky-rKG/strings.xml b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
index a01e4a8..85b2526 100644
--- a/packages/PrintSpooler/res/values-ky-rKG/strings.xml
+++ b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Бул принтер жөнүндө көбүрөөк маалымат"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Басып чыгаруу кызматтарынын айрымы өчүрүлгөн."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Принтер кызматын тандоо"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Басып чыгаруу кызматтарынын айрымы өчүрүлгөн"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Принтерлер изделүүдө"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Принтер-кызматтары иштетилген эмес"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Принтерлер табылган жок"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Принтерлер кошулбай жатат"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Принтер кошуу үчүн тандаңыз"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Иштетүү үчүн тандаңыз"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Иштетилген кызматтар"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Сунушталган кызматтар"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Өчүрүлгөн кызматтар"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Бардык кызматтар"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> басылууда"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-lo-rLA/strings.xml b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
index b5d13b5..81ace83 100644
--- a/packages/PrintSpooler/res/values-lo-rLA/strings.xml
+++ b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"ຂໍ້ມູນເພີ່ມເຕີມກ່ຽວກັບເຄື່ອງພິມນີ້"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"ບາງການບໍລິການພິມຖືກປິດນຳໃຊ້."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"ເລືອກບໍລິການການພິມ"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"ບາງການບໍລິການພິມຖືກປິດການນຳໃຊ້"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ກຳລັງຊອກຫາເຄື່ອງພິມ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ບໍ່ມີການບໍລິການພິມເປີດໃຊ້ງານໄວ້"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ບໍ່ພົບເຄື່ອງພິມ"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"ບໍ່ສາມາດເພີ່ມເຄື່ອງພິມໄດ້"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"ເລືອກເພື່ອເພີ່ມເຄື່ອງພິມ"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"ເລືອກເພື່ອເປີດໃຊ້"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"ບໍລິການທີ່ເປີດໃຊ້"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"ບໍລິການທີ່ແນະນຳ"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"ບໍລິການທີ່ຖືກປິດການນຳໃຊ້"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"ບໍລິການທັງໝົດ"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"ກຳລັງພິມ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-lt/strings.xml b/packages/PrintSpooler/res/values-lt/strings.xml
index 3b8f143..40bc7f1 100644
--- a/packages/PrintSpooler/res/values-lt/strings.xml
+++ b/packages/PrintSpooler/res/values-lt/strings.xml
@@ -63,11 +63,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"„<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g>“ – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Daugiau informacijos apie šį spausdintuvą"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Kai kurios spausdinimo paslaugos išjungtos."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Pasirinkite spausdinimo paslaugą"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Kai kurios spausdinimo paslaugos išjungtos"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Ieškoma spausdintuvų"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Neįgalinta jokių spausdinimo paslaugų"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nerasta spausdintuvų"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Nepavyko pridėti spausdintuvų"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Pasirinkite, kad pridėtumėte spausdintuvą"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Pasirinkite, kad įgalintumėte"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Įgalintos paslaugos"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Rekomenduojamos paslaugos"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Išjungtos paslaugos"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Visos paslaugos"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Spausdinama: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Atšaukiama: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Spausdintuvo klaida: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index 762d0bd..11e689b 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -62,11 +62,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> — <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Plašāka informācija par šo printeri"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Daži drukas pakalpojumi ir atspējoti."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Izvēlieties drukāšanas pakalpojumu"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Daži drukas pakalpojumi ir atspējoti."</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printeru meklēšana"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nav iespējots neviens drukas pakalpojums"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Netika atrasts neviens printeris."</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Nevar pievienot printerus"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Atlasiet, lai pievienotu printeri"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Izvēlieties, lai iespējotu"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Iespējotie pakalpojumi"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Ieteiktie pakalpojumi"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Atspējotie pakalpojumi"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Visi pakalpojumi"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Notiek darba <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> drukāšana…"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Pārtrauc drukas darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>…"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printera kļūda ar darbu <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-mk-rMK/strings.xml b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
index de6d3e9..bc2b498 100644
--- a/packages/PrintSpooler/res/values-mk-rMK/strings.xml
+++ b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Повеќе информации за овој печатач"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Некои услуги за печатење се оневозможени."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Избери услуга печатење"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Некои услуги за печатење се оневозможени"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Пребарување печатачи"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Нема овозможени услуги за печатење"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Не се пронајдени печатачи"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Не може да се додадат печатачи"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Изберете додавање печатач"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Изберете да се овозможи"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Овозможени услуги"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Препорачани услуги"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Оневозможени услуги"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Сите услуги"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> се печати"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-ml-rIN/strings.xml b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
index 7a33e14..ade7fb39 100644
--- a/packages/PrintSpooler/res/values-ml-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"ഈ പ്രിന്ററിനെ കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾ"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"ചില പ്രിന്റ് സേവനങ്ങൾ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"പ്രിന്റ് സേവനം തിരഞ്ഞെടുക്കുക"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"ചില പ്രിന്റ് സേവനങ്ങൾ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"പ്രിന്ററുകൾക്കായി തിരയുന്നു"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"പ്രിന്റ് സേവനങ്ങളൊന്നും പ്രവർത്തനക്ഷമാക്കിയിട്ടില്ല"</string>
<string name="print_no_printers" msgid="4869403323900054866">"പ്രിന്ററുകളൊന്നും കണ്ടെത്തിയില്ല"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"പ്രിന്ററുകൾ ചേർക്കാൻ കഴിയില്ല"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"പ്രിന്റർ ചേർക്കാൻ തിരഞ്ഞെടുക്കുക"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"പ്രവർത്തനക്ഷമമാക്കാൻ തിരഞ്ഞെടുക്കുക"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"പ്രവർത്തനക്ഷമമാക്കിയ സേവനങ്ങൾ"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"ശുപാർശ ചെയ്യപ്പെടുന്ന സേവനങ്ങൾ"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"പ്രവർത്തനരഹിതമാക്കിയ സേവനങ്ങൾ"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"എല്ലാ സേവനങ്ങളും"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> പ്രിന്റുചെയ്യുന്നു"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index c94e56d..133d88c 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Энэ хэвлэгчийн талаарх дэлгэрэнгүй мэдээлэл"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Зарим хэвлэх үйлчилгээг идэвхгүй болгосон байна."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Хэвлэх үйлчилгээг сонгох"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Зарим хэвлэх үйлчилгээг идэвхгүй болгосон байна"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Принтер хайж байна"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Хэвлэх үйлчилгээг идэвхжүүлээгүй"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Принтер олдсонгүй"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Хэвлэгч нэмэх боломжгүй байна"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Хэвлэгч нэмэхийн тулд сонгох"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Идэвхжүүлэхийн тулд сонгох"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Идэвхжүүлсэн үйлчилгээ"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Санал болгосон үйлчилгээ"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Идэвхгүй болгосон үйлчилгээ"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Бүх үйлчилгээ"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Хэвлэж байна <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-mr-rIN/strings.xml b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
index ab25010..2b3b29f 100644
--- a/packages/PrintSpooler/res/values-mr-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"या प्रिंटर विषयी अधिक माहिती"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"काही मुद्रण सेवा अक्षम केल्या आहेत."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"मुद्रण सेवा निवडा"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"काही मुद्रण सेवा अक्षम केल्या आहेत"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिंटर शोधत आहे"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"कोणत्याही मुद्रण सेवा सक्षम केलेल्या नाहीत"</string>
<string name="print_no_printers" msgid="4869403323900054866">"कोणतेही प्रिंटर आढळले नाही"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"प्रिंटर जोडू शकत नाही"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"प्रिंटर जोडण्यासाठी निवडा"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"सक्षम करण्यासाठी निवडा"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"सक्षम केलेल्या सेवा"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"शिफारस केलेल्या सेवा"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"अक्षम केलल्या सेवा"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"सर्व सेवा"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> मुद्रण करीत आहे"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index 917ae8a..73104e1 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Maklumat lanjut tentang pencetak ini"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Sesetengah perkhidmatan cetak dilumpuhkan."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Pilih perkhidmatan cetak"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Sesetengah perkhidmatan cetak dilumpuhkan"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Mencari pencetak"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Perkhidmatan cetak tidak didayakan"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Tiada pencetak ditemui"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Tidak dapat menambahkan pencetak"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Pilih untuk menambahkan pencetak"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Pilih untuk mendayakan"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Perkhidmatan yang didayakan"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Perkhidmatan yang disyorkan"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Perkhidmatan yang dilumpuhkan"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Semua perkhidmatan"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Mencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Membatalkan <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Ralat pencetak <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index 4d4c95b..8cec068 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"ဤပရင်တာ အကြောင်း ပိုမိုလေ့လာပါ"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"ပရင့်ထုတ်ရေး အချို့ဝန်ဆောင်မှုများကို ပိတ်ထားပါသည်။"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"စာထုတ်ရန် ဝန်ဆောင်မှုကို ရွေးချယ်ပါ"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"အချို့ပုံနှိပ်ဝန်ဆောင်မှုများကို ပိတ်ထားပါသည်"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"စာထုတ်စက်များကို ရှာနေပါသည်"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ပုံနှိပ်ထုတ်ယူရေး ဝန်ဆောင်မှုများ ဖွင့်မထားပါ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"စာထုတ်စက် တစ်ခုမှ မတွေ့ရှိပါ"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"ပုံနှိပ်စက်များကို ထည့်၍မရပါ"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"ပုံနှိပ်စက်ထည့်ရန် ရွေးပါ"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"ဖွင့်ရန် ရွေးပါ"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"ဖွင့်ထားသည့် ဝန်ဆောင်မှုများ"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"အကြံပြုထားသည့် ဝန်ဆောင်မှုများ"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"ပိတ်ထားသည့် ဝန်ဆောင်မှုများ"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"ဝန်ဆောင်မှုများ အားလုံး"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ကို စာထုတ်နေပါသည်"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index 9efa5d1..0a6f6c3 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Mer informasjon om denne printeren"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Noen utskriftstjenester er slått av."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Velg utskriftstjeneste"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Noen utskriftstjenester er slått av"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Søker etter skrivere"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ingen utskriftstjenester er slått på"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Fant ingen skrivere"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Kan ikke legge til skrivere"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Velg for å legge til skrivere"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Velg for å slå på"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Tjenester som er slått på"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Anbefalte tjenester"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Tjenester som er slått av"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Alle tjenester"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Skriver ut <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Skriverfeil <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-ne-rNP/strings.xml b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
index 281a65d..5af3a04 100644
--- a/packages/PrintSpooler/res/values-ne-rNP/strings.xml
+++ b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"यस प्रिन्टरको बारेमा थप जानकारी"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"केही मुद्रण सेवाहरू असक्षम छन्।"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"प्रिन्ट सेवा छनौट गर्नुहोस्"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"केही मुद्रण सम्बन्धी सेवाहरूलाई असक्षम गरिएको छ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"प्रिन्टरहरू खोज्दै"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"कुनै पनि मुद्रण सेवाहरू सक्रिय छैनन्"</string>
<string name="print_no_printers" msgid="4869403323900054866">"कुनै प्रिन्टरहरू भेटाइएन"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"प्रिन्टरहरू थप्न सक्दैन"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"प्रिन्टर थप्नका लागि चयन गर्नुहोस्"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"सक्षम गर्नका लागि चयन गर्नुहोस्"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"सक्षम गरिएका सेवाहरू"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"सिफारिस गरिएका सेवाहरू"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"असक्षम गरिएका सेवाहरू"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"सबै सेवाहरू"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"प्रिन्ट गरिँदै <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index eef9880..4afdb86 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Meer informatie over deze printer"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Sommige afdrukservices zijn uitgeschakeld."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Afdrukservice kiezen"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Sommige afdrukservices zijn uitgeschakeld"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printers zoeken"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Geen afdrukservices ingeschakeld"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Geen printers gevonden"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Kan geen printers toevoegen"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Selecteer om printer toe te voegen"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Selecteer om in te schakelen"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Ingeschakelde services"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Aanbevolen services"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Uitgeschakelde services"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Alle services"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> afdrukken"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> annuleren"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printerfout <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-pa-rIN/strings.xml b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
index 7d7860c..1886ef5 100644
--- a/packages/PrintSpooler/res/values-pa-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"ਇਸ ਪ੍ਰਿੰਟਰ ਬਾਰੇ ਹੋਰ ਜਾਣਕਾਰੀ"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"ਕੁਝ ਪ੍ਰਿੰਟ ਸੇਵਾਵਾਂ ਅਯੋਗ ਬਣਾਈਆਂ ਗਈਆਂ ਹਨ।"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"ਪ੍ਰਿੰਟ ਸੇਵਾ ਚੁਣੋ"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"ਕੁਝ ਪ੍ਰਿੰਟ ਸੇਵਾਵਾਂ ਅਯੋਗ ਬਣਾਈਆਂ ਗਈਆਂ ਹਨ"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ਪ੍ਰਿੰਟਰ ਖੋਜ ਰਿਹਾ ਹੈ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ਪ੍ਰਿੰਟ ਸੇਵਾਵਾਂ ਯੋਗ ਨਹੀਂ ਬਣਾਈਆਂ ਗਈਆਂ"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ਕੋਈ ਪ੍ਰਿੰਟਰ ਨਹੀਂ ਮਿਲੇ"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"ਪ੍ਰਿੰਟਰ ਸ਼ਾਮਲ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"ਪ੍ਰਿੰਟਰ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਚੁਣੋ"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"ਯੋਗ ਬਣਾਉਣ ਲਈ ਚੁਣੋ"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"ਯੋਗ ਬਣਾਈਆਂ ਗਈਆਂ ਸੇਵਾਵਾਂ"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"ਸਿਫ਼ਾਰਸ਼ ਕੀਤੀਆਂ ਸੇਵਾਵਾਂ"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"ਅਯੋਗ ਬਣਾਈਆਂ ਗਈਆਂ ਸੇਵਾਵਾਂ"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"ਸਾਰੀਆਂ ਸੇਵਾਵਾਂ"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ਨੂੰ ਪ੍ਰਿੰਟ ਕਰ ਰਿਹਾ ਹੈ"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index 6837edf..45649bb 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -63,11 +63,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Więcej informacji o tej drukarce"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Niektóre usługi drukowania są wyłączone."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Wybierz usługę drukowania"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Niektóre usługi drukowania są wyłączone"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Szukanie drukarek"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Brak włączonych usług drukowania"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nie znaleziono drukarek"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Nie można dodawać drukarek"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Wybierz, by dodać drukarkę"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Wybierz, by włączyć usługę"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Włączone usługi"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Polecane usługi"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Wyłączone usługi"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Wszystkie usługi"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Drukowanie: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Anulowanie: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Błąd drukarki: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-pt-rBR/strings.xml b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
index c9713c9..58eb24f 100644
--- a/packages/PrintSpooler/res/values-pt-rBR/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Mais informações sobre essa impressora"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Alguns serviços de impressão estão desativados."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Selecione o serviço de impressão"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Alguns serviços de impressão estão desativados"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Procurando impressoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nenhuma impressora encontrada"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Não é possível adicionar impressoras"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Selecione para adicionar uma impressora"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Selecione para ativar"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Serviços ativados"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Serviços recomendados"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Serviços desativados"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erro ao imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 9fabc0f..370bbb9 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Mais informações acerca desta impressora"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Alguns serviços de impressão estão desativados."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Escolher o serviço de impressão"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Alguns serviços de impressão estão desativados"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"A procurar impressoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nenhuma impressora encontrada"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Não é possível adicionar impressoras"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Selecione para adicionar uma impressora"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Selecione para ativar"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Serviços ativados"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Serviços recomendados"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Serviços desativados"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"A imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"A cancelar <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erro da impressora <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index c9713c9..58eb24f 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Mais informações sobre essa impressora"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Alguns serviços de impressão estão desativados."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Selecione o serviço de impressão"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Alguns serviços de impressão estão desativados"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Procurando impressoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nenhuma impressora encontrada"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Não é possível adicionar impressoras"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Selecione para adicionar uma impressora"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Selecione para ativar"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Serviços ativados"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Serviços recomendados"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Serviços desativados"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Todos os serviços"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Imprimindo <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Cancelando <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Erro ao imprimir <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 7364eb0..1097d56 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -62,11 +62,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Mai multe informații despre această imprimantă"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Unele servicii de printare sunt dezactivate."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Alegeți serviciul de printare"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Unele servicii de printare sunt dezactivate"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Se caută imprimante"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Niciun serviciu de printare activat"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nu au fost găsite imprimante"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Nu pot fi adăugate imprimante"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Selectați pentru a adăuga o imprimantă"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Selectați pentru a activa"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Servicii activate"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Servicii recomandate"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Servicii dezactivate"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Toate serviciile"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Se printează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Se anulează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Eroare de printare: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index d3d0d3f..24b1e0d 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -63,11 +63,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Подробные сведения о принтере"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Некоторые службы печати отключены."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Выберите службу печати"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Некоторые службы печати отключены"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Поиск принтеров…"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Службы печати недоступны"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Ничего не найдено"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Невозможно добавить принтеры"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Выберите, чтобы добавить принтер"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Выберите, чтобы включить"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Включенные службы"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Рекомендуемые службы"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Отключенные службы"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Все службы печати"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Печать задания \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\"…"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index 610442d..707c151 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"මෙම මුද්රණ යන්ත්රය ගැන තවත් තොරතුරු"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"සමහර මුද්රණ සේවා අබලයි."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"මුද්රණ සේවාව තෝරන්න"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"සමහර මුද්රණ සේවා අබලයි"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"මුද්රණ යන්ත්ර සොයමින්"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"මුද්රණ සේවා සබල නැත"</string>
<string name="print_no_printers" msgid="4869403323900054866">"මුද්රණ යන්ත්ර සොයා නොගැනුණි"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"මුද්රණ යන්ත්ර එක් කළ නොහැකිය"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"මුද්රණ යන්ත්රය එක් කිරීමට තෝරන්න"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"සබල කිරීමට තෝරන්න"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"සබල කළ සේවා"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"නිර්දේශිත සේවා"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"අබල කළ සේවා"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"සියලු සේවා"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> මුද්රණය වේ"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 603d1d2..1f13b54 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -63,11 +63,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Ďalšie informácie o tejto tlačiarni"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Niektoré tlačové služby sú vypnuté."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Výber tlačovej služby"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Niektoré tlačové služby sú zakázané"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Vyhľadávanie tlačiarní"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Žiadne tlačové služby nie sú aktivované"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nenašli sa žiadne tlačiarne"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Nie je možné pridať tlačiarne"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Výber služby na pridanie tlačiarne"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Vyberte a povoľte"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Povolené služby"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Odporúčané služby"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Zakázané služby"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Všetky služby"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Prebieha tlač úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Prebieha zrušenie úlohy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Chyba tlačiarne – úloha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index 4a08269..3f8a5e6 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -63,11 +63,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Več informacij o tem tiskalniku"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Nekatere tiskalne storitve so onemogočene."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Izberite tiskalno storitev"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Nekatere tiskalne storitve so onemogočene"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Iskanje tiskalnikov"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ni omogočenih tiskalnih storitev"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Tiskalnikov ni mogoče najti"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Tiskalnikov ni mogoče dodati"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Izberite za dodajanje tiskalnika"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Izberite, če želite omogočiti"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Omogočene storitve"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Priporočene storitve"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Onemogočene storitve"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Vse storitve"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Tiskanje: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Preklic: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Napaka tiskalnika: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-sq-rAL/strings.xml b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
index b0902ef..0b843d7 100644
--- a/packages/PrintSpooler/res/values-sq-rAL/strings.xml
+++ b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Më shumë informacione mbi këtë printer"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Disa shërbime printimi janë çaktivizuar."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Zgjidh shërbimin e printimit"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Disa shërbime printimi janë çaktivizuar"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Po kërkon për printerë"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nuk ka shërbime printimi të aktivizuara"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nuk u gjet asnjë printer"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Nuk mund të shtohen printerë"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Zgjidh për të shtuar printer"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Zgjidh për të aktivizuar"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Shërbimet e aktivizuara"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Shërbimet e rekomanduara"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Shërbimet e çaktivizuara"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Të gjitha shërbimet"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Po printon <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Po anulon <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printeri ndeshi në gabim gjatë punës: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index feb2940..8baa23c 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -62,11 +62,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Још информација о овом штампачу"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Неке услуге штампања су онемогућене."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Изаберите услугу штампања"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Неке услуге штампања су онемогућене"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Претрага штампача"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ниједна услуга штампања није омогућена"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Није пронађен ниједан штампач"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Није могуће додати штампаче"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Изаберите да бисте додали штампач"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Изаберите да бисте омогућили"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Омогућене услуге"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Препоручене услуге"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Онемогућене услуге"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Све услуге"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Штампа се <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index cf398c7..64b6b20 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Mer information om den här skrivaren"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Några utskriftstjänster har inaktiverats."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Välj utskriftstjänst"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Några utskriftstjänster har inaktiverats"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Söker efter skrivare"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Inga utskriftstjänster har aktiverats"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Det gick inte att hitta några skrivare"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Det går inte att lägga till skrivare"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Lägg till en skrivare"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Välj om du vill aktivera"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Aktiverade tjänster"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Rekommenderade tjänster"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Inaktiverade tjänster"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Alla tjänster"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Skriver ut <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Avbryter <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Skrivarfel för <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index 7e00b70..b3ffa71 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Maelezo zaidi kuhusu printa hii"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Baadhi ya huduma za uchapishaji zimezimwa."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Chagua huduma ya printa"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Baadhi ya huduma za uchapishaji haziruhusiwi"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Inatafuta printa"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Huduma za kuchapisha hazijawashwa"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Hakuna printa zilizopatikana"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Haiwezi kuongeza printa"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Chagua printa ya kuongeza"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Chagua ili uruhusu"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Huduma zinazoruhusiwa"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Huduma zinazopendekezwa"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Huduma ambazo haziruhusiwi"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Huduma zote"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Inachapisha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Inaghairi <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Hitilafu ya kuchapisha <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-ta-rIN/strings.xml b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
index ae0b774..7ae3cbc 100644
--- a/packages/PrintSpooler/res/values-ta-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"இந்தப் பிரிண்டர் பற்றிய கூடுதல் தகவல்"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"சில அச்சுப் பொறிகள் முடக்கப்பட்டன."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"அச்சுப் பொறியைத் தேர்வுசெய்யவும்"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"சில அச்சுப் பொறிகள் முடக்கப்பட்டன"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"அச்சுப்பொறிகளைத் தேடுகிறது"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"அச்சுப் பொறிகள் இல்லை"</string>
<string name="print_no_printers" msgid="4869403323900054866">"பிரிண்டர்கள் எதுவுமில்லை"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"பிரிண்டர்களைச் சேர்க்க முடியவில்லை"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"பிரிண்டரைச் சேர்க்க, தேர்ந்தெடுக்கவும்"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"இயக்குவதற்குத் தேர்ந்தெடுக்கவும்"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"இயக்கப்பட்ட அச்சுப் பொறிகள்"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"பரிந்துரைக்கப்படும் அச்சுப் பொறிகள்"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"முடக்கப்பட்ட அச்சுப் பொறிகள்"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"எல்லா அச்சுப் பொறிகளும்"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ஐ அச்சிடுகிறது"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-te-rIN/strings.xml b/packages/PrintSpooler/res/values-te-rIN/strings.xml
index 5fd8d60..9e8dea2 100644
--- a/packages/PrintSpooler/res/values-te-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-te-rIN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"ఈ ప్రింటర్ గురించి మరింత సమాచారం"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"కొన్ని ముద్రణ సేవలు నిలిపివేయబడ్డాయి."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"ముద్రణ సేవను ఎంచుకోండి"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"కొన్ని ముద్రణ సేవలు నిలిపివేయబడ్డాయి"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"ప్రింటర్ల కోసం శోధిస్తోంది"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ముద్రణ సేవలు ఏవీ ప్రారంభించలేదు"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ప్రింటర్లు కనుగొనబడలేదు"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"ప్రింటర్లను జోడించడం సాధ్యపడలేదు"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"ప్రింటర్ను జోడించడానికి ఎంచుకోండి"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"ప్రారంభించడానికి ఎంచుకోండి"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"ప్రారంభించిన సేవలు"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"సిఫార్సు చేయబడిన సేవలు"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"నిలిపివేసిన సేవలు"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"అన్ని సేవలు"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ను ముద్రిస్తోంది"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index ebd5e2a..c623dd7 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"ข้อมูลเพิ่มเติมเกี่ยวกับเครื่องพิมพ์นี้"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"บริการพิมพ์บางอย่างถูกปิดใช้"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"เลือกบริการพิมพ์"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"บริการพิมพ์บางอย่างปิดใช้อยู่"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"กำลังค้นหาเครื่องพิมพ์"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ไม่ได้เปิดใช้บริการพิมพ์"</string>
<string name="print_no_printers" msgid="4869403323900054866">"ไม่พบเครื่องพิมพ์"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"เพิ่มเครื่องพิมพ์ไม่ได้"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"เลือกเพื่อเพิ่มเครื่องพิมพ์"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"เลือกเพื่อเปิดใช้"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"บริการที่เปิดใช้"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"บริการที่แนะนำ"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"บริการที่ปิดใช้"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"บริการทั้งหมด"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"กำลังพิมพ์ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index ebe869b..0494cf6 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Higit pang impormasyon tungkol sa printer na ito"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Naka-disable ang ilang serbisyo sa pag-print."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Pumili ng serbisyo ng pag-print"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Naka-disable ang ilang serbisyo sa pag-print"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Naghahanap ng mga printer"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Walang mga naka-enable na serbisyo sa pag-print"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Walang mga printer na nakita"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Hindi makapagdagdag ng mga printer"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Piliin upang magdagdag ng printer"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Piliin upang i-enable"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Mga naka-enable na serbisyo"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Mga inirerekomendang serbisyo"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Mga naka-disable na serbisyo"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Lahat ng serbisyo"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Pini-print ang <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Kinakansela ang <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Error sa printer <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index 9cd42ab..2818f36 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Bu yazıcıyla ilgili daha fazla bilgi"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Bazı yazdırma hizmetleri devre dışı."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Yazdırma hizmetini seçin"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Bazı yazdırma hizmetleri devre dışı bırakıldı"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Yazıcılar aranıyor"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Etkin yazıcı hizmeti yok"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Yazıcı bulunamadı"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Yazıcı eklenemiyor"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Yazıcı eklemek için seçin"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Etkinleştirmek için seçin"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Etkin hizmetler"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Önerilen hizmetler"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Devre dışı bırakılmış hizmetler"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Tüm hizmetler"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> yazdırılıyor"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> iptal ediliyor"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Yazıcı hatası: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-uk/strings.xml b/packages/PrintSpooler/res/values-uk/strings.xml
index 1082147..41051af 100644
--- a/packages/PrintSpooler/res/values-uk/strings.xml
+++ b/packages/PrintSpooler/res/values-uk/strings.xml
@@ -63,11 +63,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Докладніше про цей принтер"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Деякі служби друку вимкнено."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Вибрати службу друку"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Деякі служби друку вимкнено"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Пошук принтерів"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Немає служб друку"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Принтери не знайдено"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Не можна додати принтери"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Виберіть, щоб додати принтер"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Виберіть, щоб увімкнути"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Увімкнені служби"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Рекомендовані служби"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Вимкнені служби"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Усі служби"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" друкується"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-ur-rPK/strings.xml b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
index 56f1093..a94b16f 100644
--- a/packages/PrintSpooler/res/values-ur-rPK/strings.xml
+++ b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"اس پرنٹر کے بارے میں مزید معلومات"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"پرنٹ کی کچھ سروسز غیر فعال ہیں۔"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"پرنٹ سروس منتخب کریں"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"پرنٹ کی کچھ سروسز غیر فعال ہیں"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"پرنٹرز تلاش کر رہا ہے"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"کوئی پرنٹ سروس فعال نہیں"</string>
<string name="print_no_printers" msgid="4869403323900054866">"کوئی پرنٹرز نہيں ملے"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"پرنٹرز شامل نہیں ہو سکتے"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"پرنٹر شامل کرنے کیلئے منتخب کریں"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"فعال کرنے کیلئے منتخب کریں"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"فعال کردہ سروسز"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"تجویز کردہ سروسز"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"غیر فعال کردہ سروسز"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"تمام سروسز"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> پرنٹ کررہا ہے"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index 30b218e..a6af5bd 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> – <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Printer haqida batafsil ma’lumot"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Bir qancha chop etish xizmatlari o‘chirilgan."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Chop etish xizmatini tanlang"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Ayrim chop etish xizmatlari o‘chirib qo‘yilgan"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Printerlar qidirilmoqda"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Hech qaysi chop etish xizmati yoqilmagan"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Printerlar topilmadi"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Printerlarni qo‘shib bo‘lmaydi"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Printer qo‘shish uchun tanlang"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Yoqish uchun tanlang"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Yoqilgan xizmatlar"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Tavsiya etilgan xizmatlar"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"O‘chirib qo‘yilgan xizmatlar"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Barcha xizmatlar"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Chop etilmoqda: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> bekor qilinmoqda"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Printerda xatolik: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index 32aaf63..df9e1a4 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Thông tin khác về máy in này"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Một số dịch vụ in bị vô hiệu hóa."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Chọn dịch vụ in"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Một số dịch vụ in đã bị tắt"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Đang tìm kiếm máy in"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Chưa kích hoạt dịch vụ in nào"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Không tìm thấy máy in"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Không thể thêm máy in"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Chọn để thêm máy in"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Chọn để bật"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Dịch vụ đã bật"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Dịch vụ được đề xuất"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Dịch vụ đã tắt"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Tất cả dịch vụ"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"In <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Hủy <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Lỗi máy in <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index 42cf3b1..fb30e44 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"关于此打印机的更多信息"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"部分打印服务已停用。"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"选择打印服务"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"部分打印服务已停用"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜索打印机"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"未启用任何打印服务"</string>
<string name="print_no_printers" msgid="4869403323900054866">"找不到打印机"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"无法添加打印机"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"选择即可添加打印机"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"选择即可启用"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"已启用的服务"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"推荐的服务"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"已停用的服务"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"所有服务"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"正在打印“<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>”"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index 0a458ad..f7c8fc9 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"此打印機詳情"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"已停用部分列印服務。"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"選擇列印服務"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"部分列印服務已停用"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜尋打印機"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"沒有已啟用的列印服務"</string>
<string name="print_no_printers" msgid="4869403323900054866">"找不到打印機"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"無法新增印表機"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"選擇即可新增印表機"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"選取即可啟用"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"已啟用的服務"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"推薦服務"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"已停用的服務"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"所有服務"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"正在列印 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index 7a30011..aa18f3c 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"查看這台印表機的詳細資訊"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"部分列印服務已停用。"</string>
- <string name="choose_print_service" msgid="3740309762324459694">"選擇列印服務"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"已停用部分列印服務"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"正在搜尋印表機"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"未啟用任何列印服務"</string>
<string name="print_no_printers" msgid="4869403323900054866">"找不到印表機"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"無法新增印表機"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"選取即可新增印表機"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"選取即可啟用"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"已啟用的列印服務"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"建議的列印服務"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"已停用的列印服務"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"所有列印服務"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"正在列印 <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index f57b58c..9cfcb33 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -61,11 +61,17 @@
</plurals>
<string name="printer_extended_description_template" msgid="1366699227703381874">"<xliff:g id="PRINT_SERVICE_LABEL">%1$s</xliff:g> - <xliff:g id="PRINTER_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="printer_info_desc" msgid="7181988788991581654">"Olunye ulwazi mayelana nale phrinta"</string>
- <string name="print_services_disabled_toast" msgid="1205302482388937547">"Amanye amasevisi wokuphrinta akhutshaziwe."</string>
- <string name="choose_print_service" msgid="3740309762324459694">"Khetha isevisi yephrinta"</string>
+ <string name="print_services_disabled_toast" msgid="9089060734685174685">"Amanye amasevisi okuphrinta akhutshaziwe"</string>
<string name="print_searching_for_printers" msgid="6550424555079932867">"Isesha amaphrinta"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Amasevisi ephrinta akavuliwe."</string>
<string name="print_no_printers" msgid="4869403323900054866">"Awekho amaphrinta atholiwe"</string>
+ <string name="cannot_add_printer" msgid="7840348733668023106">"Ayikwazi ukungeza amaphrinta"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Khetha ukuze ungeze iphrinta"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Khetha ukuze unike amandla"</string>
+ <string name="enabled_services_title" msgid="7036986099096582296">"Amasevisi anikwe amandla"</string>
+ <string name="recommended_services_title" msgid="3799434882937956924">"Amasevisi anconyiwe"</string>
+ <string name="disabled_services_title" msgid="7313253167968363211">"Amasevisi akhutshaziwe"</string>
+ <string name="all_services_title" msgid="5578662754874906455">"Onke amasevisi"</string>
<string name="printing_notification_title_template" msgid="295903957762447362">"Iphrinta i-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Ikhansela i-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Iphutha lephrinta ye-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index d1bec32..47e616e 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -22,8 +22,6 @@
<color name="print_preview_background_color">#F2F1F2</color>
- <color name="promoted_action_background_color">#FF80CBC4</color>
-
<color name="material_grey_500">#ffa3a3a3</color>
</resources>
diff --git a/packages/PrintSpooler/res/values/donottranslate.xml b/packages/PrintSpooler/res/values/donottranslate.xml
index 8069a1d..589043b 100644
--- a/packages/PrintSpooler/res/values/donottranslate.xml
+++ b/packages/PrintSpooler/res/values/donottranslate.xml
@@ -25,4 +25,6 @@
<string name="mediasize_default">ISO_A4</string>
<string name="mediasize_standard">@string/mediasize_standard_iso</string>
+ <string name="uri_package_details">market://details?id=%1$s</string>
+
</resources>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 76292a1..4b56622 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -129,7 +129,7 @@
<!-- Utterance to announce that the search box is hidden. This is spoken to a blind user. [CHAR LIMIT=none] -->
<string name="print_search_box_hidden_utterance">Search box hidden</string>
- <!-- Title of the action bar button to got to add a printer. [CHAR LIMIT=25] -->
+ <!-- Label of add printers button when no printers are found. [CHAR LIMIT=25] -->
<string name="print_add_printer">Add printer</string>
<!-- Title of the menu item to select a printer. [CHAR LIMIT=25] -->
@@ -151,12 +151,7 @@
<string name="printer_info_desc">More information about this printer</string>
<!-- Notification that print services as disabled. [CHAR LIMIT=50] -->
- <string name="print_services_disabled_toast">Some print services are disabled.</string>
-
- <!-- Add printer dialog -->
-
- <!-- Title for the alert dialog for selecting a print service. [CHAR LIMIT=50] -->
- <string name="choose_print_service">Choose print service</string>
+ <string name="print_services_disabled_toast">Some print services are disabled</string>
<!-- Title for the prompt shown as a placeholder if no printers are found while not searching. [CHAR LIMIT=50] -->
<string name="print_searching_for_printers">Searching for printers</string>
@@ -167,6 +162,29 @@
<!-- Title for the prompt shown as a placeholder if there are no printers while searching. [CHAR LIMIT=50] -->
<string name="print_no_printers">No printers found</string>
+ <!-- Add printer activity -->
+
+ <!-- Subtitle for services that cannot add printers. [CHAR LIMIT=50] -->
+ <string name="cannot_add_printer">Cannot add printers</string>
+
+ <!-- Subtitle for services that can add printers. [CHAR LIMIT=50] -->
+ <string name="select_to_add_printers">Select to add printer</string>
+
+ <!-- Subtitle for disabled services. [CHAR LIMIT=50] -->
+ <string name="enable_print_service">Select to enable</string>
+
+ <!-- Header for the list of enabled print services. [CHAR LIMIT=50] -->
+ <string name="enabled_services_title">Enabled services</string>
+
+ <!-- Header for the list of recommended print services. [CHAR LIMIT=50] -->
+ <string name="recommended_services_title">Recommended services</string>
+
+ <!-- Header for the list of disabled print services. [CHAR LIMIT=50] -->
+ <string name="disabled_services_title">Disabled services</string>
+
+ <!-- Label for the list item that links to the list of all print services. [CHAR LIMIT=50] -->
+ <string name="all_services_title">All services</string>
+
<!-- Notifications -->
<!-- Template for the notification label for a printing print job. [CHAR LIMIT=25] -->
diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml
new file mode 100644
index 0000000..1e63a67e
--- /dev/null
+++ b/packages/PrintSpooler/res/values/styles.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.
+-->
+
+<resources>
+ <!-- Preference styles -->
+ <eat-comment/>
+
+ <style name="ListItemSecondary" parent="@android:style/TextAppearance.Material.Body1">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ </style>
+
+ <style name="ListSeparator">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginTop">16dip</item>
+ <item name="android:layout_marginBottom">16dip</item>
+ <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+</resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index 05de5b7..a968ffa 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -15,8 +15,17 @@
-->
<resources>
+ <style name="Theme.AddPrinterActivity" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
+ <item name="android:listSeparatorTextViewStyle">@style/ListSeparator</item>
+ <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
+ </style>
- <style name="PrintActivity" parent="@android:style/Theme.DeviceDefault">
+ <style name="Theme.SelectPrinterActivity"
+ parent="android:style/Theme.DeviceDefault.Light.DarkActionBar">
+ <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
+ </style>
+
+ <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index ac97ad0..f8b1343 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -862,6 +862,9 @@
// Take a note that the content is rendered.
renderedPage.state = RenderedPage.STATE_RENDERED;
+ // Invalidate all caches of the old state of the bitmap
+ mRenderedPage.content.invalidateSelf();
+
// Announce success if needed.
if (mCallback != null) {
mCallback.onPageContentAvailable(renderedPage.content);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
new file mode 100644
index 0000000..f2b3e6e
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
@@ -0,0 +1,563 @@
+/*
+ * 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.printspooler.ui;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ListActivity;
+import android.app.LoaderManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.DataSetObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.print.PrintManager;
+import android.print.PrintServicesLoader;
+import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Adapter;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.printspooler.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This is an activity for adding a printer or. It consists of a list fed from three adapters:
+ * <ul>
+ * <li>{@link #mEnabledServicesAdapter} for all enabled services. If a service has an {@link
+ * PrintServiceInfo#getAddPrintersActivityName() add printer activity} this is started
+ * when the item is clicked.</li>
+ * <li>{@link #mDisabledServicesAdapter} for all disabled services. Once clicked the settings page
+ * for this service is opened.</li>
+ * <li>{@link RecommendedServicesAdapter} for a link to all services. If this item is clicked
+ * the market app is opened to show all print services.</li>
+ * </ul>
+ */
+public class AddPrinterActivity extends ListActivity implements
+ LoaderManager.LoaderCallbacks<List<PrintServiceInfo>>,
+ AdapterView.OnItemClickListener {
+ private static final String LOG_TAG = "AddPrinterActivity";
+
+ /** Ids for the loaders */
+ private static final int LOADER_ID_ENABLED_SERVICES = 1;
+ private static final int LOADER_ID_DISABLED_SERVICES = 2;
+
+ /**
+ * The enabled services list. This is filled from the {@link #LOADER_ID_ENABLED_SERVICES}
+ * loader in {@link #onLoadFinished}.
+ */
+ private EnabledServicesAdapter mEnabledServicesAdapter;
+
+ /**
+ * The disabled services list. This is filled from the {@link #LOADER_ID_DISABLED_SERVICES}
+ * loader in {@link #onLoadFinished}.
+ */
+ private DisabledServicesAdapter mDisabledServicesAdapter;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.add_printer_activity);
+
+ mEnabledServicesAdapter = new EnabledServicesAdapter();
+ mDisabledServicesAdapter = new DisabledServicesAdapter();
+
+ ArrayList<ActionAdapter> adapterList = new ArrayList<>(3);
+ adapterList.add(mEnabledServicesAdapter);
+ adapterList.add(new RecommendedServicesAdapter());
+ adapterList.add(mDisabledServicesAdapter);
+
+ setListAdapter(new CombinedAdapter(adapterList));
+
+ getListView().setOnItemClickListener(this);
+
+ getLoaderManager().initLoader(LOADER_ID_ENABLED_SERVICES, null, this);
+ getLoaderManager().initLoader(LOADER_ID_DISABLED_SERVICES, null, this);
+ // TODO: Load recommended services
+ }
+
+ @Override
+ public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_ID_ENABLED_SERVICES:
+ return new PrintServicesLoader(
+ (PrintManager) getSystemService(Context.PRINT_SERVICE), this,
+ PrintManager.ENABLED_SERVICES);
+ case LOADER_ID_DISABLED_SERVICES:
+ return new PrintServicesLoader(
+ (PrintManager) getSystemService(Context.PRINT_SERVICE), this,
+ PrintManager.DISABLED_SERVICES);
+ // TODO: Load recommended services
+ default:
+ // not reached
+ return null;
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ((ActionAdapter) getListAdapter()).performAction(position);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
+ List<PrintServiceInfo> data) {
+ switch (loader.getId()) {
+ case LOADER_ID_ENABLED_SERVICES:
+ mEnabledServicesAdapter.updateData(data);
+ break;
+ case LOADER_ID_DISABLED_SERVICES:
+ mDisabledServicesAdapter.updateData(data);
+ break;
+ // TODO: Load recommended services
+ default:
+ // not reached
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
+ if (!isFinishing()) {
+ switch (loader.getId()) {
+ case LOADER_ID_ENABLED_SERVICES:
+ mEnabledServicesAdapter.updateData(null);
+ break;
+ case LOADER_ID_DISABLED_SERVICES:
+ mDisabledServicesAdapter.updateData(null);
+ break;
+ // TODO: Reset recommended services
+ default:
+ // not reached
+ }
+ }
+ }
+
+ /**
+ * Marks an adapter that can can perform an action for a position in it's list.
+ */
+ private abstract class ActionAdapter extends BaseAdapter {
+ /**
+ * Perform the action for a position in the list.
+ *
+ * @param position The position of the item
+ */
+ abstract void performAction(@IntRange(from = 0) int position);
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+ }
+
+ /**
+ * An adapter presenting multiple sub adapters as a single combined adapter.
+ */
+ private class CombinedAdapter extends ActionAdapter {
+ /** The adapters to combine */
+ private final @NonNull ArrayList<ActionAdapter> mAdapters;
+
+ /**
+ * Create a combined adapter.
+ *
+ * @param adapters the list of adapters to combine
+ */
+ CombinedAdapter(@NonNull ArrayList<ActionAdapter> adapters) {
+ mAdapters = adapters;
+
+ final int numAdapters = mAdapters.size();
+ for (int i = 0; i < numAdapters; i++) {
+ mAdapters.get(i).registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onInvalidated() {
+ notifyDataSetChanged();
+ }
+ });
+ }
+ }
+
+ @Override
+ public int getCount() {
+ int totalCount = 0;
+
+ final int numAdapters = mAdapters.size();
+ for (int i = 0; i < numAdapters; i++) {
+ totalCount += mAdapters.get(i).getCount();
+ }
+
+ return totalCount;
+ }
+
+ /**
+ * Find the sub adapter and the position in the sub-adapter the position in the combined
+ * adapter refers to.
+ *
+ * @param position The position in the combined adapter
+ *
+ * @return The pair of adapter and position in sub adapter
+ */
+ private @NonNull Pair<ActionAdapter, Integer> getSubAdapter(int position) {
+ final int numAdapters = mAdapters.size();
+ for (int i = 0; i < numAdapters; i++) {
+ ActionAdapter adapter = mAdapters.get(i);
+
+ if (position < adapter.getCount()) {
+ return new Pair<>(adapter, position);
+ } else {
+ position -= adapter.getCount();
+ }
+ }
+
+ throw new IllegalArgumentException("Invalid position");
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ int numLowerViewTypes = 0;
+
+ final int numAdapters = mAdapters.size();
+ for (int i = 0; i < numAdapters; i++) {
+ Adapter adapter = mAdapters.get(i);
+
+ if (position < adapter.getCount()) {
+ return numLowerViewTypes + adapter.getItemViewType(position);
+ } else {
+ numLowerViewTypes += adapter.getViewTypeCount();
+ position -= adapter.getCount();
+ }
+ }
+
+ throw new IllegalArgumentException("Invalid position");
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ int totalViewCount = 0;
+
+ final int numAdapters = mAdapters.size();
+ for (int i = 0; i < numAdapters; i++) {
+ totalViewCount += mAdapters.get(i).getViewTypeCount();
+ }
+
+ return totalViewCount;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
+
+ return realPosition.first.getView(realPosition.second, convertView, parent);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
+
+ return realPosition.first.getItem(realPosition.second);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
+
+ return realPosition.first.isEnabled(realPosition.second);
+ }
+
+ @Override
+ public void performAction(@IntRange(from = 0) int position) {
+ Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
+
+ realPosition.first.performAction(realPosition.second);
+ }
+ }
+
+ /**
+ * Superclass for all adapters that just display a list of {@link PrintServiceInfo}.
+ */
+ private abstract class PrintServiceInfoAdapter extends ActionAdapter {
+ /**
+ * Raw data of the list.
+ *
+ * @see #updateData(List)
+ */
+ private @NonNull List<PrintServiceInfo> mServices;
+
+ /**
+ * Create a new adapter.
+ */
+ PrintServiceInfoAdapter() {
+ mServices = Collections.emptyList();
+ }
+
+ /**
+ * Update the data.
+ *
+ * @param services The new raw data.
+ */
+ void updateData(@Nullable List<PrintServiceInfo> services) {
+ if (services == null || services.isEmpty()) {
+ mServices = Collections.emptyList();
+ } else {
+ mServices = services;
+ }
+
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 2;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ @Override
+ public int getCount() {
+ if (mServices.isEmpty()) {
+ return 0;
+ } else {
+ return mServices.size() + 1;
+ }
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (position == 0) {
+ return null;
+ } else {
+ return mServices.get(position - 1);
+ }
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return position != 0;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+ }
+
+ /**
+ * Adapter for the enabled services.
+ */
+ private class EnabledServicesAdapter extends PrintServiceInfoAdapter {
+ @Override
+ public void performAction(@IntRange(from = 0) int position) {
+ PrintServiceInfo service = (PrintServiceInfo) getItem(position);
+ String addPrinterActivityName = service.getAddPrintersActivityName();
+
+ if (!TextUtils.isEmpty(addPrinterActivityName)) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName(service.getComponentName().getPackageName(),
+ addPrinterActivityName));
+
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(LOG_TAG, "Cannot start add printers activity", e);
+ }
+ }
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (position == 0) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(R.layout.add_printer_list_header,
+ parent, false);
+ }
+
+ ((TextView) convertView.findViewById(R.id.text))
+ .setText(R.string.enabled_services_title);
+
+ return convertView;
+ }
+
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(R.layout.enabled_print_services_list_item,
+ parent, false);
+ }
+
+ PrintServiceInfo service = (PrintServiceInfo) getItem(position);
+
+ TextView title = (TextView) convertView.findViewById(R.id.title);
+ ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+ TextView subtitle = (TextView) convertView.findViewById(R.id.subtitle);
+
+ title.setText(service.getResolveInfo().loadLabel(getPackageManager()));
+ icon.setImageDrawable(service.getResolveInfo().loadIcon(getPackageManager()));
+
+ if (TextUtils.isEmpty(service.getAddPrintersActivityName())) {
+ subtitle.setText(getString(R.string.cannot_add_printer));
+ } else {
+ subtitle.setText(getString(R.string.select_to_add_printers));
+ }
+
+ return convertView;
+ }
+ }
+
+ /**
+ * Adapter for the disabled services.
+ */
+ private class DisabledServicesAdapter extends PrintServiceInfoAdapter {
+ @Override
+ public void performAction(@IntRange(from = 0) int position) {
+ ((PrintManager) getSystemService(Context.PRINT_SERVICE)).setPrintServiceEnabled(
+ ((PrintServiceInfo) getItem(position)).getComponentName(), true);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (position == 0) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(R.layout.add_printer_list_header,
+ parent, false);
+ }
+
+ ((TextView) convertView.findViewById(R.id.text))
+ .setText(R.string.disabled_services_title);
+
+ return convertView;
+ }
+
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.disabled_print_services_list_item, parent, false);
+ }
+
+ PrintServiceInfo service = (PrintServiceInfo) getItem(position);
+
+ TextView title = (TextView) convertView.findViewById(R.id.title);
+ ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+
+ title.setText(service.getResolveInfo().loadLabel(getPackageManager()));
+ icon.setImageDrawable(service.getResolveInfo().loadIcon(getPackageManager()));
+
+ return convertView;
+ }
+ }
+
+ /**
+ * Adapter for the recommended services.
+ */
+ private class RecommendedServicesAdapter extends ActionAdapter {
+ @Override
+ public int getCount() {
+ return 2;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 2;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (position == 0) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(R.layout.add_printer_list_header,
+ parent, false);
+ }
+
+ ((TextView) convertView.findViewById(R.id.text))
+ .setText(R.string.recommended_services_title);
+
+ return convertView;
+ }
+
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(R.layout.all_print_services_list_item,
+ parent, false);
+ }
+
+ return convertView;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return position != 0;
+ }
+
+ @Override
+ public void performAction(@IntRange(from = 0) int position) {
+ String searchUri = Settings.Secure
+ .getString(getContentResolver(), Settings.Secure.PRINT_SERVICE_SEARCH_URI);
+
+ if (searchUri != null) {
+ try {
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)));
+ } catch (ActivityNotFoundException e) {
+ Log.e(LOG_TAG, "Cannot start market", e);
+ }
+ }
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index 46a2098..3b5513ab 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -16,7 +16,10 @@
package com.android.printspooler.ui;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.LoaderManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Loader;
@@ -28,9 +31,11 @@
import android.location.LocationRequest;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.print.PrintManager;
+import android.print.PrintServicesLoader;
import android.print.PrinterDiscoverySession;
import android.print.PrinterDiscoverySession.OnPrintersChangeListener;
import android.print.PrinterId;
@@ -127,11 +132,11 @@
}
}
- public FusedPrintersProvider(Context context) {
- super(context);
+ public FusedPrintersProvider(Activity activity, int internalLoaderId) {
+ super(activity);
mLocationLock = new Object();
- mPersistenceManager = new PersistenceManager(context);
- mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ mPersistenceManager = new PersistenceManager(activity, internalLoaderId);
+ mLocationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
}
public void addHistoricalPrinter(PrinterInfo printer) {
@@ -383,7 +388,6 @@
mPrinters.clear();
if (mDiscoverySession != null) {
mDiscoverySession.destroy();
- mDiscoverySession = null;
}
}
@@ -499,7 +503,8 @@
updatePrinters(mDiscoverySession.getPrinters(), newFavoritePrinters, getCurrentLocation());
}
- private final class PersistenceManager {
+ private final class PersistenceManager implements
+ LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
private static final String PERSIST_FILE_NAME = "printer_history.xml";
private static final String TAG_PRINTERS = "printers";
@@ -520,6 +525,15 @@
private final AtomicFile mStatePersistFile;
+ /**
+ * Whether the enabled print services have been updated since last time the history was
+ * read.
+ */
+ private boolean mAreEnabledServicesUpdated;
+
+ /** The enabled services read when they were last updated */
+ private @NonNull List<PrintServiceInfo> mEnabledServices;
+
private List<Pair<PrinterInfo, Location>> mHistoricalPrinters = new ArrayList<>();
private boolean mReadHistoryCompleted;
@@ -528,9 +542,52 @@
private volatile long mLastReadHistoryTimestamp;
- private PersistenceManager(Context context) {
- mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
+ private PersistenceManager(final Activity activity, final int internalLoaderId) {
+ mStatePersistFile = new AtomicFile(new File(activity.getFilesDir(),
PERSIST_FILE_NAME));
+
+ // Initialize enabled services to make sure they are set are the read task might be done
+ // before the loader updated the services the first time.
+ mEnabledServices = ((PrintManager) activity
+ .getSystemService(Context.PRINT_SERVICE))
+ .getPrintServices(PrintManager.ENABLED_SERVICES);
+
+ mAreEnabledServicesUpdated = true;
+
+ // Cannot start a loader while starting another, hence delay this loader
+ (new Handler(activity.getMainLooper())).post(new Runnable() {
+ @Override
+ public void run() {
+ activity.getLoaderManager().initLoader(internalLoaderId, null,
+ PersistenceManager.this);
+ }
+ });
+ }
+
+
+ @Override
+ public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
+ return new PrintServicesLoader(
+ (PrintManager) getContext().getSystemService(Context.PRINT_SERVICE),
+ getContext(), PrintManager.ENABLED_SERVICES);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
+ List<PrintServiceInfo> services) {
+ mAreEnabledServicesUpdated = true;
+ mEnabledServices = services;
+
+ // Ask the fused printer provider to reload which will cause the persistence manager to
+ // reload the history and reconsider the enabled services.
+ if (isStarted()) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
+ // no data is cached
}
public boolean isReadHistoryInProgress() {
@@ -644,7 +701,8 @@
}
public boolean isHistoryChanged() {
- return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
+ return mAreEnabledServicesUpdated ||
+ mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
}
/**
@@ -738,19 +796,15 @@
}
// Ignore printer records whose target services are not enabled.
- PrintManager printManager = (PrintManager) getContext()
- .getSystemService(Context.PRINT_SERVICE);
- List<PrintServiceInfo> services = printManager
- .getEnabledPrintServices();
-
Set<ComponentName> enabledComponents = new ArraySet<>();
- final int installedServiceCount = services.size();
+ final int installedServiceCount = mEnabledServices.size();
for (int i = 0; i < installedServiceCount; i++) {
- ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
+ ServiceInfo serviceInfo = mEnabledServices.get(i).getResolveInfo().serviceInfo;
ComponentName componentName = new ComponentName(
serviceInfo.packageName, serviceInfo.name);
enabledComponents.add(componentName);
}
+ mAreEnabledServicesUpdated = false;
final int printerCount = printers.size();
for (int i = printerCount - 1; i >= 0; i--) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 64f5cc6..ad4823e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -22,11 +22,13 @@
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentTransaction;
+import android.app.LoaderManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.Loader;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@@ -51,10 +53,12 @@
import android.print.PrintDocumentInfo;
import android.print.PrintJobInfo;
import android.print.PrintManager;
+import android.print.PrintServicesLoader;
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.printservice.PrintService;
+import android.printservice.PrintServiceInfo;
import android.provider.DocumentsContract;
import android.text.Editable;
import android.text.TextUtils;
@@ -94,7 +98,6 @@
import com.android.printspooler.util.MediaSizeUtils;
import com.android.printspooler.util.MediaSizeUtils.MediaSizeComparator;
import com.android.printspooler.util.PageRangeUtils;
-import com.android.printspooler.util.PrintOptionUtils;
import com.android.printspooler.widget.PrintContentView;
import com.android.printspooler.widget.PrintContentView.OptionsStateChangeListener;
import com.android.printspooler.widget.PrintContentView.OptionsStateController;
@@ -113,12 +116,14 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PrintActivity extends Activity implements RemotePrintDocument.UpdateResultCallbacks,
PrintErrorFragment.OnActionListener, PageAdapter.ContentCallbacks,
- OptionsStateChangeListener, OptionsStateController {
+ OptionsStateChangeListener, OptionsStateController,
+ LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
private static final String LOG_TAG = "PrintActivity";
private static final boolean DEBUG = false;
@@ -129,6 +134,10 @@
private static final String HAS_PRINTED_PREF = "has_printed";
+ private static final int LOADER_ID_ENABLED_PRINT_SERVICES = 1;
+ private static final int LOADER_ID_PRINT_REGISTRY = 2;
+ private static final int LOADER_ID_PRINT_REGISTRY_INT = 3;
+
private static final int ORIENTATION_PORTRAIT = 0;
private static final int ORIENTATION_LANDSCAPE = 1;
@@ -139,7 +148,7 @@
private static final int DEST_ADAPTER_MAX_ITEM_COUNT = 9;
private static final int DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF = Integer.MAX_VALUE;
- private static final int DEST_ADAPTER_ITEM_ID_ALL_PRINTERS = Integer.MAX_VALUE - 1;
+ private static final int DEST_ADAPTER_ITEM_ID_MORE = Integer.MAX_VALUE - 1;
private static final int STATE_INITIALIZING = 0;
private static final int STATE_CONFIGURING = 1;
@@ -236,6 +245,15 @@
private int mUiState = UI_STATE_PREVIEW;
+ /** Observer for changes to the printers */
+ private PrintersObserver mPrintersObserver;
+
+ /** Advances options activity name for current printer */
+ private ComponentName mAdvancedPrintOptionsActivity;
+
+ /** Whether at least one print services is enabled or not */
+ private boolean mArePrintServicesEnabled;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -275,6 +293,8 @@
}
}
});
+
+ getLoaderManager().initLoader(LOADER_ID_ENABLED_PRINT_SERVICES, null, this);
}
private void onConnectedToPrintSpooler(final IBinder documentAdapter) {
@@ -289,7 +309,7 @@
public void run() {
onPrinterRegistryReady(documentAdapter);
}
- });
+ }, LOADER_ID_PRINT_REGISTRY, LOADER_ID_PRINT_REGISTRY_INT);
}
private void onPrinterRegistryReady(IBinder documentAdapter) {
@@ -713,15 +733,12 @@
}
private void startAdvancedPrintOptionsActivity(PrinterInfo printer) {
- ComponentName serviceName = printer.getId().getServiceName();
-
- String activityName = PrintOptionUtils.getAdvancedOptionsActivityName(this, serviceName);
- if (TextUtils.isEmpty(activityName)) {
+ if (mAdvancedPrintOptionsActivity == null) {
return;
}
Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setComponent(new ComponentName(serviceName.getPackageName(), activityName));
+ intent.setComponent(mAdvancedPrintOptionsActivity);
List<ResolveInfo> resolvedActivities = getPackageManager()
.queryIntentActivities(intent, 0);
@@ -731,7 +748,10 @@
// The activity is a component name, therefore it is one or none.
if (resolvedActivities.get(0).activityInfo.exported) {
- intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, mPrintJob);
+ PrintJobInfo.Builder printJobBuilder = new PrintJobInfo.Builder(mPrintJob);
+ printJobBuilder.setPages(mSelectedPages);
+
+ intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, printJobBuilder.build());
intent.putExtra(PrintService.EXTRA_PRINTER_INFO, printer);
intent.putExtra(PrintService.EXTRA_PRINT_DOCUMENT_INFO,
mPrintedDocument.getDocumentInfo().info);
@@ -759,10 +779,14 @@
// Take the advanced options without interpretation.
mPrintJob.setAdvancedOptions(printJobInfo.getAdvancedOptions());
- // Take copies without interpretation as the advanced print dialog
- // cannot create a print job info with invalid copies.
- mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies()));
- mPrintJob.setCopies(printJobInfo.getCopies());
+ if (printJobInfo.getCopies() < 1) {
+ Log.w(LOG_TAG, "Cannot apply return value from advanced options activity. Copies " +
+ "must be 1 or more. Actual value is: " + printJobInfo.getCopies() + ". " +
+ "Ignoring.");
+ } else {
+ mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies()));
+ mPrintJob.setCopies(printJobInfo.getCopies());
+ }
PrintAttributes currAttributes = mPrintJob.getAttributes();
PrintAttributes newAttributes = printJobInfo.getAttributes();
@@ -771,7 +795,7 @@
// Take the media size only if the current printer supports is.
MediaSize oldMediaSize = currAttributes.getMediaSize();
MediaSize newMediaSize = newAttributes.getMediaSize();
- if (!oldMediaSize.equals(newMediaSize)) {
+ if (newMediaSize != null && !oldMediaSize.equals(newMediaSize)) {
final int mediaSizeCount = mMediaSizeSpinnerAdapter.getCount();
MediaSize newMediaSizePortrait = newAttributes.getMediaSize().asPortrait();
for (int i = 0; i < mediaSizeCount; i++) {
@@ -1184,7 +1208,8 @@
mCopiesEditText.addTextChangedListener(new EditTextWatcher());
// Destination.
- mDestinationSpinnerAdapter.registerDataSetObserver(new PrintersObserver());
+ mPrintersObserver = new PrintersObserver();
+ mDestinationSpinnerAdapter.registerDataSetObserver(mPrintersObserver);
mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
mDestinationSpinner.setOnItemSelectedListener(itemSelectedListener);
@@ -1272,6 +1297,59 @@
}
}
+ @Override
+ public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
+ return new PrintServicesLoader((PrintManager) getSystemService(Context.PRINT_SERVICE), this,
+ PrintManager.ENABLED_SERVICES);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
+ List<PrintServiceInfo> services) {
+ ComponentName newAdvancedPrintOptionsActivity = null;
+ if (mCurrentPrinter != null && services != null) {
+ final int numServices = services.size();
+ for (int i = 0; i < numServices; i++) {
+ PrintServiceInfo service = services.get(i);
+
+ if (service.getComponentName().equals(mCurrentPrinter.getId().getServiceName())) {
+ String advancedOptionsActivityName = service.getAdvancedOptionsActivityName();
+
+ if (!TextUtils.isEmpty(advancedOptionsActivityName)) {
+ newAdvancedPrintOptionsActivity = new ComponentName(
+ service.getComponentName().getPackageName(),
+ advancedOptionsActivityName);
+
+ break;
+ }
+ }
+ }
+ }
+
+ if (!Objects.equals(newAdvancedPrintOptionsActivity, mAdvancedPrintOptionsActivity)) {
+ mAdvancedPrintOptionsActivity = newAdvancedPrintOptionsActivity;
+ updateOptionsUi();
+ }
+
+ boolean newArePrintServicesEnabled = services != null && !services.isEmpty();
+ if (mArePrintServicesEnabled != newArePrintServicesEnabled) {
+ mArePrintServicesEnabled = newArePrintServicesEnabled;
+
+ // Reload mDestinationSpinnerAdapter as mArePrintServicesEnabled changed and the adapter
+ // reads that in DestinationAdapter#getMoreItemTitle
+ if (mDestinationSpinnerAdapter != null) {
+ mDestinationSpinnerAdapter.notifyDataSetChanged();
+ }
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
+ if (!isFinishing()) {
+ onLoadFinished(loader, null);
+ }
+ }
+
/**
* A dialog that asks the user to approve a {@link PrintService}. This dialog is automatically
* dismissed if the same {@link PrintService} gets approved by another
@@ -1711,9 +1789,7 @@
}
// Advanced print options
- ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
- if (!TextUtils.isEmpty(PrintOptionUtils.getAdvancedOptionsActivityName(
- this, serviceName))) {
+ if (mAdvancedPrintOptionsActivity != null) {
mMoreOptionsButton.setVisibility(View.VISIBLE);
mMoreOptionsButton.setEnabled(true);
} else {
@@ -1962,6 +2038,10 @@
mPrinterRegistry.setTrackedPrinter(null);
}
+ if (mPrintersObserver != null) {
+ mDestinationSpinnerAdapter.unregisterDataSetObserver(mPrintersObserver);
+ }
+
if (mState != STATE_INITIALIZING) {
mProgressMessageController.cancel();
mSpoolerProvider.destroy();
@@ -2201,14 +2281,14 @@
if (position == 0) {
return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
} else if (position == 1) {
- return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
+ return DEST_ADAPTER_ITEM_ID_MORE;
}
} else {
if (position == 1) {
return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
}
if (position == getCount() - 1) {
- return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
+ return DEST_ADAPTER_ITEM_ID_MORE;
}
}
return position;
@@ -2221,6 +2301,14 @@
return view;
}
+ private String getMoreItemTitle() {
+ if (mArePrintServicesEnabled) {
+ return getString(R.string.all_printers);
+ } else {
+ return getString(R.string.print_add_printer);
+ }
+ }
+
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (mShowDestinationPrompt) {
@@ -2249,7 +2337,7 @@
title = printerHolder.printer.getName();
icon = getResources().getDrawable(R.drawable.ic_menu_savetopdf, null);
} else if (position == 1) {
- title = getString(R.string.all_printers);
+ title = getMoreItemTitle();
}
} else {
if (position == 1 && getPdfPrinter() != null) {
@@ -2257,7 +2345,7 @@
title = printerHolder.printer.getName();
icon = getResources().getDrawable(R.drawable.ic_menu_savetopdf, null);
} else if (position == getCount() - 1) {
- title = getString(R.string.all_printers);
+ title = getMoreItemTitle();
} else {
PrinterHolder printerHolder = (PrinterHolder) getItem(position);
PrinterInfo printInfo = printerHolder.printer;
@@ -2292,7 +2380,7 @@
}
iconView.setImageDrawable(icon);
} else {
- iconView.setVisibility(View.GONE);
+ iconView.setVisibility(View.INVISIBLE);
}
return convertView;
@@ -2337,6 +2425,7 @@
PrinterInfo updatedPrinter = newPrintersMap.remove(oldPrinterId);
if (updatedPrinter != null) {
printerHolder.printer = updatedPrinter;
+ printerHolder.removed = false;
} else {
printerHolder.removed = true;
}
@@ -2442,6 +2531,7 @@
PrinterCapabilitiesInfo oldCapab = oldPrinterState.getCapabilities();
PrinterCapabilitiesInfo newCapab = newPrinterState.getCapabilities();
+ final boolean hadCabab = oldCapab != null;
final boolean hasCapab = newCapab != null;
final boolean gotCapab = oldCapab == null && newCapab != null;
final boolean lostCapab = oldCapab != null && newCapab == null;
@@ -2460,23 +2550,31 @@
mCurrentPrinter = newPrinterState;
- if ((isActive && gotCapab) || (becameActive && hasCapab)) {
- if (hasCapab && capabChanged) {
- updatePrintAttributesFromCapabilities(newCapab);
- updatePrintPreviewController(false);
- }
- onPrinterAvailable(newPrinterState);
- } else if ((becameInactive && hasCapab) || (isActive && lostCapab)) {
- onPrinterUnavailable(newPrinterState);
- }
-
final boolean updateNeeded = ((capabChanged && hasCapab && isActive)
|| (becameActive && hasCapab) || (isActive && gotCapab));
+ if (capabChanged && hasCapab) {
+ updatePrintAttributesFromCapabilities(newCapab);
+ }
+
+ if (updateNeeded) {
+ updatePrintPreviewController(false);
+ }
+
+ if ((isActive && gotCapab) || (becameActive && hasCapab)) {
+ onPrinterAvailable(newPrinterState);
+ } else if ((becameInactive && hadCabab) || (isActive && lostCapab)) {
+ onPrinterUnavailable(newPrinterState);
+ }
+
if (updateNeeded && canUpdateDocument()) {
updateDocument(false);
}
+ // Force a reload of the enabled print services to update mAdvancedPrintOptionsActivity
+ // in onLoadFinished();
+ getLoaderManager().getLoader(LOADER_ID_ENABLED_PRINT_SERVICES).forceLoad();
+
updateOptionsUi();
updateSummary();
}
@@ -2502,7 +2600,7 @@
return;
}
- if (id == DEST_ADAPTER_ITEM_ID_ALL_PRINTERS) {
+ if (id == DEST_ADAPTER_ITEM_ID_MORE) {
startSelectPrinterActivity();
return;
}
@@ -2536,6 +2634,10 @@
}
mPrinterAvailabilityDetector.updatePrinter(currentPrinter);
+
+ // Force a reload of the enabled print services to update
+ // mAdvancedPrintOptionsActivity in onLoadFinished();
+ getLoaderManager().getLoader(LOADER_ID_ENABLED_PRINT_SERVICES).forceLoad();
} else if (spinner == mMediaSizeSpinner) {
SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
PrintAttributes attributes = mPrintJob.getAttributes();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
index 6d60bb8..86366dd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
@@ -33,7 +33,7 @@
public class PrinterRegistry {
- private static final int LOADER_ID_PRINTERS_LOADER = 1;
+ private final int mLoaderId;
private final Activity mActivity;
@@ -52,12 +52,17 @@
public void onPrintersInvalid();
}
- public PrinterRegistry(Activity activity, Runnable readyCallback) {
+ public PrinterRegistry(Activity activity, Runnable readyCallback, int loaderId,
+ int internalLoaderId) {
+ mLoaderId = loaderId;
mActivity = activity;
mReadyCallback = readyCallback;
mHandler = new MyHandler(activity.getMainLooper());
- activity.getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER,
- null, mLoaderCallbacks);
+
+ Bundle loaderData = new Bundle(1);
+ loaderData.putInt(null, internalLoaderId);
+
+ activity.getLoaderManager().initLoader(loaderId, loaderData, mLoaderCallbacks);
}
public void setOnPrintersChangeListener(OnPrintersChangeListener listener) {
@@ -106,7 +111,7 @@
}
private FusedPrintersProvider getPrinterProvider() {
- Loader<?> loader = mActivity.getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
+ Loader<?> loader = mActivity.getLoaderManager().getLoader(mLoaderId);
return (FusedPrintersProvider) loader;
}
@@ -114,38 +119,34 @@
new LoaderCallbacks<List<PrinterInfo>>() {
@Override
public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
- if (loader.getId() == LOADER_ID_PRINTERS_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();
- }
+ 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();
}
}
// LoaderCallbacks#onLoadFinished
@Override
public void onLoadFinished(Loader<List<PrinterInfo>> loader, List<PrinterInfo> printers) {
- if (loader.getId() == LOADER_ID_PRINTERS_LOADER) {
- 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();
- }
- if (!mReady) {
- mReady = true;
- if (mReadyCallback != null) {
- mReadyCallback.run();
- }
+ 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();
+ }
+ if (!mReady) {
+ mReady = true;
+ if (mReadyCallback != null) {
+ mReadyCallback.run();
}
}
}
@@ -153,10 +154,7 @@
// LoaderCallbacks#onCreateLoader
@Override
public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
- if (id == LOADER_ID_PRINTERS_LOADER) {
- return new FusedPrintersProvider(mActivity);
- }
- return null;
+ return new FusedPrintersProvider(mActivity, args.getInt(null));
}
};
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index 4f7624a..e53a522 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -17,31 +17,18 @@
package com.android.printspooler.ui;
import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
+import android.app.LoaderManager;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
-import android.content.pm.ActivityInfo;
+import android.content.Loader;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
import android.print.PrintManager;
+import android.print.PrintServicesLoader;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.printservice.PrintServiceInfo;
@@ -59,17 +46,16 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.TextView;
import android.widget.Toast;
-import com.android.internal.content.PackageMonitor;
import com.android.printspooler.R;
import java.util.ArrayList;
@@ -78,35 +64,33 @@
/**
* This is an activity for selecting a printer.
*/
-public final class SelectPrinterActivity extends Activity {
+public final class SelectPrinterActivity extends Activity implements
+ LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
private static final String LOG_TAG = "SelectPrinterFragment";
+ private static final int LOADER_ID_PRINT_REGISTRY = 1;
+ private static final int LOADER_ID_PRINT_REGISTRY_INT = 2;
+ private static final int LOADER_ID_ENABLED_PRINT_SERVICES = 3;
+
public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
- private static final String FRAGMENT_TAG_ADD_PRINTER_DIALOG =
- "FRAGMENT_TAG_ADD_PRINTER_DIALOG";
-
- private static final String FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS =
- "FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS";
-
private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
+ private static final String KEY_NOT_FIRST_CREATE = "KEY_NOT_FIRST_CREATE";
+
/** If there are any enabled print services */
private boolean mHasEnabledPrintServices;
- private final ArrayList<PrintServiceInfo> mAddPrinterServices =
- new ArrayList<>();
-
private PrinterRegistry mPrinterRegistry;
private ListView mListView;
private AnnounceFilterResult mAnnounceFilterResult;
- /** Monitor if new print services get enabled or disabled */
- private ContentObserver mPrintServicesDisabledObserver;
- private PackageMonitor mPackageObserver;
+ private void startAddPrinterActivity() {
+ startActivity(new Intent(this, AddPrinterActivity.class));
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -115,7 +99,8 @@
setContentView(R.layout.select_printer_activity);
- mPrinterRegistry = new PrinterRegistry(this, null);
+ mPrinterRegistry = new PrinterRegistry(this, null, LOADER_ID_PRINT_REGISTRY,
+ LOADER_ID_PRINT_REGISTRY_INT);
// Hook up the list view.
mListView = (ListView) findViewById(android.R.id.list);
@@ -145,22 +130,67 @@
}
PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
- onPrinterSelected(printer.getId());
+
+ if (printer == null) {
+ startAddPrinterActivity();
+ } else {
+ onPrinterSelected(printer.getId());
+ }
+ }
+ });
+
+ findViewById(R.id.button).setOnClickListener(new OnClickListener() {
+ @Override public void onClick(View v) {
+ startAddPrinterActivity();
}
});
registerForContextMenu(mListView);
- // Display a notification about disabled services if there are disabled services
- String disabledServicesSetting = Settings.Secure.getString(getContentResolver(),
- Settings.Secure.DISABLED_PRINT_SERVICES);
- if (!TextUtils.isEmpty(disabledServicesSetting)) {
- Toast.makeText(this, getString(R.string.print_services_disabled_toast),
- Toast.LENGTH_LONG).show();
+ getLoaderManager().initLoader(LOADER_ID_ENABLED_PRINT_SERVICES, null, this);
+
+ // On first creation:
+ //
+ // If no services are installed, instantly open add printer dialog.
+ // If some are disabled and some are enabled show a toast to notify the user
+ if (savedInstanceState == null || !savedInstanceState.getBoolean(KEY_NOT_FIRST_CREATE)) {
+ List<PrintServiceInfo> allServices =
+ ((PrintManager) getSystemService(Context.PRINT_SERVICE))
+ .getPrintServices(PrintManager.ALL_SERVICES);
+ boolean hasEnabledServices = false;
+ boolean hasDisabledServices = false;
+
+ if (allServices != null) {
+ final int numServices = allServices.size();
+ for (int i = 0; i < numServices; i++) {
+ if (allServices.get(i).isEnabled()) {
+ hasEnabledServices = true;
+ } else {
+ hasDisabledServices = true;
+ }
+ }
+ }
+
+ if (!hasEnabledServices) {
+ startAddPrinterActivity();
+ } else if (hasDisabledServices) {
+ String disabledServicesSetting = Settings.Secure.getString(getContentResolver(),
+ Settings.Secure.DISABLED_PRINT_SERVICES);
+ if (!TextUtils.isEmpty(disabledServicesSetting)) {
+ Toast.makeText(this, getString(R.string.print_services_disabled_toast),
+ Toast.LENGTH_LONG).show();
+ }
+ }
}
}
@Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_NOT_FIRST_CREATE, true);
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
@@ -249,60 +279,13 @@
* Adjust the UI if the enabled print services changed.
*/
private synchronized void onPrintServicesUpdate() {
- updateServicesWithAddPrinterActivity();
updateEmptyView((DestinationAdapter)mListView.getAdapter());
invalidateOptionsMenu();
}
- /**
- * Register listener for changes to the enabled print services.
- */
- private void registerServiceMonitor() {
- // Listen for services getting disabled
- mPrintServicesDisabledObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- onPrintServicesUpdate();
- }
- };
-
- // Listen for services getting installed or uninstalled
- mPackageObserver = new PackageMonitor() {
- @Override
- public void onPackageModified(String packageName) {
- onPrintServicesUpdate();
- }
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- onPrintServicesUpdate();
- }
-
- @Override
- public void onPackageAdded(String packageName, int uid) {
- onPrintServicesUpdate();
- }
- };
-
- getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.DISABLED_PRINT_SERVICES), false,
- mPrintServicesDisabledObserver);
-
- mPackageObserver.register(this, getMainLooper(), false);
- }
-
- /**
- * Unregister the listeners for changes to the enabled print services.
- */
- private void unregisterServiceMonitorIfNeeded() {
- getContentResolver().unregisterContentObserver(mPrintServicesDisabledObserver);
- mPackageObserver.unregister();
- }
-
@Override
public void onStart() {
super.onStart();
- registerServiceMonitor();
onPrintServicesUpdate();
}
@@ -316,19 +299,9 @@
@Override
public void onStop() {
- unregisterServiceMonitorIfNeeded();
super.onStop();
}
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == R.id.action_add_printer) {
- showAddPrinterSelectionDialog();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
private void onPrinterSelected(PrinterId printerId) {
Intent intent = new Intent();
intent.putExtra(INTENT_EXTRA_PRINTER_ID, printerId);
@@ -336,68 +309,6 @@
finish();
}
- private void updateServicesWithAddPrinterActivity() {
- mHasEnabledPrintServices = true;
- mAddPrinterServices.clear();
-
- // Get all enabled print services.
- PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
- List<PrintServiceInfo> enabledServices = printManager.getEnabledPrintServices();
-
- // No enabled print services - done.
- if (enabledServices.isEmpty()) {
- mHasEnabledPrintServices = false;
- return;
- }
-
- // Find the services with valid add printers activities.
- final int enabledServiceCount = enabledServices.size();
- for (int i = 0; i < enabledServiceCount; i++) {
- PrintServiceInfo enabledService = enabledServices.get(i);
-
- // No add printers activity declared - next.
- if (TextUtils.isEmpty(enabledService.getAddPrintersActivityName())) {
- continue;
- }
-
- ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo;
- ComponentName addPrintersComponentName = new ComponentName(
- serviceInfo.packageName, enabledService.getAddPrintersActivityName());
- Intent addPritnersIntent = new Intent()
- .setComponent(addPrintersComponentName);
-
- // The add printers activity is valid - add it.
- PackageManager pm = getPackageManager();
- List<ResolveInfo> resolvedActivities = pm.queryIntentActivities(addPritnersIntent, 0);
- if (!resolvedActivities.isEmpty()) {
- // The activity is a component name, therefore it is one or none.
- ActivityInfo activityInfo = resolvedActivities.get(0).activityInfo;
- if (activityInfo.exported
- && (activityInfo.permission == null
- || pm.checkPermission(activityInfo.permission, getPackageName())
- == PackageManager.PERMISSION_GRANTED)) {
- mAddPrinterServices.add(enabledService);
- }
- }
- }
- }
-
- private void showAddPrinterSelectionDialog() {
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- Fragment oldFragment = getFragmentManager().findFragmentByTag(
- FRAGMENT_TAG_ADD_PRINTER_DIALOG);
- if (oldFragment != null) {
- transaction.remove(oldFragment);
- }
- AddPrinterAlertDialogFragment newFragment = new AddPrinterAlertDialogFragment();
- Bundle arguments = new Bundle();
- arguments.putParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS,
- mAddPrinterServices);
- newFragment.setArguments(arguments);
- transaction.add(newFragment, FRAGMENT_TAG_ADD_PRINTER_DIALOG);
- transaction.commit();
- }
-
public void updateEmptyView(DestinationAdapter adapter) {
if (mListView.getEmptyView() == null) {
View emptyView = findViewById(R.id.empty_print_state);
@@ -426,71 +337,28 @@
}
}
- public static class AddPrinterAlertDialogFragment extends DialogFragment {
+ @Override
+ public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
+ return new PrintServicesLoader((PrintManager) getSystemService(Context.PRINT_SERVICE), this,
+ PrintManager.ENABLED_SERVICES);
+ }
- private String mAddPrintServiceItem;
+ @Override
+ public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
+ List<PrintServiceInfo> data) {
+ if (data == null || data.isEmpty()) {
+ mHasEnabledPrintServices = false;
+ } else {
+ mHasEnabledPrintServices = true;
+ }
- @Override
- @SuppressWarnings("unchecked")
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.choose_print_service);
+ onPrintServicesUpdate();
+ }
- final List<PrintServiceInfo> printServices = (List<PrintServiceInfo>) (List<?>)
- getArguments().getParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS);
-
- final ArrayAdapter<String> adapter = new ArrayAdapter<>(
- getActivity(), android.R.layout.simple_list_item_1);
- final int printServiceCount = printServices.size();
- for (int i = 0; i < printServiceCount; i++) {
- PrintServiceInfo printService = printServices.get(i);
- adapter.add(printService.getResolveInfo().loadLabel(
- getActivity().getPackageManager()).toString());
- }
-
- final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(),
- Settings.Secure.PRINT_SERVICE_SEARCH_URI);
- final Intent viewIntent;
- if (!TextUtils.isEmpty(searchUri)) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
- if (getActivity().getPackageManager().resolveActivity(intent, 0) != null) {
- viewIntent = intent;
- mAddPrintServiceItem = getString(R.string.add_print_service_label);
- adapter.add(mAddPrintServiceItem);
- } else {
- viewIntent = null;
- }
- } else {
- viewIntent = null;
- }
-
- builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String item = adapter.getItem(which);
- if (item.equals(mAddPrintServiceItem)) {
- try {
- startActivity(viewIntent);
- } catch (ActivityNotFoundException anfe) {
- Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
- }
- } else {
- PrintServiceInfo printService = printServices.get(which);
- ComponentName componentName = new ComponentName(
- printService.getResolveInfo().serviceInfo.packageName,
- printService.getAddPrintersActivityName());
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setComponent(componentName);
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException anfe) {
- Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
- }
- }
- }
- });
-
- return builder.create();
+ @Override
+ public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
+ if (!isFinishing()) {
+ onLoadFinished(loader, null);
}
}
@@ -592,14 +460,40 @@
@Override
public int getCount() {
synchronized (mLock) {
- return mFilteredPrinters.size();
+ if (mFilteredPrinters.isEmpty()) {
+ return 0;
+ } else {
+ // Add "add printer" item to the end of the list. If the list is empty there is
+ // a link on the empty view
+ return mFilteredPrinters.size() + 1;
+ }
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 2;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ // Use separate view types for the "add printer" item an the items referring to printers
+ if (getItem(position) == null) {
+ return 0;
+ } else {
+ return 1;
}
}
@Override
public Object getItem(int position) {
synchronized (mLock) {
- return mFilteredPrinters.get(position);
+ if (position < mFilteredPrinters.size()) {
+ return mFilteredPrinters.get(position);
+ } else {
+ // Return null to mark this as the "add printer item"
+ return null;
+ }
}
}
@@ -615,6 +509,18 @@
@Override
public View getView(int position, View convertView, ViewGroup parent) {
+ final PrinterInfo printer = (PrinterInfo) getItem(position);
+
+ // Handle "add printer item"
+ if (printer == null) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(R.layout.add_printer_list_item,
+ parent, false);
+ }
+
+ return convertView;
+ }
+
if (convertView == null) {
convertView = getLayoutInflater().inflate(
R.layout.printer_list_item, parent, false);
@@ -622,7 +528,6 @@
convertView.setEnabled(isActionable(position));
- final PrinterInfo printer = (PrinterInfo) getItem(position);
CharSequence title = printer.getName();
Drawable icon = printer.loadIcon(SelectPrinterActivity.this);
@@ -661,7 +566,7 @@
subtitleView.setVisibility(View.GONE);
}
- ImageView moreInfoView = (ImageView) convertView.findViewById(R.id.more_info);
+ LinearLayout moreInfoView = (LinearLayout) convertView.findViewById(R.id.more_info);
if (printer.getInfoIntent() != null) {
moreInfoView.setVisibility(View.VISIBLE);
moreInfoView.setOnClickListener(new OnClickListener() {
@@ -699,7 +604,12 @@
public boolean isActionable(int position) {
PrinterInfo printer = (PrinterInfo) getItem(position);
- return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
+
+ if (printer == null) {
+ return true;
+ } else {
+ return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
+ }
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java
deleted file mode 100644
index 446952d..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java
+++ /dev/null
@@ -1,56 +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.printspooler.util;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ServiceInfo;
-import android.print.PrintManager;
-import android.printservice.PrintServiceInfo;
-
-import java.util.List;
-
-public class PrintOptionUtils {
-
- private PrintOptionUtils() {
- /* ignore - hide constructor */
- }
-
- /**
- * Gets the advanced options activity name for a print service.
- *
- * @param context Context for accessing system resources.
- * @param serviceName The print service name.
- * @return The advanced options activity name or null.
- */
- public static String getAdvancedOptionsActivityName(Context context,
- ComponentName serviceName) {
- PrintManager printManager = (PrintManager) context.getSystemService(
- Context.PRINT_SERVICE);
- List<PrintServiceInfo> printServices = printManager.getEnabledPrintServices();
- final int printServiceCount = printServices.size();
- for (int i = 0; i < printServiceCount; i ++) {
- PrintServiceInfo printServiceInfo = printServices.get(i);
- ServiceInfo serviceInfo = printServiceInfo.getResolveInfo().serviceInfo;
- if (serviceInfo.name.equals(serviceName.getClassName())
- && serviceInfo.packageName.equals(serviceName.getPackageName())) {
- return printServiceInfo.getAdvancedOptionsActivityName();
- }
- }
- return null;
- }
-}
diff --git a/packages/SettingsLib/res/layout/restricted_icon.xml b/packages/SettingsLib/res/layout/restricted_icon.xml
index d57fb80..724a524 100644
--- a/packages/SettingsLib/res/layout/restricted_icon.xml
+++ b/packages/SettingsLib/res/layout/restricted_icon.xml
@@ -17,5 +17,4 @@
android:id="@+id/restricted_icon"
android:layout_width="@dimen/restricted_icon_size"
android:layout_height="@dimen/restricted_icon_size"
- android:src="@drawable/ic_info"
- android:gravity="end|center_vertical" />
\ No newline at end of file
+ android:src="@drawable/ic_info" />
\ 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 9e5d029..67296a6 100644
--- a/packages/SettingsLib/res/layout/settings_with_drawer.xml
+++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml
@@ -17,14 +17,15 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:background="?android:attr/colorPrimaryDark">
<!-- The main content view -->
<LinearLayout
android:id="@+id/content_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:fitsSystemWindows="false" >
+ android:fitsSystemWindows="true" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -36,14 +37,14 @@
android:layout_height="wrap_content"
android:navigationContentDescription="@*android:string/action_bar_up_description"
android:theme="?android:attr/actionBarTheme"
- android:paddingTop="@*android:dimen/status_bar_height"
style="?android:attr/toolbarStyle"
android:background="?android:attr/colorPrimary" />
</FrameLayout>
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
- android:layout_height="fill_parent" />
+ android:layout_height="fill_parent"
+ android:background="?android:attr/windowBackground" />
</LinearLayout>
<!-- The navigation drawer -->
<ListView android:id="@+id/left_drawer"
diff --git a/packages/SettingsLib/res/layout/usage_view.xml b/packages/SettingsLib/res/layout/usage_view.xml
index 28981f8..aa1a046 100644
--- a/packages/SettingsLib/res/layout/usage_view.xml
+++ b/packages/SettingsLib/res/layout/usage_view.xml
@@ -22,7 +22,8 @@
<LinearLayout
android:id="@+id/graph_label_group"
android:layout_width="match_parent"
- android:layout_height="@dimen/usage_graph_area_height"
+ android:layout_height="0dp"
+ android:layout_weight="1"
android:orientation="horizontal"
android:clipChildren="false"
android:clipToPadding="false">
@@ -37,6 +38,7 @@
layout="@layout/usage_side_label" />
<Space
+ android:id="@+id/space1"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1" />
@@ -45,6 +47,7 @@
layout="@layout/usage_side_label" />
<Space
+ android:id="@+id/space2"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1" />
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 94e376c8..a408c08 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laai nie"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Vol"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Beheer deur administrateur"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Geaktiveer deur administrateur"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Gedeaktiveer deur administrateur"</string>
<string name="home" msgid="8263346537524314127">"Tuis"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> gelede"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> oor"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index c605d2d..7ae97e2 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ኃይል እየሞላ አይደለም"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ሙሉነው"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"በአስተዳዳሪ ቁጥጥር የተደረገበት"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"በአስተዳዳሪ የነቃ"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"በአስተዳዳሪ የተሰናከለ"</string>
<string name="home" msgid="8263346537524314127">"መነሻ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"ከ<xliff:g id="ID_1">%1$s</xliff:g> በፊት"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ቀርቷል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 975051a..eb76e1f 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"لا يتم الشحن"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ممتلئة"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"إعدادات يتحكم فيها المشرف"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"تم التمكين بواسطة المشرف"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"تم التعطيل بواسطة المشرف"</string>
<string name="home" msgid="8263346537524314127">"الشاشة الرئيسية"</string>
<string name="charge_length_format" msgid="8978516217024434156">"قبل <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"يتبقى <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index 3f78fa2..9903397 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Enerji doldurulmur"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Tam"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Admin tərəfindən nəzarət olunur"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Administrator tərəfindən aktiv edildi"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Administrator tərəfindən deaktiv edildi"</string>
<string name="home" msgid="8263346537524314127">"Əsas səhifə"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> əvvəl"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> qalıb"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index a38542d..f570d09 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontroliše administrator"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Omogućio je administrator"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Onemogućio je administrator"</string>
<string name="home" msgid="8263346537524314127">"Početni"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Pre <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Još <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index e3c8d8a..72652c9 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се зарежда"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Пълна"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролира се от администратор"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Активирано от администратора"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Деактивирано от администратора"</string>
<string name="home" msgid="8263346537524314127">"Начало"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Преди <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Оставащо време: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index d3c9e6b..b863773 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"চার্জ হচ্ছে না"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"পূর্ণ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"প্রশাসকের দ্বারা নিয়ন্ত্রিত"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"প্রশাসক সক্ষম করেছেন"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"প্রশাসক অক্ষম করেছেন"</string>
<string name="home" msgid="8263346537524314127">"হোম"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> আগে"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> বাকী আছে"</string>
diff --git a/packages/SettingsLib/res/values-bs-rBA/strings.xml b/packages/SettingsLib/res/values-bs-rBA/strings.xml
index 673b08b..542c3ce 100644
--- a/packages/SettingsLib/res/values-bs-rBA/strings.xml
+++ b/packages/SettingsLib/res/values-bs-rBA/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Pod kontrolom administratora"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Omogućio administrator"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Onemogućio je administrator"</string>
<string name="home" msgid="8263346537524314127">"Početna stranica"</string>
<string name="charge_length_format" msgid="8978516217024434156">"prije <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Još otprilike <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index d4d2f6c..e2c4495 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"No s\'està carregant"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Plena"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlat per l\'administrador"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Opció activada per l\'administrador"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Opció desactivada per l\'administrador"</string>
<string name="home" msgid="8263346537524314127">"Inici"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Fa <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Temps restant: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 00d4f47..f713ceb 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíjí se"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Spravováno administrátorem"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Povoleno administrátorem"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Zakázáno administrátorem"</string>
<string name="home" msgid="8263346537524314127">"Plocha"</string>
<string name="charge_length_format" msgid="8978516217024434156">"před <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Zbývající čas: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 398875a..d1d7b23 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Oplader ikke"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fuld"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolleret af administratoren"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Aktiveret af administratoren"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Deaktiveret af administratoren"</string>
<string name="home" msgid="8263346537524314127">"Start"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> siden"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> tilbage"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 2a56425..fbd1207 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wird nicht geladen"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Voll"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Durch den Administrator verwaltet"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Vom Administrator aktiviert"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Vom Administrator deaktiviert"</string>
<string name="home" msgid="8263346537524314127">"Startseite"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Vor <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Noch <xliff:g id="ID_1">%1$s</xliff:g> verbleibend"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index dd6bb4f..dce2cf7 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Δεν φορτίζει"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Πλήρης"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Ελέγχονται από το διαχειριστή"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Ενεργοποιήθηκε από το διαχειριστή"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Απενεργοποιήθηκε από το διαχειριστή"</string>
<string name="home" msgid="8263346537524314127">"Αρχική οθόνη"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Πριν από <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Απομένουν <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 1ed6dbd..4821a71 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Enabled by administrator"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Disabled by administrator"</string>
<string name="home" msgid="8263346537524314127">"Home"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 1ed6dbd..4821a71 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Enabled by administrator"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Disabled by administrator"</string>
<string name="home" msgid="8263346537524314127">"Home"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 1ed6dbd..4821a71 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Enabled by administrator"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Disabled by administrator"</string>
<string name="home" msgid="8263346537524314127">"Home"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 3f2befd..2d0ce02 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se realiza la carga"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Cargado"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada por el administrador"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Habilitada por el administrador"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Inhabilitada por el administrador"</string>
<string name="home" msgid="8263346537524314127">"Página principal"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Hace <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Falta <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index a01d2e9..8dcf6ab 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se está cargando"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada por el administrador"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Habilitado por el administrador"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Inhabilitado por el administrador"</string>
<string name="home" msgid="8263346537524314127">"Inicio"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Hace <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Tiempo restante: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index 27546ce..62dd7ab 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei lae"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Täis"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Juhib administraator"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Administraator on lubanud"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Administraator on keelanud"</string>
<string name="home" msgid="8263346537524314127">"Avaekraan"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> tagasi"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> on jäänud"</string>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index dd0e1cb..cb6f0b4 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ez da kargatzen ari"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Beteta"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Administratzaileak kontrolatzen du"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Administratzaileak gaitu du"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Administratzaileak desgaitu du"</string>
<string name="home" msgid="8263346537524314127">"Hasierako pantaila"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Duela <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> guztiz kargatu arte"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 0a2d111..e7cbd1e 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -274,11 +274,11 @@
<string name="inactive_app_active_summary" msgid="4174921824958516106">"فعال. برای تغییر حالت ضربه بزنید."</string>
<string name="runningservices_settings_title" msgid="8097287939865165213">"سرویسهای در حال اجرا"</string>
<string name="runningservices_settings_summary" msgid="854608995821032748">"مشاهده و کنترل سرویسهای در حال اجرای فعلی"</string>
- <string name="enable_webview_multiprocess" msgid="3405948012467585908">"فعال کردن وبنما چند پردازشی"</string>
+ <string name="enable_webview_multiprocess" msgid="3405948012467585908">"فعال کردن وبنمای چندپردازشی"</string>
<string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"اجرای تولیدکننده تصویر وبنما در یک پردازش مجزا."</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"اجرای وبنما"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"تنظیم اجرای وبنما"</string>
- <string name="select_webview_provider_toast_text" msgid="8168796505966635684">"اجرای وبنما انتخابی نامعتبر است چون فهرست گزینههای اجرای انتخابی قدیمی شده است. این فهرست اکنون باید بهروزرسانی شود."</string>
+ <string name="select_webview_provider_toast_text" msgid="8168796505966635684">"اجرای وبنمای انتخابی نامعتبر است چون فهرست گزینههای اجرای انتخابی قدیمی شده است. این فهرست اکنون باید بهروزرسانی شود."</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"تبدیل به رمزگذاری برحسب فایل"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"تبدیل…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"از قبل به رمزگذاری بر حسب فایل تبدیل شده است"</string>
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"شارژ نمیشود"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"پر"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"توسط سرپرست سیستم کنترل میشود"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"سرپرست آن را فعال کرده است"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"سرپرست آن را غیرفعال کرده است"</string>
<string name="home" msgid="8263346537524314127">"صفحه اصلی"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> قبل"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> باقی مانده است"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index bb27bcf..bb3ad97 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei laturissa"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Täynnä"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Järjestelmänvalvoja hallinnoi tätä asetusta."</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Järjestelmänvalvojan käyttöön ottama"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Järjestelmänvalvojan käytöstä poistama"</string>
<string name="home" msgid="8263346537524314127">"Aloitusnäyttö"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> sitten"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> jäljellä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 7ca3f89..1f12f18 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"N\'est pas en charge"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Pleine"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Contrôlé par l\'administrateur"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Activé par l\'administrateur"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Désactivé par l\'administrateur"</string>
<string name="home" msgid="8263346537524314127">"Accueil"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Il y a <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Durée restante :<xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index adfc7a3..3dc2a6a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Débranchée"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"pleine"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Contrôlé par l\'administrateur"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Activé par l\'administrateur"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Désactivé par l\'administrateur"</string>
<string name="home" msgid="8263346537524314127">"Accueil"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Il y a <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Il reste <xliff:g id="ID_1">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index 867a29d..3715919 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non está cargando"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Opción controlada polo administrador"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Activado polo administrador"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Desactivado polo administrador"</string>
<string name="home" msgid="8263346537524314127">"Inicio"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Hai <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Tempo restante: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index 54e7288..c77aeb6 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ચાર્જ થઈ રહ્યું નથી"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"પૂર્ણ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"વ્યવસ્થાપક દ્વારા નિયંત્રિત"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"વ્યવસ્થાપક દ્વારા સક્ષમ કરેલ"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
<string name="home" msgid="8263346537524314127">"હોમ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> પહેલાં"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> બાકી"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index a7e838e..e8a47cb 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज नहीं हो रही है"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"पूरी"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"व्यवस्थापक द्वारा नियंत्रित"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"व्यवस्थापक द्वारा सक्षम किया गया"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"व्यवस्थापक द्वारा अक्षम किया गया"</string>
<string name="home" msgid="8263346537524314127">"होम"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पहले"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> शेष"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f315fe9..320ebc1 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolira administrator"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Omogućio administrator"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Onemogućio administrator"</string>
<string name="home" msgid="8263346537524314127">"Početni zaslon"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Prije <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Još <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 9b62eb1..4ea4528 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nem töltődik"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Feltöltve"</string>
<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="8263346537524314127">"Főoldal"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Ennyi ideje: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> van hátra"</string>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index 36a3813..046ea48 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Չի լիցքավորվում"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Լիցքավորված"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Վերահսկվում է ադմինիստրատորի կողմից"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Միացված է ադմինիստրատորի կողմից"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Կասեցված է ադմինիստրատորի կողմից"</string>
<string name="home" msgid="8263346537524314127">"Գլխավոր էջ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> առաջ"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Մնացել է <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 62372ee..02c747a 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengisi daya"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Dikontrol oleh admin"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Diaktifkan oleh administrator"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Dinonaktifkan oleh administrator"</string>
<string name="home" msgid="8263346537524314127">"Layar Utama"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> lalu"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Tersisa <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index cd8734d..17a2f080 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ekki í hleðslu"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fullhlaðin"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Stjórnað af kerfisstjóra"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Virkjað af stjórnanda"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Stjórnandi gerði óvirkt"</string>
<string name="home" msgid="8263346537524314127">"Heim"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Fyrir <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> eftir"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 44bffcb..9c75939 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non in carica"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Carica"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Gestita dall\'amministratore"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Attivata dall\'amministratore"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Disattivata dall\'amministratore"</string>
<string name="home" msgid="8263346537524314127">"Home page"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> fa"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> rimanenti"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index de7f7e3..429ce61 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"לא טוען"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"מלא"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"נמצא בשליטת מנהל מערכת"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"הופעל על ידי מנהל המערכת"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"הושבת על ידי מנהל המערכת"</string>
<string name="home" msgid="8263346537524314127">"דף הבית"</string>
<string name="charge_length_format" msgid="8978516217024434156">"לפני <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"נשארו <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index f341d71..6c42110 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -313,6 +313,8 @@
<!-- no translation found for battery_info_status_full (2824614753861462808) -->
<skip />
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"管理者により管理されています"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"管理者によって有効にされています"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"管理者によって無効にされています"</string>
<string name="home" msgid="8263346537524314127">"ホーム"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"あと <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index 08e6327..6dba53b 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"არ იტენება"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ბატარეა დატენილია"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"იმართება ადმინისტრატორის მიერ"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"ჩართულია ადმინისტრატორის მიერ"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"გათიშულია ადმინისტრატორის მიერ"</string>
<string name="home" msgid="8263346537524314127">"მთავარი"</string>
<string name="charge_length_format" msgid="8978516217024434156">"გავიდა <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"დარჩენილია <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index 921812a..36843fd 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Зарядталып тұрған жоқ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Толық"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Әкімші басқарады"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Әкімші қосқан"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Әкімші өшірген"</string>
<string name="home" msgid="8263346537524314127">"Негізгі бет"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> бұрын"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> қалды"</string>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index f64158c..9985a1e 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"មិនបញ្ចូលថ្ម"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ពេញ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"គ្រប់គ្រងដោយអ្នកគ្រប់គ្រង"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"បានបើកដំណើរការដោយអ្នកគ្រប់គ្រង"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រង"</string>
<string name="home" msgid="8263346537524314127">"ដើម"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> មុន"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"នៅសល់ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 7e68655..8ca9807 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ಭರ್ತಿ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ನಿರ್ವಾಹಕರ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗಿದೆ"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"ನಿರ್ವಾಹಕರಿಂದ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"ನಿರ್ವಾಹಕರಿಂದ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="home" msgid="8263346537524314127">"ಮುಖಪುಟ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ಹಿಂದೆ"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ಉಳಿದಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index a7434d0..31e0378 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"충전 안함"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"충전 완료"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"관리자가 제어"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"관리자가 사용 설정함"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"관리자가 사용 중지함"</string>
<string name="home" msgid="8263346537524314127">"홈"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> 전"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> 남음"</string>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index 178d47e..fd2d905 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Кубатталган жок"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Толук"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Администратор тарабынан көзөмөлдөнөт"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Администратор иштетип койгон"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Администратор өчүрүп койгон"</string>
<string name="home" msgid="8263346537524314127">"Башкы бет"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> мурун"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> калды"</string>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index e9017d9..605f476 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ບໍ່ໄດ້ສາກໄຟ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ເຕັມ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ຄວບຄຸມໂດຍຜູ້ເບິ່ງແຍງ"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"ຖືກເປີດໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບ"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"ຖືກປິດໄວ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບ"</string>
<string name="home" msgid="8263346537524314127">"ໜ້າຫຼັກ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ກ່ອນນີ້"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"ຍັງເຫຼືອ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 5c3f5be..253eeb3 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nekraunama"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Visiškai įkrautas"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Valdo administratorius"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Įgalino administratorius"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Išjungė administratorius"</string>
<string name="home" msgid="8263346537524314127">"Pagrindinis ekranas"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Prieš <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Liko <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 27eeaf5..3323cd9 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenotiek uzlāde"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Pilns"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolē administrators"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Iespējojis administrators"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Atspējojis administrators"</string>
<string name="home" msgid="8263346537524314127">"Sākums"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Pirms šāda laika: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Atlikušais laiks: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index ec09f21..41c8600 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се полни"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Целосна"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролирано од администраторот"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Овозможено од администраторот"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Оневозможено од администраторот"</string>
<string name="home" msgid="8263346537524314127">"Почетна страница"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Пред <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Преостанаа <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index 24ad333..80cf43e 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"നിറഞ്ഞു"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"അഡ്മിൻ നിയന്ത്രിക്കുന്നത്"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"അഡ്മിനിസ്ട്രേറ്റർ പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"അഡ്മിനിസ്ട്രേറ്റർ പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="home" msgid="8263346537524314127">"ഹോം"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> മുമ്പ്"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index 5466ff1..5e7ea15 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Цэнэглэхгүй байна"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Дүүрэн"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Админ удирдсан"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Админ идэвхтэй болгосон"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Админ идэвхгүй болгосон"</string>
<string name="home" msgid="8263346537524314127">"Нүүр"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> өмнө"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> үлдсэн"</string>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index ab55a3d..7a3d76d 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज होत नाही"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"प्रशासकाने नियंत्रित केलेले"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"प्रशासकाने सक्षम केलेले"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"प्रशासकाने अक्षम केलेले"</string>
<string name="home" msgid="8263346537524314127">"मुख्यपृष्ठ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पूर्वी"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> शिल्लक"</string>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index 01b52c1..3f29687 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengecas"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Dikawal oleh pentadbir"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Didayakan oleh pentadbir"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Dilumpuhkan oleh pentadbir"</string>
<string name="home" msgid="8263346537524314127">"Skrin Utama"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> yang lalu"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> lagi"</string>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index d3659b7..71862a3 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"အားသွင်းမနေပါ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"အပြည့်"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"စီမံခန့်ခွဲသူမှ ထိန်းချုပ်ပါသည်"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"စီမံခန့်ခွဲသူမှ ဖွင့်ထားသည်"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"စီမံခန့်ခွဲသူမှ ပိတ်ထားသည်"</string>
<string name="home" msgid="8263346537524314127">"ပင်မ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"ပြီးခဲ့သည့် <xliff:g id="ID_1">%1$s</xliff:g> က"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ကျန်ပါသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 6865d50..2ad14dc 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Lader ikke"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrollert av administratoren"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Slått på av administratoren"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Avslått av administratoren"</string>
<string name="home" msgid="8263346537524314127">"Startside"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> siden"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> gjenstår"</string>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index 7742b9d..6c8dc59 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज हुँदै छैन"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"प्रशासकद्वारा नियन्त्रित"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"प्रशासकद्वारा सक्षम गरिएको छ"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"प्रशासकद्वारा असक्षम गरिएको छ"</string>
<string name="home" msgid="8263346537524314127">"गृह"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पहिले"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> बाँकी"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index efee429..7f9df25 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wordt niet opgeladen"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Volledig"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Ingesteld door beheerder"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Ingeschakeld door beheerder"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Uitgeschakeld door beheerder"</string>
<string name="home" msgid="8263346537524314127">"Startpagina"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> geleden"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> resterend"</string>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index 15e6635..10d57f4 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"ਪੂਰੀ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਯੋਗ ਬਣਾਈ ਗਈ"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
<string name="home" msgid="8263346537524314127">"ਮੁੱਖ ਸਕ੍ਰੀਨ"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ਪਹਿਲਾਂ"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ਬਾਕੀ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 9d8d97a..3e89094 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nie podłączony"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Naładowana"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolowane przez administratora"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Włączone przez administratora"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Wyłączone przez administratora"</string>
<string name="home" msgid="8263346537524314127">"Ekran główny"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> temu"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Pozostało <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 844ae41..c772ded 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada pelo admin"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Ativada pelo administrador"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Desativada pelo administrador"</string>
<string name="home" msgid="8263346537524314127">"Início"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> atrás"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> restante(s)"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 115f951..2b3dcc7 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está a carregar"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Completo"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlado pelo administrador"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Ativado pelo administrador"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Desativado pelo administrador"</string>
<string name="home" msgid="8263346537524314127">"Página inicial"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Há <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Resta(m) <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 844ae41..c772ded 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada pelo admin"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Ativada pelo administrador"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Desativada pelo administrador"</string>
<string name="home" msgid="8263346537524314127">"Início"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> atrás"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> restante(s)"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 0faf848..0322f55 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nu încarcă"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Complet"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlată de administrator"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Activată de administrator"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Dezactivată de administrator"</string>
<string name="home" msgid="8263346537524314127">"Ecranul principal"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Acum <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Timp rămas: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 09f792e..7548ace5 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряжается"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Батарея заряжена"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролируется администратором"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Включено администратором"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Отключено администратором"</string>
<string name="home" msgid="8263346537524314127">"Главная"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> назад"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Осталось <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index 20cff85..f3d6dc3 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ආරෝපණය නොවෙමින්"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"පූර්ණ"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"පරිපාලක විසින් පාලනය කරන ලදී"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"පරිපාලක විසින් සබල කරන ලදී"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"පරිපාලක විසින් අබල කරන ලදී"</string>
<string name="home" msgid="8263346537524314127">"මුල් පිටුව"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>කට පෙර"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g>ක් ඉතිරිය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 5640b13..712c348 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíja sa"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Ovládané správcom"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Povolené správcom"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Zakázané správcom"</string>
<string name="home" msgid="8263346537524314127">"Domov"</string>
<string name="charge_length_format" msgid="8978516217024434156">"pred <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Zostáva <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index ffe794e..2f23144 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Se ne polni"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Poln"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Nadzira skrbnik"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Omogočil skrbnik"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Onemogočil skrbnik"</string>
<string name="home" msgid="8263346537524314127">"Začetni zaslon"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Pred toliko časa: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Še <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index c84430c..853dd2f 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nuk po ngarkohet"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"E mbushur"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolluar nga administratori"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Aktivizuar nga administratori"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Çaktivizuar nga administratori"</string>
<string name="home" msgid="8263346537524314127">"Kreu"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> më parë"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> të mbetura"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index af3daba..094a405 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не пуни се"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Пуно"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролише администратор"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Омогућио је администратор"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Онемогућио је администратор"</string>
<string name="home" msgid="8263346537524314127">"Почетни"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Пре <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Још <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 1cd9b6f..e12d394 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laddar inte"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Strys av administratören"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Har aktiverats av administratören"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Har inaktiverats av administratören"</string>
<string name="home" msgid="8263346537524314127">"Startsida"</string>
<string name="charge_length_format" msgid="8978516217024434156">"för <xliff:g id="ID_1">%1$s</xliff:g> sedan"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> kvar"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 24d626c..d5a9d1b 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Haichaji"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Imejaa"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Imedhibitiwa na msimamizi"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Msimamizi amewasha mapendeleo ya mipangilio"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Msimamizi amezima mapendeleo ya mipangilio"</string>
<string name="home" msgid="8263346537524314127">"Mwanzo"</string>
<string name="charge_length_format" msgid="8978516217024434156">"Zimepita <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Zimesalia <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index fac31df..5c7732f 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"சார்ஜ் ஏறவில்லை"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"முழுமை"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"நிர்வாகி கட்டுப்படுத்துகிறார்"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"நிர்வாகி இயக்கியுள்ளார்"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"நிர்வாகி முடக்கியுள்ளார்"</string>
<string name="home" msgid="8263346537524314127">"முகப்பு"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> முன்"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> உள்ளது"</string>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index 4b4aabf..a74fc82 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ఛార్జ్ కావడం లేదు"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"నిండింది"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"నిర్వాహకుని ద్వారా నియంత్రించబడింది"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"నిర్వాహకులు ప్రారంభించారు"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"నిర్వాహకులు నిలిపివేసారు"</string>
<string name="home" msgid="8263346537524314127">"హోమ్"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> క్రితం"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> మిగిలి ఉంది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 6906671..53caea5 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"ไม่ได้ชาร์จ"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"เต็ม"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ผู้ดูแลระบบเป็นผู้ควบคุม"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"เปิดใช้โดยผู้ดูแลระบบ"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"ปิดใช้โดยผู้ดูแลระบบ"</string>
<string name="home" msgid="8263346537524314127">"หน้าแรก"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>ที่ผ่านมา"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"เหลือ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index a472075..0c1f167 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hindi nagkakarga"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Pinapamahalaan ng admin"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Na-enable ng administrator"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Na-disable ng administrator"</string>
<string name="home" msgid="8263346537524314127">"Home"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> na ang nakalipas"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> na lang"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 85b6080..230da22 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Şarj etmiyor"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Dolu"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Yönetici tarafından denetleniyor"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Yönetici tarafından etkinleştirildi"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Yönetici tarafından devre dışı bırakıldı"</string>
<string name="home" msgid="8263346537524314127">"Ana Ekran"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> önce"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> kaldı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 32cd586..01cedc6 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряджається"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Акумулятор заряджено"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Керується адміністратором"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Увімкнено адміністратором"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Вимкнено адміністратором"</string>
<string name="home" msgid="8263346537524314127">"Головний екран"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> тому"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Залишилося <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index 0eaead8..878ff0c 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"چارج نہیں ہو رہا ہے"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"مکمل"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"کنٹرول کردہ بذریعہ منتظم"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"منتظم نے فعال کر دیا"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"منتظم نے غیر فعال کر دیا"</string>
<string name="home" msgid="8263346537524314127">"ہوم"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> قبل"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> باقی ہیں"</string>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index 30a8576..d81a041 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Quvvatlanmayapti"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"To‘la"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Administrator tomonidan boshqariladi"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Administrator tomonidan yoqilgan"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Administrator tomonidan o‘chirilgan"</string>
<string name="home" msgid="8263346537524314127">"Bosh ekran"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> oldin"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> qoldi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 38ddcc0..2b4bddd 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hiện không sạc"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Đầy"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Do quản trị viên kiểm soát"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Được bật bởi quản trị viên"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Bị tắt bởi quản trị viên"</string>
<string name="home" msgid="8263346537524314127">"Màn hình chính"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> trước"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"Còn <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 4001d2c..48f03b7 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"未在充电"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"电量充足"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"由管理员控制"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"已被管理员启用"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"已被管理员禁用"</string>
<string name="home" msgid="8263346537524314127">"主屏幕"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"还剩 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 02be160..38656e5 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"未開始充電"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"電量已滿"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"已由管理員停用"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"已由管理員啟用"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"已由管理員停用"</string>
<string name="home" msgid="8263346537524314127">"主畫面"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"尚餘 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 7407e28..af83365 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"非充電中"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"電力充足"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"已由管理員停用"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"已由管理員啟用"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"已由管理員停用"</string>
<string name="home" msgid="8263346537524314127">"主畫面"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"還剩 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e563bff..16841d3 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -311,6 +311,8 @@
<string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ayishaji"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Kugcwele"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kulawulwa umqondisi"</string>
+ <string name="enabled_by_admin" msgid="2386503803463071894">"Kunikwe amandla umqondisi"</string>
+ <string name="disabled_by_admin" msgid="3669999613095206948">"Ikhutshazwe umlawuli"</string>
<string name="home" msgid="8263346537524314127">"Ekhaya"</string>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> edlule"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> osele"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ae2c6e7..e4c2cbc 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -625,7 +625,7 @@
<!-- UI debug setting: force allow apps on external storage [CHAR LIMIT=50] -->
<string name="force_allow_on_external">Force allow apps on external</string>
<!-- UI debug setting: force allow on external summary [CHAR LIMIT=150] -->
- <string name="force_allow_on_external_summary">Makes any app elligible to be written to external storage, regardless of manifest values</string>
+ <string name="force_allow_on_external_summary">Makes any app eligible to be written to external storage, regardless of manifest values</string>
<!-- UI debug setting: force all activites to be resizable for multiwindow [CHAR LIMIT=50] -->
<string name="force_resizable_activities">Force activities to be resizable</string>
@@ -677,7 +677,7 @@
<!-- Services settings screen, setting option summary for the user to go to the screen to view running services -->
<string name="runningservices_settings_summary">View and control currently running services</string>
- <!-- Developer settings: enable WebView multiprocess name [CHAR LIMIT=30] -->
+ <!-- Developer settings: enable WebView multiprocess name [CHAR LIMIT=50] -->
<string name="enable_webview_multiprocess">Enable multiprocess WebView</string>
<!-- Developer settings: enable WebView multiprocess summary [CHAR LIMIT=60] -->
<string name="enable_webview_multiprocess_desc">Run WebView renderers in an isolated process.</string>
@@ -775,7 +775,7 @@
<!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
<string name="enabled_by_admin">Enabled by administrator</string>
- <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
+ <!-- Summary for switch preference to denote it is switched off [CHAR LIMIT=50] -->
<string name="disabled_by_admin">Disabled by administrator</string>
<!-- Option in navigation drawer that leads to Settings main screen [CHAR LIMIT=30] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
deleted file mode 100644
index 4c0450e..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
+++ /dev/null
@@ -1,153 +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.settingslib;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.support.v7.preference.DropDownPreference;
-import android.support.v7.preference.PreferenceViewHolder;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
-public class RestrictedDropDownPreference extends DropDownPreference {
- private Spinner mSpinner;
- private final Drawable mRestrictedPadlock;
- private final int mRestrictedPadlockPadding;
- private List<RestrictedItem> mRestrictedItems = new ArrayList<>();
-
- public RestrictedDropDownPreference(Context context) {
- this(context, null);
- }
-
- public RestrictedDropDownPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(context);
- mRestrictedPadlockPadding = context.getResources().getDimensionPixelSize(
- R.dimen.restricted_icon_padding);
- }
-
- private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
- if (position >= 0) {
- String value = getEntryValues()[position].toString();
- RestrictedItem item = getRestrictedItemForEntryValue(value);
- if (item != null) {
- RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
- item.enforcedAdmin);
- mSpinner.setSelection(findIndexOfValue(getValue()));
- } else if (!value.equals(getValue()) && callChangeListener(value)) {
- setValue(value);
- }
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // noop
- }
- };
-
- @Override
- protected ArrayAdapter createAdapter() {
- return new RestrictedArrayItemAdapter(getContext());
- }
-
- @Override
- public void setValue(String value) {
- if (getRestrictedItemForEntryValue(value) != null) {
- return;
- }
- super.setValue(value);
- }
-
- @Override
- public void onBindViewHolder(PreferenceViewHolder view) {
- super.onBindViewHolder(view);
- mSpinner = (Spinner) view.itemView.findViewById(R.id.spinner);
- mSpinner.setOnItemSelectedListener(mItemSelectedListener);
- }
-
- private class RestrictedArrayItemAdapter extends ArrayAdapter<String> {
- public RestrictedArrayItemAdapter(Context context) {
- super(context, R.layout.spinner_dropdown_restricted_item);
- }
-
- @Override
- public View getDropDownView(int position, View convertView, ViewGroup parent) {
- TextView view = (TextView) super.getView(position, convertView, parent);
- CharSequence entry = getItem(position);
- boolean isEntryRestricted = isRestrictedForEntry(entry);
- RestrictedLockUtils.setTextViewPadlock(getContext(), view, isEntryRestricted);
- view.setEnabled(!isEntryRestricted);
- return view;
- }
- }
-
- private boolean isRestrictedForEntry(CharSequence entry) {
- if (entry == null) {
- return false;
- }
- for (RestrictedItem item : mRestrictedItems) {
- if (entry.equals(item.entry)) {
- return true;
- }
- }
- return false;
- }
-
- private RestrictedItem getRestrictedItemForEntryValue(CharSequence entryValue) {
- if (entryValue == null) {
- return null;
- }
- for (RestrictedItem item : mRestrictedItems) {
- if (entryValue.equals(item.entryValue)) {
- return item;
- }
- }
- return null;
- }
-
- public void addRestrictedItem(RestrictedItem item) {
- mRestrictedItems.add(item);
- }
-
- public static class RestrictedItem {
- public CharSequence entry;
- public CharSequence entryValue;
- public EnforcedAdmin enforcedAdmin;
-
- public RestrictedItem(CharSequence entry, CharSequence entryValue,
- EnforcedAdmin enforcedAdmin) {
- this.entry = entry;
- this.entryValue = entryValue;
- this.enforcedAdmin = enforcedAdmin;
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index d0c249f..1f1a9b8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -651,8 +651,11 @@
final int disabledColor = context.getColor(R.color.disabled_text_color);
sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- final ImageSpan image = new RestrictedLockImageSpan(context);
- sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ textView.setCompoundDrawables(null, null, getRestrictedPadlock(context), null);
+ textView.setCompoundDrawablePadding(context.getResources().getDimensionPixelSize(
+ R.dimen.restricted_icon_padding));
+ } else {
+ textView.setCompoundDrawables(null, null, null, null);
}
textView.setText(sb);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 0c0af24..d0aba22 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -22,9 +22,6 @@
import android.os.UserHandle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
-import android.text.Spanned;
-import android.text.SpannableStringBuilder;
-import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
@@ -39,8 +36,6 @@
public class RestrictedPreferenceHelper {
private final Context mContext;
private final Preference mPreference;
- private final Drawable mRestrictedPadlock;
- private final int mRestrictedPadlockPadding;
private boolean mDisabledByAdmin;
private EnforcedAdmin mEnforcedAdmin;
@@ -52,10 +47,6 @@
mContext = context;
mPreference = preference;
- mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext);
- mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.restricted_icon_padding);
-
if (attrs != null) {
final TypedArray attributes = context.obtainStyledAttributes(attrs,
R.styleable.RestrictedPreference);
@@ -154,13 +145,14 @@
*/
public boolean setDisabledByAdmin(EnforcedAdmin admin) {
final boolean disabled = (admin != null ? true : false);
- mEnforcedAdmin = (disabled ? admin : null);
+ mEnforcedAdmin = admin;
+ boolean changed = false;
if (mDisabledByAdmin != disabled) {
mDisabledByAdmin = disabled;
- mPreference.setEnabled(!disabled);
- return true;
+ changed = true;
}
- return false;
+ mPreference.setEnabled(!disabled);
+ return changed;
}
public boolean isDisabledByAdmin() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 99bd4ad..fcff305 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -22,6 +22,7 @@
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -40,12 +41,21 @@
new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
/**
- * @return the set of enabled accessibility services. If there are not services
- * it returned the unmodifiable {@link Collections#emptySet()}.
+ * @return the set of enabled accessibility services. If there are no services,
+ * it returns the unmodifiable {@link Collections#emptySet()}.
*/
public static Set<ComponentName> getEnabledServicesFromSettings(Context context) {
- final String enabledServicesSetting = Settings.Secure.getString(
- context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ return getEnabledServicesFromSettings(context, UserHandle.myUserId());
+ }
+
+ /**
+ * @return the set of enabled accessibility services for {@param userId}. If there are no
+ * services, it returns the unmodifiable {@link Collections#emptySet()}.
+ */
+ public static Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) {
+ final String enabledServicesSetting = Settings.Secure.getStringForUser(
+ context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userId);
if (enabledServicesSetting == null) {
return Collections.emptySet();
}
@@ -77,11 +87,22 @@
return langContext.getText(resId);
}
+ /**
+ * Changes an accessibility component's state.
+ */
public static void setAccessibilityServiceState(Context context, ComponentName toggledService,
boolean enabled) {
+ setAccessibilityServiceState(context, toggledService, enabled, UserHandle.myUserId());
+ }
+
+ /**
+ * Changes an accessibility component's state for {@param userId}.
+ */
+ public static void setAccessibilityServiceState(Context context, ComponentName toggledService,
+ boolean enabled, int userId) {
// Parse the enabled services.
Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(
- context);
+ context, userId);
if (enabledServices.isEmpty()) {
enabledServices = new ArraySet<>(1);
@@ -121,13 +142,9 @@
if (enabledServicesBuilderLength > 0) {
enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
}
- Settings.Secure.putString(context.getContentResolver(),
+ Settings.Secure.putStringForUser(context.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- enabledServicesBuilder.toString());
-
- // Update accessibility enabled.
- Settings.Secure.putInt(context.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0);
+ enabledServicesBuilder.toString(), userId);
}
private static Set<ComponentName> getInstalledServices(Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 2dfdfda..418b138 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -41,7 +41,7 @@
public class TileUtils {
private static final boolean DEBUG = false;
- private static final boolean DEBUG_TIMING = true;
+ private static final boolean DEBUG_TIMING = false;
private static final String LOG_TAG = "TileUtils";
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/UsageGraph.java b/packages/SettingsLib/src/com/android/settingslib/graph/UsageGraph.java
index 530ec16..1fff0fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/UsageGraph.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/UsageGraph.java
@@ -43,6 +43,7 @@
private final Paint mDottedPaint;
private final Drawable mDivider;
+ private final Drawable mTintedDivider;
private final int mDividerSize;
private final Path mPath = new Path();
@@ -51,6 +52,7 @@
private final SparseIntArray mPaths = new SparseIntArray();
// Paths in local coordinates for drawing.
private final SparseIntArray mLocalPaths = new SparseIntArray();
+ private final int mCornerRadius;
private int mAccentColor;
private boolean mShowProjection;
@@ -59,6 +61,10 @@
private float mMaxX = 100;
private float mMaxY = 100;
+ private float mMiddleDividerLoc = .5f;
+ private int mMiddleDividerTint = -1;
+ private int mTopDividerTint = -1;
+
public UsageGraph(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
final Resources resources = context.getResources();
@@ -68,8 +74,8 @@
mLinePaint.setStrokeCap(Cap.ROUND);
mLinePaint.setStrokeJoin(Join.ROUND);
mLinePaint.setAntiAlias(true);
- mLinePaint.setPathEffect(new CornerPathEffect(resources.getDimensionPixelSize(
- R.dimen.usage_graph_line_corner_radius)));
+ mCornerRadius = resources.getDimensionPixelSize(R.dimen.usage_graph_line_corner_radius);
+ mLinePaint.setPathEffect(new CornerPathEffect(mCornerRadius));
mLinePaint.setStrokeWidth(resources.getDimensionPixelSize(R.dimen.usage_graph_line_width));
mFillPaint = new Paint(mLinePaint);
@@ -86,6 +92,7 @@
TypedValue v = new TypedValue();
context.getTheme().resolveAttribute(com.android.internal.R.attr.listDivider, v, true);
mDivider = context.getDrawable(v.resourceId);
+ mTintedDivider = context.getDrawable(v.resourceId);
mDividerSize = resources.getDimensionPixelSize(R.dimen.usage_graph_divider_size);
}
@@ -98,6 +105,15 @@
mMaxY = maxY;
}
+ void setDividerLoc(int height) {
+ mMiddleDividerLoc = 1 - height / mMaxY;
+ }
+
+ void setDividerColors(int middleColor, int topColor) {
+ mMiddleDividerTint = middleColor;
+ mTopDividerTint = topColor;
+ }
+
public void addPath(SparseIntArray points) {
for (int i = 0; i < points.size(); i++) {
mPaths.put(points.keyAt(i), points.valueAt(i));
@@ -150,7 +166,7 @@
if (mLocalPaths.size() > 0) {
int lastX = mLocalPaths.keyAt(mLocalPaths.size() - 1);
int lastY = mLocalPaths.valueAt(mLocalPaths.size() - 1);
- if (lastY != PATH_DELIM && (lastX == lx || lastY == ly)) {
+ if (lastY != PATH_DELIM && !hasDiff(lastX, lx) && !hasDiff(lastY, ly)) {
pendingYLoc = ly;
continue;
}
@@ -160,6 +176,10 @@
}
}
+ private boolean hasDiff(int x1, int x2) {
+ return Math.abs(x2 - x1) >= mCornerRadius;
+ }
+
private int getX(float x) {
return (int) (x / mMaxX * getWidth());
}
@@ -180,9 +200,12 @@
@Override
protected void onDraw(Canvas canvas) {
// Draw lines across the top, middle, and bottom.
- drawDivider(0, canvas);
- drawDivider((canvas.getHeight() - mDividerSize) / 2, canvas);
- drawDivider(canvas.getHeight() - mDividerSize, canvas);
+ if (mMiddleDividerLoc != 0) {
+ drawDivider(0, canvas, mTopDividerTint);
+ }
+ drawDivider((int) ((canvas.getHeight() - mDividerSize) * mMiddleDividerLoc), canvas,
+ mMiddleDividerTint);
+ drawDivider(canvas.getHeight() - mDividerSize, canvas, -1);
if (mLocalPaths.size() == 0) {
return;
@@ -242,8 +265,13 @@
canvas.drawPath(mPath, mFillPaint);
}
- private void drawDivider(int y, Canvas canvas) {
- mDivider.setBounds(0, y, canvas.getWidth(), y + mDividerSize);
- mDivider.draw(canvas);
+ private void drawDivider(int y, Canvas canvas, int tintColor) {
+ Drawable d = mDivider;
+ if (tintColor != -1) {
+ mTintedDivider.setTint(tintColor);
+ d = mTintedDivider;
+ }
+ d.setBounds(0, y, canvas.getWidth(), y + mDividerSize);
+ d.draw(canvas);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java b/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java
index f95a97ad..ee1821d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java
+++ b/packages/SettingsLib/src/com/android/settingslib/graph/UsageView.java
@@ -20,6 +20,7 @@
import android.util.SparseIntArray;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -98,6 +99,26 @@
mUsageGraph.setAccentColor(color);
}
+ public void setDividerLoc(int dividerLoc) {
+ mUsageGraph.setDividerLoc(dividerLoc);
+ }
+
+ public void setDividerColors(int middleColor, int topColor) {
+ mUsageGraph.setDividerColors(middleColor, topColor);
+ }
+
+ public void setSideLabelWeights(float before, float after) {
+ setWeight(R.id.space1, before);
+ setWeight(R.id.space2, after);
+ }
+
+ private void setWeight(int id, float weight) {
+ View v = findViewById(id);
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) v.getLayoutParams();
+ params.weight = weight;
+ v.setLayoutParams(params);
+ }
+
public void setSideLabels(CharSequence[] labels) {
if (labels.length != mLabels.length) {
throw new IllegalArgumentException("Invalid number of labels");
diff --git a/packages/SettingsProvider/res/values-bs-rBA/defaults.xml b/packages/SettingsProvider/res/values-bs-rBA/defaults.xml
index 5650077..4a87a12 100644
--- a/packages/SettingsProvider/res/values-bs-rBA/defaults.xml
+++ b/packages/SettingsProvider/res/values-bs-rBA/defaults.xml
@@ -19,9 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for def_device_name (6309317409634339402) -->
- <skip />
- <!-- no translation found for def_device_name_simple (9037785625140748221) -->
- <skip />
+ <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+ <string name="def_device_name_simple" msgid="9037785625140748221">"%1$s"</string>
<string name="def_nfc_payment_component" msgid="5861297439873026958"></string>
</resources>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 51d8ca0..978ca94 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -155,6 +155,9 @@
<!-- Default for Settings.Secure.LONG_PRESS_TIMEOUT_MILLIS -->
<integer name="def_long_press_timeout_millis">500</integer>
+ <!-- Default for Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD -->
+ <bool name="def_show_ime_with_hard_keyboard">false</bool>
+
<!-- Default for Settings.System.POINTER_SPEED -->
<integer name="def_pointer_speed">0</integer>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index d89abf42..b79ce80 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -62,8 +62,9 @@
*/
private static final ArraySet<String> sBroadcastOnRestore;
static {
- sBroadcastOnRestore = new ArraySet<String>(3);
+ sBroadcastOnRestore = new ArraySet<String>(4);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+ sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a424d55..987b5ea 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1671,16 +1671,16 @@
private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper,
SQLiteDatabase database, int userId) {
- // Move over the global settings if owner.
- if (userId == UserHandle.USER_SYSTEM) {
- final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, userId);
- ensureSettingsStateLocked(globalKey);
- SettingsState globalSettings = mSettingsStates.get(globalKey);
- migrateLegacySettingsLocked(globalSettings, database, TABLE_GLOBAL);
- globalSettings.persistSyncLocked();
- }
+ // Move over the system settings.
+ final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);
+ ensureSettingsStateLocked(systemKey);
+ SettingsState systemSettings = mSettingsStates.get(systemKey);
+ migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);
+ systemSettings.persistSyncLocked();
// Move over the secure settings.
+ // Do this after System settings, since this is the first thing we check when deciding
+ // to skip over migration from db to xml for a secondary user.
final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);
ensureSettingsStateLocked(secureKey);
SettingsState secureSettings = mSettingsStates.get(secureKey);
@@ -1688,12 +1688,16 @@
ensureSecureSettingAndroidIdSetLocked(secureSettings);
secureSettings.persistSyncLocked();
- // Move over the system settings.
- final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);
- ensureSettingsStateLocked(systemKey);
- SettingsState systemSettings = mSettingsStates.get(systemKey);
- migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);
- systemSettings.persistSyncLocked();
+ // Move over the global settings if owner.
+ // Do this last, since this is the first thing we check when deciding
+ // to skip over migration from db to xml for owner user.
+ if (userId == UserHandle.USER_SYSTEM) {
+ final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, userId);
+ ensureSettingsStateLocked(globalKey);
+ SettingsState globalSettings = mSettingsStates.get(globalKey);
+ migrateLegacySettingsLocked(globalSettings, database, TABLE_GLOBAL);
+ globalSettings.persistSyncLocked();
+ }
// Drop the database as now all is moved and persisted.
if (DROP_DATABASE_ON_MIGRATION) {
@@ -1936,7 +1940,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 124;
+ private static final int SETTINGS_VERSION = 125;
private final int mUserId;
@@ -2116,6 +2120,22 @@
currentVersion = 124;
}
+ if (currentVersion == 124) {
+ // Version 124: allow OEMs to set a default value for whether IME should be
+ // shown when a physical keyboard is connected.
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ Setting currentSetting = secureSettings.getSettingLocked(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
+ if (currentSetting == null) {
+ secureSettings.insertSettingLocked(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+ getContext().getResources().getBoolean(
+ R.bool.def_show_ime_with_hard_keyboard) ? "1" : "0",
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 125;
+ }
+
// vXXX: Add new settings above this point.
// Return the current version.
diff --git a/packages/Shell/res/values-af/strings.xml b/packages/Shell/res/values-af/strings.xml
index f86b551..8dbe3cd 100644
--- a/packages/Shell/res/values-af/strings.xml
+++ b/packages/Shell/res/values-af/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Tuisskerm"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Foutverslag <xliff:g id="ID">#%d</xliff:g> word tans geskep"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Foutverslag <xliff:g id="ID">#%d</xliff:g> is vasgevang"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Foutverslag <xliff:g id="ID">#%d</xliff:g> vasgevang, maar skermkiekie hangende"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Foutverslae bevat data van die stelsel se verskillende loglêers af, insluitend persoonlike en private inligting. Deel foutverslae net met programme en mense wat jy vertrou."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Wys hierdie boodskap volgende keer"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Foutverslae"</string>
diff --git a/packages/Shell/res/values-am/strings.xml b/packages/Shell/res/values-am/strings.xml
index 55c5390..b0d3de0 100644
--- a/packages/Shell/res/values-am/strings.xml
+++ b/packages/Shell/res/values-am/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"ቀፎ"</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_pending_screenshot_title" msgid="5460883450679439591">"የሳንካ ሪፖርት <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"የሳንካ ሪፖርቶች የግል መረጃን ጨምሮ ከበርካታ የስርዓቱ ምዝግብ ማስታወሻዎች የመጣ ውሂብን ይዟል። የሳንካ ሪፖርቶች ለሚያምኗቸው መተግበሪያዎችን እና ሰዎችን ብቻ ያጋሩ።"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ይህን መልዕክት በሚቀጥለው ጊዜ አሳይ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"የሳንካ ሪፖርቶች"</string>
diff --git a/packages/Shell/res/values-ar/strings.xml b/packages/Shell/res/values-ar/strings.xml
index f0af8ce..818c368 100644
--- a/packages/Shell/res/values-ar/strings.xml
+++ b/packages/Shell/res/values-ar/strings.xml
@@ -19,10 +19,13 @@
<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_pending_screenshot_title" msgid="5460883450679439591">"تم التقاط تقرير الأخطاء <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"تحتوي تقارير الأخطاء على بيانات من ملفات سجلات النظام المتنوعة، بما في ذلك معلومات شخصية وخاصة. لا تشارك تقارير الأخطاء إلا مع التطبيقات والأشخاص الموثوق بهم."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"إظهار هذه الرسالة في المرة القادمة"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"تقارير الأخطاء"</string>
diff --git a/packages/Shell/res/values-az-rAZ/strings.xml b/packages/Shell/res/values-az-rAZ/strings.xml
index 6d375fe..22791a6 100644
--- a/packages/Shell/res/values-az-rAZ/strings.xml
+++ b/packages/Shell/res/values-az-rAZ/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Baq hesabatı <xliff:g id="ID">#%d</xliff:g> yaradıldı"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Baq hesabatı <xliff:g id="ID">#%d</xliff:g> alındı"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"<xliff:g id="ID">#%d</xliff:g> baq hesabatı çəkildi, amma skrinşot hələ gözlənilir"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Baq raportları sistemin müxtəlif jurnal fayllarından data içərir ki, buna şəxsi və konfidensial məlumatlar da aiddir. Yalnız inandığınız adamlarla baq raportlarını paylaşın."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bu mesajı növbəti dəfə göstər"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Baq hesabatları"</string>
diff --git a/packages/Shell/res/values-b+sr+Latn/strings.xml b/packages/Shell/res/values-b+sr+Latn/strings.xml
index ad84941..bc5ce26 100644
--- a/packages/Shell/res/values-b+sr+Latn/strings.xml
+++ b/packages/Shell/res/values-b+sr+Latn/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Izveštaj o grešci <xliff:g id="ID">#%d</xliff:g> se generiše"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"Izveštaj o grešci <xliff:g id="ID">#%d</xliff:g> snimljen; snimak ekrana se čeka"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Izveštaji o greškama sadrže podatke iz različitih sistemskih datoteka evidencije, uključujući lične i privatne podatke. Delite izveštaje o greškama samo sa aplikacijama i ljudima u koje imate poverenja."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Prikaži ovu poruku sledeći put"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Izveštaji o greškama"</string>
diff --git a/packages/Shell/res/values-bg/strings.xml b/packages/Shell/res/values-bg/strings.xml
index e543839..0f8676f 100644
--- a/packages/Shell/res/values-bg/strings.xml
+++ b/packages/Shell/res/values-bg/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Команден ред"</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_pending_screenshot_title" msgid="5460883450679439591">"Сигналът (<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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Отчетите за програмни грешки съдържат данни от различни регистрационни файлове на системата, включително лична и поверителна информация. Споделяйте ги само с приложения и хора, на които имате доверие."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Това съобщение да се показва следващия път"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Отчети за прогр. грешки"</string>
diff --git a/packages/Shell/res/values-bn-rBD/strings.xml b/packages/Shell/res/values-bn-rBD/strings.xml
index bba778d6..c632b10 100644
--- a/packages/Shell/res/values-bn-rBD/strings.xml
+++ b/packages/Shell/res/values-bn-rBD/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"শেল"</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_pending_screenshot_title" msgid="5460883450679439591">"ত্রুটির প্রতিবেদন <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"ত্রুটির প্রতিবেদনগুলিতে থাকা ডেটা, সিস্টেমের বিভিন্ন লগ ফাইলগুলি থেকে আসে, যাতে ব্যক্তিগত এবং গোপনীয় তথ্য অন্তর্ভুক্ত থাকে৷ আপনি বিশ্বাস করেন শুধুমাত্র এমন অ্যাপ্লিকেশান এবং ব্যক্তিদের সাথে ত্রুটির প্রতিবেদনগুলি ভাগ করুন৷"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"এই বার্তাটি পরের বার দেখান"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ত্রুটির প্রতিবেদনগুলি"</string>
diff --git a/packages/Shell/res/values-bs-rBA/strings.xml b/packages/Shell/res/values-bs-rBA/strings.xml
index 54be955..f4d71e9 100644
--- a/packages/Shell/res/values-bs-rBA/strings.xml
+++ b/packages/Shell/res/values-bs-rBA/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Ljuska"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Izvještaj o grešci <xliff:g id="ID">#%d</xliff:g> se generira"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"Izvještaj o greškama <xliff:g id="ID">#%d</xliff:g> snimljen, čeka se snim. ekr."</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Izvještaji o greškama sadrže podatke iz raznih zapisnika sistema, uključujući lične i privatne informacije. Podijelite izvještaje o greškama samo sa aplikacijama i osobama kojima vjerujete."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaži ovu poruku sljedeći put"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Izvještaji o greškama"</string>
diff --git a/packages/Shell/res/values-ca/strings.xml b/packages/Shell/res/values-ca/strings.xml
index a92177e..3efb53b 100644
--- a/packages/Shell/res/values-ca/strings.xml
+++ b/packages/Shell/res/values-ca/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Protecció"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"S\'està generant l\'informe d\'errors <xliff:g id="ID">#%d</xliff:g>"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"L\'informe d\'errors <xliff:g id="ID">#%d</xliff:g> s\'ha capturat (captura pendent)"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Els informes d\'error contenen dades dels diferents fitxers de registre del sistema, inclosa informació privada i personal. Comparteix els informes d\'error només amb les aplicacions i amb les persones en qui confies."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra aquest missatge la propera vegada"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Informes d\'error"</string>
diff --git a/packages/Shell/res/values-cs/strings.xml b/packages/Shell/res/values-cs/strings.xml
index 6f7549a..afb6f9e 100644
--- a/packages/Shell/res/values-cs/strings.xml
+++ b/packages/Shell/res/values-cs/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Zpráva o chybě <xliff:g id="ID">#%d</xliff:g> se vytváří"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"Zpráva o chybě <xliff:g id="ID">#%d</xliff:g> byla vytvořena, čeká se na snímek"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Chybová hlášení obsahují data z různých souborů protokolů systému včetně osobních a soukromých informací. Chybová hlášení sdílejte pouze s aplikacemi a uživateli, kterým důvěřujete."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobrazit tuto zprávu příště"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Zprávy o chybách"</string>
diff --git a/packages/Shell/res/values-da/strings.xml b/packages/Shell/res/values-da/strings.xml
index 72a9a99..0f7b784 100644
--- a/packages/Shell/res/values-da/strings.xml
+++ b/packages/Shell/res/values-da/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Fejlrapporten <xliff:g id="ID">#%d</xliff:g> genereres"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Fejrapporten <xliff:g id="ID">#%d</xliff:g> blev gemt"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Fejlrapport <xliff:g id="ID">#%d</xliff:g> blev gemt, men skærmbilledet afventer"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Fejlrapporter indeholder data fra systemets forskellige logfiler, f.eks. personlige og private oplysninger. Del kun fejlrapporter med apps og personer, du har tillid til."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne underretning næste gang"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Fejlrapporter"</string>
diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml
index f848c9d..8f87fed 100644
--- a/packages/Shell/res/values-de/strings.xml
+++ b/packages/Shell/res/values-de/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Fehlerbericht <xliff:g id="ID">#%d</xliff:g> wird generiert"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Fehlerbericht <xliff:g id="ID">#%d</xliff:g> erfasst"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Fehlerbericht <xliff:g id="ID">#%d</xliff:g> erfasst, aber Screenshot ausstehend"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Fehlerberichte enthalten Daten aus verschiedenen Protokolldateien des Systems, darunter auch personenbezogene und private Daten. Teile Fehlerberichte nur mit Apps und Personen, denen du vertraust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Diese Nachricht nächstes Mal zeigen"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Fehlerberichte"</string>
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
index 0c04809..517d83a 100644
--- a/packages/Shell/res/values-el/strings.xml
+++ b/packages/Shell/res/values-el/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Κέλυφος"</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_pending_screenshot_title" msgid="5460883450679439591">"Η αναφ. σφάλματος <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Οι αναφορές σφαλμάτων περιέχουν δεδομένα από τα διάφορα αρχεία καταγραφής του συστήματος, συμπεριλαμβανομένων προσωπικών και ιδιωτικών πληροφοριών. Να μοιράζεστε αναφορές σφαλμάτων μόνο με εφαρμογές και άτομα που εμπιστεύεστε."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Εμφάνιση αυτού του μηνύματος την επόμενη φορά"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Αναφορές σφαλμάτων"</string>
diff --git a/packages/Shell/res/values-en-rAU/strings.xml b/packages/Shell/res/values-en-rAU/strings.xml
index ad637f5..2349ec5 100644
--- a/packages/Shell/res/values-en-rAU/strings.xml
+++ b/packages/Shell/res/values-en-rAU/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Bug report <xliff:g id="ID">#%d</xliff:g> is being generated"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Bug report <xliff:g id="ID">#%d</xliff:g> captured"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Bug report <xliff:g id="ID">#%d</xliff:g> captured but screenshot pending"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
diff --git a/packages/Shell/res/values-en-rGB/strings.xml b/packages/Shell/res/values-en-rGB/strings.xml
index ad637f5..2349ec5 100644
--- a/packages/Shell/res/values-en-rGB/strings.xml
+++ b/packages/Shell/res/values-en-rGB/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Bug report <xliff:g id="ID">#%d</xliff:g> is being generated"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Bug report <xliff:g id="ID">#%d</xliff:g> captured"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Bug report <xliff:g id="ID">#%d</xliff:g> captured but screenshot pending"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
diff --git a/packages/Shell/res/values-en-rIN/strings.xml b/packages/Shell/res/values-en-rIN/strings.xml
index ad637f5..2349ec5 100644
--- a/packages/Shell/res/values-en-rIN/strings.xml
+++ b/packages/Shell/res/values-en-rIN/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Bug report <xliff:g id="ID">#%d</xliff:g> is being generated"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Bug report <xliff:g id="ID">#%d</xliff:g> captured"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Bug report <xliff:g id="ID">#%d</xliff:g> captured but screenshot pending"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
diff --git a/packages/Shell/res/values-es-rUS/strings.xml b/packages/Shell/res/values-es-rUS/strings.xml
index a9a1e08..307548a 100644
--- a/packages/Shell/res/values-es-rUS/strings.xml
+++ b/packages/Shell/res/values-es-rUS/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Se está generando el informe de errores <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Se capturó el informe de errores <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Informe de errores <xliff:g id="ID">#%d</xliff:g> capturado (captura pendiente)"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida la información personal y privada. Comparte los informes de errores únicamente con aplicaciones y personas en las que confíes."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de errores"</string>
diff --git a/packages/Shell/res/values-es/strings.xml b/packages/Shell/res/values-es/strings.xml
index b82d372..68abf83 100644
--- a/packages/Shell/res/values-es/strings.xml
+++ b/packages/Shell/res/values-es/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Se está generando el informe de errores <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Informe de errores <xliff:g id="ID">#%d</xliff:g> capturado"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Informe de errores <xliff:g id="ID">#%d</xliff:g> capturado (captura pendiente)"</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="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 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 informe de errores sin captura de pantalla o espera a que se haga la captura."</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida información personal y privada. Comparte los informes de errores únicamente con aplicaciones y usuarios en los que confíes."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de error"</string>
diff --git a/packages/Shell/res/values-et-rEE/strings.xml b/packages/Shell/res/values-et-rEE/strings.xml
index cdb774a0..028e2bd 100644
--- a/packages/Shell/res/values-et-rEE/strings.xml
+++ b/packages/Shell/res/values-et-rEE/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Kest"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Luuakse veaaruannet <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Jäädvustati veaaruanne <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Jäädvustati veaaruanne <xliff:g id="ID">#%d</xliff:g>, kuid ekraanipilt on ootel"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Veaaruanded sisaldavad andmeid erinevatest süsteemi logifailidest, sh isiklikku ja privaatset teavet. Jagage veaaruandeid ainult usaldusväärsete rakenduste ja inimestega."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Kuva see sõnum järgmisel korral"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Veaaruanded"</string>
diff --git a/packages/Shell/res/values-eu-rES/strings.xml b/packages/Shell/res/values-eu-rES/strings.xml
index 42dfe27..7946f67 100644
--- a/packages/Shell/res/values-eu-rES/strings.xml
+++ b/packages/Shell/res/values-eu-rES/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell-interfazea"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Akatsen <xliff:g id="ID">#%d</xliff:g> txostena egiten ari gara"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Akatsen <xliff:g id="ID">#%d</xliff:g> txostena egin da"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Egin da <xliff:g id="ID">#%d</xliff:g> txostena. Pantaila-argazkia falta 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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Akatsen txostenek sistemaren erregistro-fitxategietako datuak dauzkate, informazio pertsonala eta pribatua barne. Akatsen txostenak partekatzen badituzu, partekatu soilik aplikazio eta pertsona fidagarriekin."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Erakutsi mezu hau hurrengoan"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Akatsen txostenak"</string>
diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml
index e825f3a..ecadf1a 100644
--- a/packages/Shell/res/values-fa/strings.xml
+++ b/packages/Shell/res/values-fa/strings.xml
@@ -19,10 +19,13 @@
<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_pending_screenshot_title" msgid="5460883450679439591">"گزارش اشکال <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"گزارشهای اشکال حاوی دادههایی از فایلهای گزارش مختلف در سیستم هستند، شامل اطلاعات شخصی و خصوصی. گزارشهای اشکال را فقط با افراد و برنامههای مورد اعتماد خود به اشتراک بگذارید."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"دفعه بعد این پیام نشان داده شود"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"گزارش اشکال"</string>
diff --git a/packages/Shell/res/values-fi/strings.xml b/packages/Shell/res/values-fi/strings.xml
index 8c67c66..b5ae006 100644
--- a/packages/Shell/res/values-fi/strings.xml
+++ b/packages/Shell/res/values-fi/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Komentotulkki"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Luodaan virheraporttia <xliff:g id="ID">#%d</xliff:g>."</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Virheraportti <xliff:g id="ID">#%d</xliff:g> tallennettu"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Virheraportti <xliff:g id="ID">#%d</xliff:g> tallennettu, kuvakaappaus odottaa."</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Virheraportit sisältävät järjestelmän lokitietoja, ja niihin voi sisältyä henkilökohtaisia ja yksityisiä tietoja. Jaa virheraportteja vain luotettaville sovelluksille ja käyttäjille."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Näytä tämä viesti seuraavalla kerralla"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Virheraportit"</string>
diff --git a/packages/Shell/res/values-fr-rCA/strings.xml b/packages/Shell/res/values-fr-rCA/strings.xml
index 3daa120..6c40edd 100644
--- a/packages/Shell/res/values-fr-rCA/strings.xml
+++ b/packages/Shell/res/values-fr-rCA/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Rapport de bogue <xliff:g id="ID">#%d</xliff:g> généré"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Rapport de bogue <xliff:g id="ID">#%d</xliff:g> enregistré"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Rap. bogue <xliff:g id="ID">#%d</xliff:g> enreg., mais attente de saisie d\'écran"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bogue contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bogue qu\'avec les applications et les personnes que vous estimez fiables."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Rapports de bogues"</string>
diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml
index c512aca..54f0b98 100644
--- a/packages/Shell/res/values-fr/strings.xml
+++ b/packages/Shell/res/values-fr/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Le rapport de bug \"<xliff:g id="ID">#%d</xliff:g>\" est en cours de création"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"Rapport bug \"<xliff:g id="ID">#%d</xliff:g>\" enregistré, mais attente capt. écran"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bug contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bug qu\'avec les applications et les personnes que vous estimez fiables."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Rapports d\'erreur"</string>
diff --git a/packages/Shell/res/values-gl-rES/strings.xml b/packages/Shell/res/values-gl-rES/strings.xml
index ea95c97..e462cee 100644
--- a/packages/Shell/res/values-gl-rES/strings.xml
+++ b/packages/Shell/res/values-gl-rES/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Estase xerando o informe de erros <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Rexistrouse o informe de erros <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Inf. erros rexistrado <xliff:g id="ID">#%d</xliff:g> e captura pantalla pendente"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Os informes de erros conteñen datos dos distintos ficheiros de rexistro do sistema, incluída información persoal e privada. Comparte os informes de erros unicamente con aplicacións e persoas de confianza."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensaxe a próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de erros"</string>
diff --git a/packages/Shell/res/values-gu-rIN/strings.xml b/packages/Shell/res/values-gu-rIN/strings.xml
index 1436c3d..f99167d 100644
--- a/packages/Shell/res/values-gu-rIN/strings.xml
+++ b/packages/Shell/res/values-gu-rIN/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"શેલ"</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_pending_screenshot_title" msgid="5460883450679439591">"બગ રિપોર્ટ <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"બગ રિપોર્ટ્સ વ્યક્તિગત અને ખાનગી માહિતી સહિત, સિસ્ટમની વિભિન્ન લૉગ ફાઇલોનો ડેટા ધરાવે છે. બગ રિપોર્ટ્સ ફક્ત તમે વિશ્વાસ કરતા હો તે એપ્લિકેશનો અને લોકો સાથે જ શેર કરો."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"આગલી વખતે આ સંદેશ બતાવો"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"બગ રિપોર્ટ્સ"</string>
diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml
index 9a735f2..aedec048 100644
--- a/packages/Shell/res/values-hi/strings.xml
+++ b/packages/Shell/res/values-hi/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"शेल"</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_pending_screenshot_title" msgid="5460883450679439591">"बग रिपोर्ट <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"बग रिपोर्ट में व्यक्तिगत और निजी जानकारी सहित, सिस्टम की विभिन्न लॉग फ़ाइलों का डेटा होता है. बग रिपोर्ट केवल विश्वसनीय ऐप्स और व्यक्तियों से ही साझा करें."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"यह संदेश अगली बार दिखाएं"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रिपोर्ट"</string>
diff --git a/packages/Shell/res/values-hr/strings.xml b/packages/Shell/res/values-hr/strings.xml
index 71d50b8..462eb2a1 100644
--- a/packages/Shell/res/values-hr/strings.xml
+++ b/packages/Shell/res/values-hr/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Ljuska"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Generira se izvješće o programskoj pogrešci <xliff:g id="ID">#%d</xliff:g>"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"Izvješće o pogrešci <xliff:g id="ID">#%d</xliff:g> snimljeno, ali se čeka snimka"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Prijave programskih pogrešaka sadržavaju podatke iz različitih datoteka zapisnika sustava, uključujući osobne i privatne informacije. Prijave programskih pogrešaka dijelite samo s aplikacijama i osobama koje smatrate pouzdanima."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Prikaži tu poruku sljedeći put"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Izvj. o prog. pogreš."</string>
diff --git a/packages/Shell/res/values-hu/strings.xml b/packages/Shell/res/values-hu/strings.xml
index cf10315..1f7f1fa 100644
--- a/packages/Shell/res/values-hu/strings.xml
+++ b/packages/Shell/res/values-hu/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Héj"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Hibajelentés (<xliff:g id="ID">#%d</xliff:g>) létrehozása folyamatban"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Hibajelentés (<xliff:g id="ID">#%d</xliff:g>) rögzítve"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Hibajelentés (<xliff:g id="ID">#%d</xliff:g>) rögzítve – képernyőkép függőben"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"A programhiba-jelentések a rendszer különféle naplófájljaiból származó adatokat tartalmaznak, köztük személyes és magánjellegű információkat is. Csak olyan alkalmazásokkal és személyekkel osszon meg programhiba-jelentéseket, amelyekben vagy akikben megbízik."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Üzenet mutatása legközelebb"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Hibajelentések"</string>
diff --git a/packages/Shell/res/values-hy-rAM/strings.xml b/packages/Shell/res/values-hy-rAM/strings.xml
index 1cf5c94..c9351c2 100644
--- a/packages/Shell/res/values-hy-rAM/strings.xml
+++ b/packages/Shell/res/values-hy-rAM/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Խեցի"</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_pending_screenshot_title" msgid="5460883450679439591">"<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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Վրիպակի զեկույցները պարունակում են տվյալներ համակարգի տարբեր մուտքի ֆայլերից, այդ թվում նաև անհատական և գաղտնի տեղեկություններ: Վրիպակի զեկույցները կիսեք միայն այն հավելվածների և մարդկանց հետ, որոնց վստահում եք:"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Այս հաղորդագրությունը ցույց տալ հաջորդ անգամ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Վրիպակների հաշվետվություններ"</string>
diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml
index 1e8fe87..4bca166 100644
--- a/packages/Shell/res/values-in/strings.xml
+++ b/packages/Shell/res/values-in/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Kerangka"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Laporan bug <xliff:g id="ID">#%d</xliff:g> sedang dibuat"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Laporan bug <xliff:g id="ID">#%d</xliff:g> dijepret"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Laporan bug <xliff:g id="ID">#%d</xliff:g> dijepret, tetapi tangkapan layar belum selesai"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Laporan bug berisi data dari berbagai file log sistem, termasuk informasi pribadi dan rahasia. Hanya bagikan laporan bug dengan aplikasi dan orang yang Anda percaya."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tampilkan pesan ini lain kali"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan bug"</string>
diff --git a/packages/Shell/res/values-is-rIS/strings.xml b/packages/Shell/res/values-is-rIS/strings.xml
index afcea59..aedf3bc3 100644
--- a/packages/Shell/res/values-is-rIS/strings.xml
+++ b/packages/Shell/res/values-is-rIS/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Skipanalína"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Verið er að búa til villutilkynningu <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Villutilkynning <xliff:g id="ID">#%d</xliff:g> búin til"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Villuskýrsla <xliff:g id="ID">#%d</xliff:g> tilbúin en skjámynd í vinnslu"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Villutilkynningar innihalda gögn úr hinum ýmsu annálsskrám kerfisins, þ. á m. persónuleg gögn og trúnaðarupplýsingar. Deildu villutilkynningum eingöngu með forritum og fólki sem þú treystir."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Sýna þessi skilaboð næst"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Villutilkynningar"</string>
diff --git a/packages/Shell/res/values-it/strings.xml b/packages/Shell/res/values-it/strings.xml
index d7ac9a5..d7d62e2 100644
--- a/packages/Shell/res/values-it/strings.xml
+++ b/packages/Shell/res/values-it/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Generazione segnalazione di bug <xliff:g id="ID">#%d</xliff:g> in corso"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Segnalazione di bug <xliff:g id="ID">#%d</xliff:g> acquisita"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Segnalazione di bug <xliff:g id="ID">#%d</xliff:g> acquisita, screenshot in attesa"</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="default" msgid="8353769438382138847">"Tocca per condividere la segnalazione di bug"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tocca per condividere la segnalazione di bug senza screenshot o attendi che lo screenshot sia completo"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tocca per condividere la segnalazione di bug senza screenshot o attendi che lo screenshot sia completo"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Le segnalazioni di bug contengono dati da vari file di log del sistema, incluse informazioni personali e private. Condividi le segnalazioni di bug solo con app e persone attendibili."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra questo messaggio la prossima volta"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Rapporti sui bug"</string>
diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml
index fd64ee1..6589c8b 100644
--- a/packages/Shell/res/values-iw/strings.xml
+++ b/packages/Shell/res/values-iw/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"מעטפת"</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_pending_screenshot_title" msgid="5460883450679439591">"הדוח על הבאג <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"דוחות על באגים כוללים נתונים מקובצי היומן השונים במערכת, כולל מידע אישי ופרטי. שתף דוחות באגים רק עם אפליקציות ואנשים שאתה סומך עליהם."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"הצג את ההודעה הזו בפעם הבאה"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"דוחות באגים"</string>
diff --git a/packages/Shell/res/values-ja/strings.xml b/packages/Shell/res/values-ja/strings.xml
index 050c5df..a6ff33e 100644
--- a/packages/Shell/res/values-ja/strings.xml
+++ b/packages/Shell/res/values-ja/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"シェル"</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_pending_screenshot_title" msgid="5460883450679439591">"バグレポート <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"バグレポートには、個人の非公開情報など、システムのさまざまなログファイルのデータが含まれます。共有する場合は信頼するアプリとユーザーのみを選択してください。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"このメッセージを次回も表示する"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"バグレポート"</string>
diff --git a/packages/Shell/res/values-ka-rGE/strings.xml b/packages/Shell/res/values-ka-rGE/strings.xml
index 8c5c6fb..119c360 100644
--- a/packages/Shell/res/values-ka-rGE/strings.xml
+++ b/packages/Shell/res/values-ka-rGE/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"გარეკანი"</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_pending_screenshot_title" msgid="5460883450679439591">"ხარვეზის ანგარიში <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"ხარვეზის ანგარიშები მოიცავს მონაცემებს სხვადასხვა სისტემური ჟურნალის ფაილებიდან, მათ შორის პირად და კონფიდენციალურ ინფორმაციას."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"შემდგომში აჩვენე ეს შეტყობინება"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"შეცდომების ანგარიშები"</string>
diff --git a/packages/Shell/res/values-kk-rKZ/strings.xml b/packages/Shell/res/values-kk-rKZ/strings.xml
index edb2dd0..9c2e4e7 100644
--- a/packages/Shell/res/values-kk-rKZ/strings.xml
+++ b/packages/Shell/res/values-kk-rKZ/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Қабыршық"</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_pending_screenshot_title" msgid="5460883450679439591">"<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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Вирус туралы баянатта жүйеде тіркелген әртүрлі файлдар туралы деректер болады, оған жеке және құпия ақпарат та кіреді. Вирус баянаттарын сенімді қолданбалар және сенімді адамдармен ғана бөлісіңіз."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Бұл хабарды келесі жолы көрсетіңіз"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Қате туралы баяндамалар"</string>
diff --git a/packages/Shell/res/values-km-rKH/strings.xml b/packages/Shell/res/values-km-rKH/strings.xml
index ba0de9f..d5fc400 100644
--- a/packages/Shell/res/values-km-rKH/strings.xml
+++ b/packages/Shell/res/values-km-rKH/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"សែល"</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_pending_screenshot_title" msgid="5460883450679439591">"<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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"របាយការណ៍កំហុសរួមមានឯកសារកំណត់ហេតុផ្សេងៗរបស់ប្រព័ន្ធ រួមមានព័ត៌មានផ្ទាល់ខ្លួន និងឯកជន។ ចែករំលែករបាយការណ៍កំហុសជាមួយកម្មវិធី និងមនុស្សដែលអ្នកទុកចិត្ត។"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"បង្ហាញសារនេះពេលក្រោយ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"រាយការណ៍ពីកំហុស"</string>
diff --git a/packages/Shell/res/values-kn-rIN/strings.xml b/packages/Shell/res/values-kn-rIN/strings.xml
index b041f35..1331f7e 100644
--- a/packages/Shell/res/values-kn-rIN/strings.xml
+++ b/packages/Shell/res/values-kn-rIN/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"ಶೆಲ್"</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_pending_screenshot_title" msgid="5460883450679439591">"ದೋಷ ವರದಿಯ <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"ವೈಯಕ್ತಿಕ ಮತ್ತು ಖಾಸಗಿ ಮಾಹಿತಿಯು ಸೇರಿದಂತೆ, ಸಿಸ್ಟಂನ ಹಲವಾರು ಲಾಗ್ ಫೈಲ್ಗಳಿಂದ ಡೇಟಾವನ್ನು ದೋಷದ ವರದಿಗಳು ಒಳಗೊಂಡಿವೆ. ನೀವು ನಂಬುವಂತಹ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಜನರೊಂದಿಗೆ ಮಾತ್ರ ದೋಷದ ವರದಿಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಿ."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ಈ ಸಂದೇಶವನ್ನು ಮುಂದಿನ ಬಾರಿ ತೋರಿಸಿ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ದೋಷ ವರದಿಗಳು"</string>
diff --git a/packages/Shell/res/values-ko/strings.xml b/packages/Shell/res/values-ko/strings.xml
index 2a86360..4c0a169 100644
--- a/packages/Shell/res/values-ko/strings.xml
+++ b/packages/Shell/res/values-ko/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"셸"</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_pending_screenshot_title" msgid="5460883450679439591">"버그 신고 <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"버그 신고서는 시스템의 다양한 로그 파일 데이터(예: 개인 및 비공개 정보)를 포함합니다. 신뢰할 수 있는 앱과 사용자에게만 버그 신고서를 공유하세요."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"다음에 이 메시지 표시"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"버그 신고"</string>
diff --git a/packages/Shell/res/values-ky-rKG/strings.xml b/packages/Shell/res/values-ky-rKG/strings.xml
index 1903109..e4039fe 100644
--- a/packages/Shell/res/values-ky-rKG/strings.xml
+++ b/packages/Shell/res/values-ky-rKG/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Командалык кабык"</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_pending_screenshot_title" msgid="5460883450679439591">"Мүчүлүштүк тууралуу билдирүү <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Ката тууралуу билдирүүлөр системанын ар кандай лог файлдарынын берилиштерин камтыйт, аларга өздүк жана купуя маалыматтар дагы кирет. Ката тууралуу билдирүүлөрдү сиз ишенген колдонмолор жана адамдар менен гана бөлүшүңүз."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Бул билдирүү кийин көрсөтүлсүн"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Мүчүлүштүктөрдү кабарлоолор"</string>
@@ -35,6 +38,6 @@
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Мүчүлүштүк тууралуу билдирүүнүн <xliff:g id="ID">#%d</xliff:g> чоо-жайы"</string>
<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="bugreport_info_description" msgid="5072835127481627722">"Мүчүлүштүк корутундусу"</string>
<string name="save" msgid="4781509040564835759">"Сактоо"</string>
</resources>
diff --git a/packages/Shell/res/values-lo-rLA/strings.xml b/packages/Shell/res/values-lo-rLA/strings.xml
index 4348250..2804528 100644
--- a/packages/Shell/res/values-lo-rLA/strings.xml
+++ b/packages/Shell/res/values-lo-rLA/strings.xml
@@ -19,10 +19,13 @@
<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_pending_screenshot_title" msgid="5460883450679439591">"ບັນທຶກລາຍງານຂໍ້ຜິດພາດ <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"ການລາຍງານຂໍ້ຜິດພາດປະກອບມີ ຂໍ້ມູນຈາກໄຟລ໌ບັນທຶກຂອງລະບົບຫຼາຍໄຟລ໌, ຮວມທັງຂໍ້ມູນສ່ວນໂຕນຳ. ທ່ານຕ້ອງແບ່ງປັນລາຍງານຂໍ້ຜິດພາດໃຫ້ແອັບຯ ແລະຄົນທີ່ທ່ານເຊື່ອຖືໄດ້ເທົ່ານັ້ນ."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ສະແດງຂໍ້ຄວາມນີ້ອີກໃນເທື່ອຕໍ່ໄປ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ລາຍງານບັນຫາ"</string>
diff --git a/packages/Shell/res/values-lt/strings.xml b/packages/Shell/res/values-lt/strings.xml
index a714efc..a8468cf 100644
--- a/packages/Shell/res/values-lt/strings.xml
+++ b/packages/Shell/res/values-lt/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Apvalkalas"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Pranešimas apie riktą (<xliff:g id="ID">#%d</xliff:g>) generuojamas"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Pranešimas apie riktą (<xliff:g id="ID">#%d</xliff:g>) užfiksuotas"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Pranešimas apie riktą (<xliff:g id="ID">#%d</xliff:g>) užfiksuotas, bet laukiama ekrano kopijos"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Riktų ataskaitose pateikiami duomenys iš įvairių sistemos žurnalo failų, įskaitant asmeninę ir privačią informaciją. Riktų ataskaitas bendrinkite tik su patikimomis programomis ir žmonėmis."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rodyti šį pranešimą kitą kartą"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Riktų ataskaitos"</string>
diff --git a/packages/Shell/res/values-lv/strings.xml b/packages/Shell/res/values-lv/strings.xml
index 880d651..08e25be 100644
--- a/packages/Shell/res/values-lv/strings.xml
+++ b/packages/Shell/res/values-lv/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Aizsargs"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Kļūdas pārskats <xliff:g id="ID">#%d</xliff:g> tiek ģenerēts"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"Kļūdas pārskats <xliff:g id="ID">#%d</xliff:g> izveidots; gaida ekrānuzņēmumu"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Kļūdu pārskatā ir iekļauti dati no dažādiem sistēmas žurnālfailiem, tostarp personas dati un privāta informācija. Kļūdu pārskatus ieteicams kopīgot tikai ar uzticamām lietotnēm un lietotājiem."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rādīt šo ziņojumu nākamajā reizē"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Kļūdu ziņojumi"</string>
diff --git a/packages/Shell/res/values-mk-rMK/strings.xml b/packages/Shell/res/values-mk-rMK/strings.xml
index 478c189..3f879b7e 100644
--- a/packages/Shell/res/values-mk-rMK/strings.xml
+++ b/packages/Shell/res/values-mk-rMK/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Обвивка"</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_pending_screenshot_title" msgid="5460883450679439591">"Извештајот за грешка <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Извештаите за грешка содржат податоци од разни датотеки за евиденција на системот, вклучувајќи лични и приватни информации. Извештаите за грешка споделувајте ги само со апликации и луѓе на коишто им верувате."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Прикажи ја поракава следниот пат"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаи за грешки"</string>
diff --git a/packages/Shell/res/values-ml-rIN/strings.xml b/packages/Shell/res/values-ml-rIN/strings.xml
index 70e6333..3bb715d 100644
--- a/packages/Shell/res/values-ml-rIN/strings.xml
+++ b/packages/Shell/res/values-ml-rIN/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"ഷെൽ"</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_pending_screenshot_title" msgid="5460883450679439591">"ബഗ് റിപ്പോർട്ട് <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"വ്യക്തിഗതവും സ്വകാര്യവുമായ വിവരങ്ങൾ ഉൾപ്പെടെ, സിസ്റ്റത്തിന്റെ നിരവധി ലോഗ് ഫയലുകളിൽ നിന്നുള്ള ഡാറ്റ, ബഗ് റിപ്പോർട്ടുകളിൽ അടങ്ങിയിരിക്കുന്നു. നിങ്ങൾ വിശ്വസിക്കുന്ന അപ്ലിക്കേഷനുകൾക്കും ആളുകൾക്കും മാത്രം ബഗ് റിപ്പോർട്ടുകൾ പങ്കിടുക."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ഈ സന്ദേശം അടുത്ത തവണ ദൃശ്യമാക്കുക"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ബഗ് റിപ്പോർട്ടുകൾ"</string>
diff --git a/packages/Shell/res/values-mn-rMN/strings.xml b/packages/Shell/res/values-mn-rMN/strings.xml
index 49196a2..296afab 100644
--- a/packages/Shell/res/values-mn-rMN/strings.xml
+++ b/packages/Shell/res/values-mn-rMN/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Шел"</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_pending_screenshot_title" msgid="5460883450679439591">"Алдааны тайлан <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Алдааны репорт нь хувийн болон нууц мэдээлэл зэргийг агуулсан системийн төрөл бүрийн лог файлын датаг агуулна. Алдааны репортыг зөвхөн итгэлтэй апп болон хүмүүст хуваалцана уу."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Энэ мессежийг дараагийн удаа харуулах"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Гэмтлийн тухай тайлан"</string>
diff --git a/packages/Shell/res/values-mr-rIN/strings.xml b/packages/Shell/res/values-mr-rIN/strings.xml
index 19a8706..8eb5686 100644
--- a/packages/Shell/res/values-mr-rIN/strings.xml
+++ b/packages/Shell/res/values-mr-rIN/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"शेल"</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_pending_screenshot_title" msgid="5460883450679439591">"दोष अहवाल <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"दोष अहवालांमध्ये वैयक्तिक आणि खाजगी माहितीसह, सिस्टमच्या अनेक लॉग फायलींमधील डेटा असतो. केवळ आपला विश्वास असलेल्या अॅप्स आणि लोकांसह दोष अहवाल सामायिक करा."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"पुढील वेळी हा संदेश दर्शवा"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"दोष अहवाल"</string>
diff --git a/packages/Shell/res/values-ms-rMY/strings.xml b/packages/Shell/res/values-ms-rMY/strings.xml
index c9f5375..e758745 100644
--- a/packages/Shell/res/values-ms-rMY/strings.xml
+++ b/packages/Shell/res/values-ms-rMY/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Laporan pepijat <xliff:g id="ID">#%d</xliff:g> sedang dijana"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Laporan pepijat <xliff:g id="ID">#%d</xliff:g> telah ditangkap"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Laporan pepijat <xliff:g id="ID">#%d</xliff:g> ditangkap, menunggu tngkpn skrin"</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="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 sehingga 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 sehingga tangkapan skrin selesai"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Laporan pepijat mengandungi data dari pelbagai fail log sistem, termasuk maklumat peribadi dan sulit. Kongsikan laporan pepijat hanya dengan apl dan orang yang anda percayai."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tunjukkan mesej ini pada masa akan datang"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan pepijat"</string>
diff --git a/packages/Shell/res/values-my-rMM/strings.xml b/packages/Shell/res/values-my-rMM/strings.xml
index 4250eda..6f02e70 100644
--- a/packages/Shell/res/values-my-rMM/strings.xml
+++ b/packages/Shell/res/values-my-rMM/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"အခွံ"</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_pending_screenshot_title" msgid="5460883450679439591">"ချွတ်ယွင်းချက် အစီရင်ခံစာ <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"အမှားအယွင်း မှတ်တမ်းမှာ ပါရှိသော အချက်အလက်များမှာ ကိုယ်ရေးကိုယ်တာ နဲ့ လုံခြုံရေး အချက်အလက်များပါဝင်သော စနစ်မှ ပြုလုပ်မှု မှတ်တမ်းများ ဖြစ်ပါသည်၊ အမှားအယွင်း မှတ်တမ်းများကို ယုံကြည်ရသော အပလီကေးရှင်းများနဲ့ လူများကိုသာ ပေးဝေပြသမှု လုပ်ပါရန်။"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ဤစာတန်းကို နောက်တစ်ခါတွင် ပြရန်"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ချို့ယွင်းမှု အစီရင်ခံစာများ"</string>
diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml
index aa86ae4..0605e0e 100644
--- a/packages/Shell/res/values-nb/strings.xml
+++ b/packages/Shell/res/values-nb/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Kommandoliste"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Feilrapporten <xliff:g id="ID">#%d</xliff:g> blir generert"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Feilrapporten <xliff:g id="ID">#%d</xliff:g> er fullført"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Feilrapporten <xliff:g id="ID">#%d</xliff:g> er hentet. Venter på skjermdumpen"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Feilrapporter inkluderer data fra systemets forskjellige loggfiler. Dette omfatter personlig og privat informasjon. Du bør bare dele feilrapporter med apper og folk du stoler på."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne meldingen neste gang"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Feilrapporter"</string>
diff --git a/packages/Shell/res/values-ne-rNP/strings.xml b/packages/Shell/res/values-ne-rNP/strings.xml
index 43c2a5c..9fb1bcb 100644
--- a/packages/Shell/res/values-ne-rNP/strings.xml
+++ b/packages/Shell/res/values-ne-rNP/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"सेल"</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_pending_screenshot_title" msgid="5460883450679439591">"बग रिपोर्ट <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"बग रिपोर्टहरूमा प्रणालीका विभिन्न लग फाइलहरूबाट व्यक्तिगत तथा नीजि सूचनासहितको डेटा रहन्छ। बग रिपोर्टहरू अनुप्रयोगहरू र तपाईँले विश्वास गरेका व्यक्तिहरूसँग मात्र साझेदारी गर्नुहोस्।"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"यो सन्देश अर्को पटक देखाउनुहोस्"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रिपोर्टहरू"</string>
diff --git a/packages/Shell/res/values-nl/strings.xml b/packages/Shell/res/values-nl/strings.xml
index 01fd577..3022691 100644
--- a/packages/Shell/res/values-nl/strings.xml
+++ b/packages/Shell/res/values-nl/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Bugrapport <xliff:g id="ID">#%d</xliff:g> wordt gegenereerd"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Bugrapport <xliff:g id="ID">#%d</xliff:g> is vastgelegd"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Bugrapport <xliff:g id="ID">#%d</xliff:g> vastgelegd, screenshot in behandeling"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Foutenrapporten bevatten gegevens uit de verschillende logbestanden van het systeem, waaronder persoonlijke en privégegevens. Deel foutenrapporten alleen met apps en mensen die u vertrouwt."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Dit bericht de volgende keer weergeven"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Foutenrapporten"</string>
diff --git a/packages/Shell/res/values-pa-rIN/strings.xml b/packages/Shell/res/values-pa-rIN/strings.xml
index 3494199..d44daf7 100644
--- a/packages/Shell/res/values-pa-rIN/strings.xml
+++ b/packages/Shell/res/values-pa-rIN/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"ਸ਼ੈਲ"</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_pending_screenshot_title" msgid="5460883450679439591">"ਬੱਗ ਰਿਪੋਰਟ <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"ਬਗ ਰਿਪੋਰਟਾਂ ਵਿੱਚ ਸਿਸਟਮ ਦੀਆਂ ਭਿੰਨ ਲੌਗ ਫਾਈਲਾਂ ਦਾ ਡਾਟਾ ਹੁੰਦਾ ਹੈ, ਨਿੱਜੀ ਅਤੇ ਪ੍ਰਾਈਵੇਟ ਜਾਣਕਾਰੀ ਸਮੇਤ। ਕੇਵਲ ਉਹਨਾਂ ਐਪਸ ਅਤੇ ਲੋਕਾਂ ਨਾਲ ਬਗ ਰਿਪੋਰਟਾਂ ਸ਼ੇਅਰ ਕਰੋ, ਜਿਹਨਾਂ ਤੇ ਤੁਸੀਂ ਭਰੋਸਾ ਕਰਦੇ ਹੋ।"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ਅਗਲੀ ਵਾਰ ਇਹ ਸੁਨੇਹਾ ਦਿਖਾਓ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ਬਗ ਰਿਪੋਰਟਾਂ"</string>
diff --git a/packages/Shell/res/values-pl/strings.xml b/packages/Shell/res/values-pl/strings.xml
index 91934c9..b4977a4 100644
--- a/packages/Shell/res/values-pl/strings.xml
+++ b/packages/Shell/res/values-pl/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Powłoka"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Generuję raport o błędzie <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Raport o błędzie <xliff:g id="ID">#%d</xliff:g> został zapisany"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Raport <xliff:g id="ID">#%d</xliff:g> zapisany. Zrzut nie jest jeszcze gotowy"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Raporty o błędach zawierają dane z różnych plików dzienników systemu, w tym dane osobowe i prywatne. Udostępniaj je tylko aplikacjom i osobom, którym ufasz."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaż ten komunikat następnym razem"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Raporty o błędach"</string>
diff --git a/packages/Shell/res/values-pt-rBR/strings.xml b/packages/Shell/res/values-pt-rBR/strings.xml
index 41f4e24..2306c39 100644
--- a/packages/Shell/res/values-pt-rBR/strings.xml
+++ b/packages/Shell/res/values-pt-rBR/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório do bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório do bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Relatório do bug <xliff:g id="ID">#%d</xliff:g> capturado, captura pendente"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de bugs contêm dados de diversos arquivos de registro do sistema, inclusive informações pessoais e particulares. Compartilhe relatórios de bugs somente com apps e pessoas nos quais você confia."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
diff --git a/packages/Shell/res/values-pt-rPT/strings.xml b/packages/Shell/res/values-pt-rPT/strings.xml
index 416db80..7daed8b 100644
--- a/packages/Shell/res/values-pt-rPT/strings.xml
+++ b/packages/Shell/res/values-pt-rPT/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório de erro <xliff:g id="ID">#%d</xliff:g> está a ser criado"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório de erro <xliff:g id="ID">#%d</xliff:g> criado"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Rel. de erro <xliff:g id="ID">#%d</xliff:g> capturado, captura de ecrã pendente"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de erros incluem dados de vários ficheiros de registo do sistema, nomeadamente informações pessoais e privadas. Partilhe relatórios de erros apenas com aplicações e pessoas fidedignas."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de erros"</string>
diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml
index 41f4e24..2306c39 100644
--- a/packages/Shell/res/values-pt/strings.xml
+++ b/packages/Shell/res/values-pt/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório do bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório do bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Relatório do bug <xliff:g id="ID">#%d</xliff:g> capturado, captura pendente"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de bugs contêm dados de diversos arquivos de registro do sistema, inclusive informações pessoais e particulares. Compartilhe relatórios de bugs somente com apps e pessoas nos quais você confia."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml
index 4112e60..9529ade 100644
--- a/packages/Shell/res/values-ro/strings.xml
+++ b/packages/Shell/res/values-ro/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Raportul de eroare <xliff:g id="ID">#%d</xliff:g> se generează"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Raportul de eroare <xliff:g id="ID">#%d</xliff:g> a fost creat"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Raport eroare <xliff:g id="ID">#%d</xliff:g> creat, captură ecran în așteptare"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Rapoartele despre erori conțin date din diferite fișiere de jurnal ale sistemului, inclusiv informații private și personale. Permiteți accesul la rapoartele despre erori numai aplicațiilor și persoanelor în care aveți încredere."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afișați acest mesaj data viitoare"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Rapoarte de erori"</string>
diff --git a/packages/Shell/res/values-ru/strings.xml b/packages/Shell/res/values-ru/strings.xml
index ffd2e66..4aafbba 100644
--- a/packages/Shell/res/values-ru/strings.xml
+++ b/packages/Shell/res/values-ru/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Оболочка"</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_pending_screenshot_title" msgid="5460883450679439591">"Отчет об ошибке (<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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Отчеты об ошибках содержат данные различных системных журналов и могут включать личную информацию. Рекомендуем открывать к ним доступ только лицам и приложениям, заслуживающим доверие."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показать это сообщение в следующий раз"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Отчеты об ошибках"</string>
diff --git a/packages/Shell/res/values-si-rLK/strings.xml b/packages/Shell/res/values-si-rLK/strings.xml
index 807ebb1..1ea7586 100644
--- a/packages/Shell/res/values-si-rLK/strings.xml
+++ b/packages/Shell/res/values-si-rLK/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"ෂෙල්"</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_pending_screenshot_title" msgid="5460883450679439591">"දෝෂ වාර්තාව <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"පුද්ගලික සහ පෞද්ගලික තොරතුරු ඇතුළත්ව පද්ධතියේ විවිධ ලොග් ගොනු වල දත්ත දෝෂ වාර්තාවේ අඩංගු වේ. ඔබට විශ්වාසවන්ත යෙදුම් සහ පුද්ගලයින් සමඟ පමණක් දෝෂ වාර්තා බෙදා ගන්න."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ඊළඟ වෙලාවේ මෙම පණිවිඩය පෙන්වන්න"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"දෝෂ වාර්තා"</string>
diff --git a/packages/Shell/res/values-sk/strings.xml b/packages/Shell/res/values-sk/strings.xml
index a62a6eb..fa17326 100644
--- a/packages/Shell/res/values-sk/strings.xml
+++ b/packages/Shell/res/values-sk/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Prostredie"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Generuje sa hlásenie chyby <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Hlásenie chyby <xliff:g id="ID">#%d</xliff:g> bolo zaznamenané"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Hlásenie chyby <xliff:g id="ID">#%d</xliff:g> bolo zaznamenané, ale čaká sa na snímku obrazovky"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Správy o chybách obsahujú údaje z rôznych súborov denníkov systému vrátane osobných a súkromných informácií. Zdieľajte ich iba s dôveryhodnými aplikáciami a ľuďmi."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobraziť túto správu nabudúce"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Hlásenia chýb"</string>
diff --git a/packages/Shell/res/values-sl/strings.xml b/packages/Shell/res/values-sl/strings.xml
index 6d1be5a..fdf3446 100644
--- a/packages/Shell/res/values-sl/strings.xml
+++ b/packages/Shell/res/values-sl/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Lupina"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Poročilo o napaki <xliff:g id="ID">#%d</xliff:g> je v izdelavi"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Poročilo o napaki <xliff:g id="ID">#%d</xliff:g> zajeto"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Poroč. o napakah <xliff:g id="ID">#%d</xliff:g> zajeto, posnetek zaslona nastaja"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Poročila o napakah vsebujejo podatke iz različnih dnevniških datotek sistema, vključno z osebnimi in zasebnimi podatki. Poročila o napakah delite samo z aplikacijami in ljudmi, ki jim zaupate."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaži to sporočilo naslednjič"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Poročila o napakah"</string>
diff --git a/packages/Shell/res/values-sq-rAL/strings.xml b/packages/Shell/res/values-sq-rAL/strings.xml
index ce990e9..f43ce9f 100644
--- a/packages/Shell/res/values-sq-rAL/strings.xml
+++ b/packages/Shell/res/values-sq-rAL/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Guaska"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Raporti i defekteve në kod <xliff:g id="ID">#%d</xliff:g> po krijohet"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"Raporti i defekteve në kod <xliff:g id="ID">#%d</xliff:g> u regjistrua, por pamja e çastit është në pritje"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Raportet e gabimeve përmbajnë të dhëna nga skedarë të ndryshëm ditarësh sistemi, përfshi informacione personale dhe private. Shpërndaji publikisht raportet e gabimeve vetëm me aplikacionet dhe personat që iu beson."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tregoje këtë mesazh herën tjetër"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Raportet e gabimeve"</string>
diff --git a/packages/Shell/res/values-sr/strings.xml b/packages/Shell/res/values-sr/strings.xml
index 0157a25..3bea4f3 100644
--- a/packages/Shell/res/values-sr/strings.xml
+++ b/packages/Shell/res/values-sr/strings.xml
@@ -19,10 +19,13 @@
<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_pending_screenshot_title" msgid="5460883450679439591">"Извештај о грешци <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Извештаји о грешкама садрже податке из различитих системских датотека евиденције, укључујући личне и приватне податке. Делите извештаје о грешкама само са апликацијама и људима у које имате поверења."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Прикажи ову поруку следећи пут"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаји о грешкама"</string>
diff --git a/packages/Shell/res/values-sv/strings.xml b/packages/Shell/res/values-sv/strings.xml
index 88af0db..a8cb64d 100644
--- a/packages/Shell/res/values-sv/strings.xml
+++ b/packages/Shell/res/values-sv/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Skal"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Felrapporten <xliff:g id="ID">#%d</xliff:g> genereras"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Felrapporten <xliff:g id="ID">#%d</xliff:g> har skapats"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Felrapporten <xliff:g id="ID">#%d</xliff:g> har skapats, väntar på skärmdumpen"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Felrapporter innehåller data från systemets olika loggfiler, inklusive personliga och privata uppgifter. Dela bara felrapporter med personer du litar på."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Visa det här meddelandet nästa gång"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Felrapporter"</string>
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index 01df55e..945bb60 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Ganda"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Ripoti ya hitilafu ya <xliff:g id="ID">#%d</xliff:g> inatayarishwa"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Ripoti ya hitilafu ya <xliff:g id="ID">#%d</xliff:g> imerekodiwa"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Ripoti ya hitilafu ya <xliff:g id="ID">#%d</xliff:g> imerekodiwa lakini picha ya skrini bado haijakamilishwa"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Ripoti ya hitilafu ina data kutoka kwenye faili za kumbukumbu mbalimbali za mfumo, pamoja na maelezo ya kibinafsi na faragha. Shiriki ripoti ya hitilafu na programu na watu unaowaamini pekee."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Onyesha ujumbe huu wakati mwingine"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Ripoti za hitilafu"</string>
diff --git a/packages/Shell/res/values-ta-rIN/strings.xml b/packages/Shell/res/values-ta-rIN/strings.xml
index 8238d25..a501764 100644
--- a/packages/Shell/res/values-ta-rIN/strings.xml
+++ b/packages/Shell/res/values-ta-rIN/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"ஷெல்"</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_pending_screenshot_title" msgid="5460883450679439591">"பிழை அறிக்கை <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"பிழை அறிக்கைகளில், சொந்த வாழ்க்கை மற்றும் தனிப்பட்ட தகவல் உள்பட கணினியின் பல்வேறு பதிவுகளில் உள்ள தரவு இருக்கும். நீங்கள் நம்பும் பயன்பாடுகள் மற்றும் நபர்களுடன் மட்டும் பிழை அறிக்கைகளைப் பகிரவும்."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"இந்தச் செய்தியை அடுத்த முறைக் காட்டு"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"பிழை அறிக்கைகள்"</string>
@@ -34,7 +37,7 @@
<string name="bugreport_screenshot_failed" msgid="5853049140806834601">"ஸ்கிரீன் ஷாட்டை எடுக்க முடியவில்லை."</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"பிழை அறிக்கை <xliff:g id="ID">#%d</xliff:g> இன் விவரங்கள்"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"கோப்புப்பெயர்"</string>
- <string name="bugreport_info_title" msgid="2306030793918239804">"பிழைத் தலைப்பு"</string>
+ <string name="bugreport_info_title" msgid="2306030793918239804">"பிழை தலைப்பு"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"பிழை குறித்த சுருக்க விவரம்"</string>
<string name="save" msgid="4781509040564835759">"சேமி"</string>
</resources>
diff --git a/packages/Shell/res/values-te-rIN/strings.xml b/packages/Shell/res/values-te-rIN/strings.xml
index 3ed1f95..f406869 100644
--- a/packages/Shell/res/values-te-rIN/strings.xml
+++ b/packages/Shell/res/values-te-rIN/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"షెల్"</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_pending_screenshot_title" msgid="5460883450679439591">"బగ్ నివే. <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"బగ్ నివేదికలు వ్యక్తిగతమైన మరియు రహస్యమైన సమాచారంతో సహా సిస్టమ్ యొక్క విభిన్న లాగ్ ఫైల్ల్లోని డేటాను కలిగి ఉంటాయి. కనుక బగ్ నివేదికలను మీరు విశ్వసించే అనువర్తనాలు మరియు వ్యక్తులతో మాత్రమే భాగస్వామ్యం చేయండి."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"తదుపరిసారి ఈ సందేశాన్ని చూపు"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"బగ్ నివేదికలు"</string>
diff --git a/packages/Shell/res/values-th/strings.xml b/packages/Shell/res/values-th/strings.xml
index f28bac6..db7f823 100644
--- a/packages/Shell/res/values-th/strings.xml
+++ b/packages/Shell/res/values-th/strings.xml
@@ -19,10 +19,13 @@
<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_pending_screenshot_title" msgid="5460883450679439591">"จับภาพรายงานข้อบกพร่อง <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"รายงานข้อบกพร่องมีข้อมูลจากไฟล์บันทึกต่างๆ ของระบบ รวมถึงข้อมูลส่วนตัว แชร์รายงานข้อบกพร่องกับแอปและบุคคลที่คุณไว้ใจเท่านั้น"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"แสดงข้อความนี้ในครั้งต่อไป"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"รายงานข้อบกพร่อง"</string>
diff --git a/packages/Shell/res/values-tl/strings.xml b/packages/Shell/res/values-tl/strings.xml
index d270401..f99a1b8 100644
--- a/packages/Shell/res/values-tl/strings.xml
+++ b/packages/Shell/res/values-tl/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Binubuo na ang ulat ng bug na <xliff:g id="ID">#%d</xliff:g>"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"Nakunan ang ulat ng bug <xliff:g id="ID">#%d</xliff:g>, nakabinbin ang screenshot"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Naglalaman ang mga ulat ng bug ng data mula sa iba\'t ibang file ng log ng system, kabilang ang personal at pribadong impormasyon. Magbahagi lang ng mga ulat ng bug sa apps at mga tao na pinagkakatiwalaan mo."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ipakita ang mensaheng ito sa susunod"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Mga ulat sa bug"</string>
diff --git a/packages/Shell/res/values-tr/strings.xml b/packages/Shell/res/values-tr/strings.xml
index 6da3490..be448af 100644
--- a/packages/Shell/res/values-tr/strings.xml
+++ b/packages/Shell/res/values-tr/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Kabuk"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Hata raporu (<xliff:g id="ID">#%d</xliff:g>) oluşturuluyor"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Hata raporu (<xliff:g id="ID">#%d</xliff:g>) yakalandı"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"<xliff:g id="ID">#%d</xliff:g> hata raporu yakalandı, ekran görüntüsü bekleniyor"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Hata raporları, kişisel ve özel bilgiler dahil olmak üzere sistemin çeşitli günlük dosyalarından veriler içerir. Hata raporlarını sadece güvendiğiniz uygulamalar ve kişilerle paylaşın."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bir dahaki sefere bu iletiyi göster"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Hata raporları"</string>
diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml
index 00a7793..c35c61c 100644
--- a/packages/Shell/res/values-uk/strings.xml
+++ b/packages/Shell/res/values-uk/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Оболонка"</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_pending_screenshot_title" msgid="5460883450679439591">"Повідомлення <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Звіти про помилки містять дані з різних файлів журналу системи, зокрема особисті та конфіденційні. Надсилайте звіт про помилки лише тим, кому довіряєте."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показати це повідомлення наступного разу"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Звіти про помилки"</string>
diff --git a/packages/Shell/res/values-ur-rPK/strings.xml b/packages/Shell/res/values-ur-rPK/strings.xml
index abf6940..ef6801f 100644
--- a/packages/Shell/res/values-ur-rPK/strings.xml
+++ b/packages/Shell/res/values-ur-rPK/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"شیل"</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_pending_screenshot_title" msgid="5460883450679439591">"بگ رپورٹ <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"بَگ رپورٹس میں سسٹم کی مختلف لاگ فائلوں سے ڈیٹا شامل ہوتا ہے، بشمول ذاتی اور نجی معلومات۔ بَگ رپورٹس کا اشتراک صرف اپنے بھروسے مند ایپس اور لوگوں کے ساتھ کریں۔"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"یہ پیغام اگلی بار دکھائیں"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"بگ رپورٹس"</string>
diff --git a/packages/Shell/res/values-uz-rUZ/strings.xml b/packages/Shell/res/values-uz-rUZ/strings.xml
index 7782b42..c1a1948 100644
--- a/packages/Shell/res/values-uz-rUZ/strings.xml
+++ b/packages/Shell/res/values-uz-rUZ/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Terminal"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Xatoliklar hisoboti (<xliff:g id="ID">#%d</xliff:g>) tayyorlanmoqda"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Xatoliklar hisoboti (<xliff:g id="ID">#%d</xliff:g>) yozib olindi"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Xatoliklar hisoboti (<xliff:g id="ID">#%d</xliff:g>) tayyor, skrinshot kutilmoqda"</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="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 bosing yoki skrinshot saqlanguncha kuting"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Xatoliklar hisobotini darhol yuborish uchun bosing yoki skrinshot saqlanguncha kuting"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Xatolik hisobotlari tizimdagi har xil jurnal fayllardagi ma’lumotlarni, shuningdek, shaxsiy hamda maxfiy ma’lumotlarni o‘z ichiga oladi. Xatolik hisobotlarini faqat ishonchli dasturlar va odamlar bilan bo‘lishing."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ushbu xabar keyingi safar ko‘rsatilsin"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Xatoliklar hisoboti"</string>
diff --git a/packages/Shell/res/values-vi/strings.xml b/packages/Shell/res/values-vi/strings.xml
index 27a9c7f..2229991 100644
--- a/packages/Shell/res/values-vi/strings.xml
+++ b/packages/Shell/res/values-vi/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Báo cáo lỗi <xliff:g id="ID">#%d</xliff:g> đang được tạo"</string>
<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_finished_pending_screenshot_title" msgid="5460883450679439591">"Đã chụp báo cáo lỗi <xliff:g id="ID">#%d</xliff:g>, đang chờ ảnh chụp màn hình"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Các báo cáo lỗi chứa dữ liệu từ nhiều tệp nhật ký khác nhau của hệ thống, bao gồm cả thông tin cá nhân và riêng tư. Chỉ chia sẻ báo cáo lỗi với các ứng dụng và những người mà bạn tin tưởng."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Hiển thị thông báo này vào lần tới"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Báo cáo lỗi"</string>
diff --git a/packages/Shell/res/values-zh-rCN/strings.xml b/packages/Shell/res/values-zh-rCN/strings.xml
index 65cfbbc..b09a7d0 100644
--- a/packages/Shell/res/values-zh-rCN/strings.xml
+++ b/packages/Shell/res/values-zh-rCN/strings.xml
@@ -19,10 +19,13 @@
<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_pending_screenshot_title" msgid="5460883450679439591">"已捕获错误报告 <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"错误报告包含的数据来自于系统的各个日志文件,其中包含个人信息和隐私信息。请务必只与您信任的应用和用户分享错误报告。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再显示这条讯息"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"错误报告"</string>
diff --git a/packages/Shell/res/values-zh-rHK/strings.xml b/packages/Shell/res/values-zh-rHK/strings.xml
index 34a849e..384eee7 100644
--- a/packages/Shell/res/values-zh-rHK/strings.xml
+++ b/packages/Shell/res/values-zh-rHK/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"命令介面"</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_pending_screenshot_title" msgid="5460883450679439591">"已擷取錯誤報告 <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告中有來自系統各個記錄檔案的資料,包括個人和私人資料。請只與您信任的應用程式和使用者分享錯誤報告。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再顯示這則訊息"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml
index fff73bb..2702bad 100644
--- a/packages/Shell/res/values-zh-rTW/strings.xml
+++ b/packages/Shell/res/values-zh-rTW/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"殼層"</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_pending_screenshot_title" msgid="5460883450679439591">"錯誤報告 <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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告的資料來自系統各個紀錄檔,包括個人和私密資訊。請務必只與您信任的應用程式和使用者分享錯誤報告。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次仍顯示這則訊息"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
diff --git a/packages/Shell/res/values-zu/strings.xml b/packages/Shell/res/values-zu/strings.xml
index 868de3e..561ac0a 100644
--- a/packages/Shell/res/values-zu/strings.xml
+++ b/packages/Shell/res/values-zu/strings.xml
@@ -19,10 +19,13 @@
<string name="app_label" msgid="3701846017049540910">"I-Shell"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Umbiko wesiphazamisi ongu-<xliff:g id="ID">#%d</xliff:g> uyacutshungulwa"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Umbiko wesiphazamisi ongu-<xliff:g id="ID">#%d</xliff:g> uthwetshuliwe"</string>
+ <string name="bugreport_finished_pending_screenshot_title" msgid="5460883450679439591">"Umbiko wesiphazamisi esingu-<xliff:g id="ID">#%d</xliff:g> uthwetshuliwe kodwa isithombe-skrini silindile"</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="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>
<string name="bugreport_confirm" msgid="5130698467795669780">"Imibiko yeziphazamisi iqukethe idatha yamafayela wokungena ahlukile wesistimu, afaka ulwazi lomuntu siqu noma lobumfihlo. Yabelana kuphela ngemibiko yeziphazamisi nezinhlelo zokusebenza nabantu obathembayo."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bonisa lo mlayezo ngesikhathi esilandelayo"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Imibiko yeziphazamiso"</string>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index 38ea880..5d90189 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -20,6 +20,8 @@
<string name="bugreport_in_progress_title">Bug report <xliff:g id="id">#%d</xliff:g> is being generated</string>
<!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] -->
<string name="bugreport_finished_title">Bug report <xliff:g id="id">#%d</xliff:g> captured</string>
+ <!-- Title of notification indicating a bugreport has been successfully captured, but screenshot is not finished yet. [CHAR LIMIT=50] -->
+ <string name="bugreport_finished_pending_screenshot_title">Bug report <xliff:g id="id">#%d</xliff:g> captured but screenshot pending</string>
<!-- Title of notification indicating a bugreport is being updated before it can be shared. [CHAR LIMIT=50] -->
<string name="bugreport_updating_title">Adding details to the bug report</string>
<!-- Content notification indicating a bugreport is being updated before it can be shared, asking the user to wait [CHAR LIMIT=50] -->
@@ -29,7 +31,10 @@
<string name="bugreport_finished_text" product="watch">Swipe left to share your bug report</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] -->
+ <string name="bugreport_finished_pending_screenshot_text" product="watch">Tap to share your bug report without a screenshot or wait for the screenshot to finish</string>
+ <!-- Text of notification indicating that tapping will share the captured bugreport, but giving user the option to wait for the screenshot. [CHAR LIMIT=100] -->
+ <string name="bugreport_finished_pending_screenshot_text" product="default">Tap to share your bug report without a screenshot or wait for the screenshot to finish</string>
<!-- Body of dialog informing user about contents of a bugreport. [CHAR LIMIT=NONE] -->
<string name="bugreport_confirm">Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people you trust.</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 0b52588..10c5416 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -56,7 +56,6 @@
import android.app.Service;
import android.content.ClipData;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
@@ -121,8 +120,6 @@
static final String INTENT_BUGREPORT_FINISHED = "android.intent.action.BUGREPORT_FINISHED";
static final String INTENT_REMOTE_BUGREPORT_FINISHED =
"android.intent.action.REMOTE_BUGREPORT_FINISHED";
- static final String INTENT_REMOTE_BUGREPORT_DISPATCH =
- "android.intent.action.REMOTE_BUGREPORT_DISPATCH";
// Internal intents used on notification actions.
static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL";
@@ -208,7 +205,7 @@
mMainHandler = new ServiceHandler("BugreportProgressServiceMainThread");
mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread");
- mScreenshotsDir = new File(new ContextWrapper(mContext).getFilesDir(), SCREENSHOT_DIR);
+ mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR);
if (!mScreenshotsDir.exists()) {
Log.i(TAG, "Creating directory " + mScreenshotsDir + " to store temporary screenshots");
if (!mScreenshotsDir.mkdir()) {
@@ -507,9 +504,9 @@
Log.d(TAG, "Removing ID " + id);
mProcesses.remove(id);
}
- stopSelfWhenDone();
Log.v(TAG, "stopProgress(" + id + "): cancel notification");
NotificationManager.from(mContext).cancel(TAG, id);
+ stopSelfWhenDone();
}
/**
@@ -623,7 +620,11 @@
* upon receiving a {@link #INTENT_BUGREPORT_STARTED}.
*/
private void takeScreenshot(int id, boolean delayed) {
- MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
+ if (delayed) {
+ // Only logs screenshots requested from the notification action.
+ MetricsLogger.action(this,
+ MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
+ }
if (getInfo(id) == null) {
// Most likely am killed Shell before user tapped the notification. Since system might
// be too busy anwyays, it's better to ignore the notification and switch back to the
@@ -717,7 +718,7 @@
if (info.finished) {
Log.d(TAG, "Screenshot finished after bugreport; updating share notification");
info.renameScreenshots(mScreenshotsDir);
- sendBugreportNotification(mContext, info);
+ sendBugreportNotification(mContext, info, mTakingScreenshot);
}
msg = mContext.getString(R.string.bugreport_screenshot_taken);
} else {
@@ -806,10 +807,10 @@
boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt");
if (!isPlainText) {
// Already zipped, send it right away.
- sendBugreportNotification(context, info);
+ sendBugreportNotification(context, info, mTakingScreenshot);
} else {
// Asynchronously zip the file first, then send it.
- sendZippedBugreportNotification(context, info);
+ sendZippedBugreportNotification(context, info, mTakingScreenshot);
}
}
@@ -879,6 +880,8 @@
info = sharedInfo;
Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
+ mProcesses + "), using info from intent instead (" + info + ")");
+ } else {
+ Log.v(TAG, "shareBugReport(): id " + id + " info = " + info);
}
addDetailsToZipFile(mContext, info);
@@ -904,7 +907,8 @@
/**
* Sends a notification indicating the bugreport has finished so use can share it.
*/
- private static void sendBugreportNotification(Context context, BugreportInfo info) {
+ private static void sendBugreportNotification(Context context, BugreportInfo info,
+ boolean takingScreenshot) {
// Since adding the details can take a while, do it before notifying user.
addDetailsToZipFile(context, info);
@@ -915,12 +919,20 @@
shareIntent.putExtra(EXTRA_ID, info.id);
shareIntent.putExtra(EXTRA_INFO, info);
- final String title = context.getString(R.string.bugreport_finished_title, info.id);
+ final String title, content;
+ if (takingScreenshot) {
+ title = context.getString(R.string.bugreport_finished_pending_screenshot_title,
+ info.id);
+ content = context.getString(R.string.bugreport_finished_pending_screenshot_text);
+ } else {
+ title = context.getString(R.string.bugreport_finished_title, info.id);
+ content = context.getString(R.string.bugreport_finished_text);
+ }
final Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setContentTitle(title)
.setTicker(title)
- .setContentText(context.getString(R.string.bugreport_finished_text))
+ .setContentText(content)
.setContentIntent(PendingIntent.getService(context, info.id, shareIntent,
PendingIntent.FLAG_UPDATE_CURRENT))
.setDeleteIntent(newCancelIntent(context, info))
@@ -959,12 +971,12 @@
* Sends a zipped bugreport notification.
*/
private static void sendZippedBugreportNotification(final Context context,
- final BugreportInfo info) {
+ final BugreportInfo info, final boolean takingScreenshot) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
zipBugreport(info);
- sendBugreportNotification(context, info);
+ sendBugreportNotification(context, info, takingScreenshot);
return null;
}
}.execute();
@@ -1182,14 +1194,13 @@
* Takes a screenshot and save it to the given location.
*/
private static boolean takeScreenshot(Context context, String screenshotFile) {
- ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE))
- .vibrate(150);
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) {
+ ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).vibrate(150);
return true;
}
Log.e(TAG, "screencap (" + screencap.command() + ") failed: " + exitValue);
@@ -1534,6 +1545,7 @@
final File newFile;
if (!newName.equals(oldName)) {
final File renamedFile = new File(screenshotDir, newName);
+ Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
} else {
Log.w(TAG, "Name didn't change: " + oldName); // Shouldn't happen.
diff --git a/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java b/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java
index be54b43..634c3b4 100644
--- a/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java
@@ -18,13 +18,13 @@
import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
import static com.android.shell.BugreportProgressService.INTENT_REMOTE_BUGREPORT_FINISHED;
-import static com.android.shell.BugreportProgressService.INTENT_REMOTE_BUGREPORT_DISPATCH;
import static com.android.shell.BugreportProgressService.getFileExtra;
import static com.android.shell.BugreportProgressService.getUri;
import static com.android.shell.BugreportReceiver.cleanupOldFiles;
import java.io.File;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -41,8 +41,6 @@
public class RemoteBugreportReceiver extends BroadcastReceiver {
private static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
- private static final String EXTRA_REMOTE_BUGREPORT_HASH =
- "android.intent.extra.REMOTE_BUGREPORT_HASH";
/** Always keep just the last remote bugreport's files around. */
private static final int REMOTE_BUGREPORT_FILES_AMOUNT = 3;
@@ -57,11 +55,12 @@
final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
final Uri bugreportUri = getUri(context, bugreportFile);
- final String bugreportHash = intent.getStringExtra(EXTRA_REMOTE_BUGREPORT_HASH);
+ final String bugreportHash = intent.getStringExtra(
+ DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH);
- final Intent newIntent = new Intent(INTENT_REMOTE_BUGREPORT_DISPATCH);
+ final Intent newIntent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
newIntent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
- newIntent.putExtra(EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
+ newIntent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
context.sendBroadcastAsUser(newIntent, UserHandle.SYSTEM,
android.Manifest.permission.DUMP);
}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index a629aac..47e3b3b 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -130,6 +130,9 @@
private static final boolean RENAMED_SCREENSHOTS = true;
private static final boolean DIDNT_RENAME_SCREENSHOTS = false;
+ private static final boolean PENDING_SCREENSHOT = true;
+ private static final boolean NOT_PENDING_SCREENSHOT = false;
+
private String mDescription;
private String mPlainTextPath;
@@ -409,9 +412,8 @@
sendBugreportStarted(ID2, PID2, NAME2, 1000);
- Bundle extras = sendBugreportFinishedAndGetSharedIntent(ID, mZipPath, mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, ID, PID, TITLE,
- NEW_NAME, TITLE, DESCRIPTION, 1, RENAMED_SCREENSHOTS);
+ sendBugreportFinished(ID, mZipPath, mScreenshotPath);
+ Bundle extras = acceptBugreportAndGetSharedIntent(ID, PENDING_SCREENSHOT);
detailsUi = new DetailsUi(mUiBot, ID2);
detailsUi.assertName(NAME2);
@@ -602,7 +604,7 @@
private Bundle sendBugreportFinishedAndGetSharedIntent(int id, String bugreportPath,
String screenshotPath) {
sendBugreportFinished(id, bugreportPath, screenshotPath);
- return acceptBugreportAndGetSharedIntent(id);
+ return acceptBugreportAndGetSharedIntent(id, NOT_PENDING_SCREENSHOT);
}
/**
@@ -611,7 +613,11 @@
* @return extras sent in the shared intent.
*/
private Bundle acceptBugreportAndGetSharedIntent(int id) {
- acceptBugreport(id);
+ return acceptBugreportAndGetSharedIntent(id, NOT_PENDING_SCREENSHOT);
+ }
+
+ private Bundle acceptBugreportAndGetSharedIntent(int id, boolean pendingScreenshot) {
+ acceptBugreport(id, pendingScreenshot);
mUiBot.chooseActivity(UI_NAME);
return mListener.getExtras();
}
@@ -627,7 +633,13 @@
* Accepts the notification to share the finished bugreport.
*/
private void acceptBugreport(int id) {
- mUiBot.clickOnNotification(mContext.getString(R.string.bugreport_finished_title, id));
+ acceptBugreport(id, NOT_PENDING_SCREENSHOT);
+ }
+
+ private void acceptBugreport(int id, boolean pendingScreenshot) {
+ final int res = pendingScreenshot ? R.string.bugreport_finished_pending_screenshot_title
+ : R.string.bugreport_finished_title;
+ mUiBot.clickOnNotification(mContext.getString(res, id));
}
/**
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b5b7bcd7..334035c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -187,6 +187,15 @@
android:process=":screenshot"
android:exported="false" />
+ <!-- Called from PhoneWindowManager -->
+ <receiver android:name=".screenshot.ScreenshotServiceErrorReceiver"
+ android:process=":screenshot"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.android.systemui.screenshot.SHOW_ERROR" />
+ </intent-filter>
+ </receiver>
+
<service android:name=".LoadAverageService"
android:exported="true" />
@@ -245,7 +254,7 @@
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
android:screenOrientation="behind"
- android:theme="@style/RecentsTheme.Wallpaper">
+ android:theme="@style/RecentsTvTheme.Wallpaper">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
</intent-filter>
diff --git a/packages/SystemUI/res/color/notification_guts_buttons.xml b/packages/SystemUI/res/color/notification_guts_buttons.xml
new file mode 100644
index 0000000..f7a4ee9
--- /dev/null
+++ b/packages/SystemUI/res/color/notification_guts_buttons.xml
@@ -0,0 +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" />
+ <item android:color="@android:color/black"
+ android:alpha=".87" />
+</selector>
\ No newline at end of file
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
similarity index 62%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
index b541d13..e98d43f 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/packages/SystemUI/res/drawable/recents_tv_background_gradient.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -12,10 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:startColor="#99000000"
+ android:endColor="#E6000000"
+ android:angle="90"/>
+</shape>
\ No newline at end of file
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/packages/SystemUI/res/drawable/tv_pip_overlay_background.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to packages/SystemUI/res/drawable/tv_pip_overlay_background.xml
index b541d13..e247dec 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_overlay_background.xml
@@ -1,4 +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.
@@ -12,10 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+ <gradient
+ android:startColor="#B2000000"
+ android:endColor="#00000000"
+ android:angle="90"/>
+</shape>
diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
index 99121a9..af3e379 100644
--- a/packages/SystemUI/res/layout/battery_detail.xml
+++ b/packages/SystemUI/res/layout/battery_detail.xml
@@ -33,7 +33,7 @@
<com.android.settingslib.graph.UsageView
android:id="@+id/battery_usage"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="141dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="24dp"
systemui:sideLabels="@array/battery_labels"
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 8b337ea..c1fe1a8 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -33,4 +33,10 @@
android:layout_height="match_parent"
android:src="@android:color/white"
android:visibility="gone" />
+ <com.android.systemui.screenshot.ScreenshotSelectorView
+ android:id="@+id/global_screenshot_selector"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:pointerShape="crosshair"/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 4b0c834..1ab6bf9 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -63,29 +63,32 @@
android:id="@+id/importance_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:paddingBottom="8dip"
+ android:paddingTop="4dp"
+ android:paddingBottom="16dip"
android:paddingEnd="8dp" >
<RadioButton
android:id="@+id/silent_importance"
android:layout_width="wrap_content"
android:layout_height="48dp"
+ android:paddingStart="32dp"
android:text="@string/show_silently"
- style="@style/TextAppearance.NotificationGuts.Primary"
- android:buttonTint="#858383" />
+ style="@style/TextAppearance.NotificationGuts.Radio"
+ android:buttonTint="@color/notification_guts_buttons" />
<RadioButton
android:id="@+id/block_importance"
android:layout_width="wrap_content"
android:layout_height="48dp"
+ android:paddingStart="32dp"
android:text="@string/block"
- style="@style/TextAppearance.NotificationGuts.Primary"
- android:buttonTint="#858383" />
+ style="@style/TextAppearance.NotificationGuts.Radio"
+ android:buttonTint="@color/notification_guts_buttons" />
<RadioButton
android:id="@+id/reset_importance"
android:layout_width="wrap_content"
android:layout_height="48dp"
- style="@style/TextAppearance.NotificationGuts.Primary"
- android:buttonTint="#858383" />
+ android:paddingStart="32dp"
+ style="@style/TextAppearance.NotificationGuts.Radio"
+ android:buttonTint="@color/notification_guts_buttons" />
</RadioGroup>
<!-- Importance slider -->
<LinearLayout
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 582ad48..7af247e 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -14,34 +14,14 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+
+<!-- Height is 0 because it will be managed by the QSContainer manually -->
<com.android.systemui.qs.customize.QSCustomizer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="0dp"
android:orientation="vertical"
android:background="@drawable/qs_customizer_background"
android:gravity="center_horizontal">
- <Toolbar
- android:id="@*android:id/action_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="28dp"
- android:navigationContentDescription="@*android:string/action_bar_up_description"
- style="?android:attr/toolbarStyle" />
-
- <android.support.v7.widget.RecyclerView
- android:id="@android:id/list"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:scrollIndicators="top"
- android:scrollbars="vertical" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="@dimen/navigation_bar_size"
- android:layout_gravity="bottom"
- android:background="#ff000000" />
-
</com.android.systemui.qs.customize.QSCustomizer>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
new file mode 100644
index 0000000..75f8fa4
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.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.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <Toolbar
+ android:id="@*android:id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="28dp"
+ android:navigationContentDescription="@*android:string/action_bar_up_description"
+ style="?android:attr/toolbarStyle" />
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@android:id/list"
+ android:layout_width="@dimen/notification_panel_width"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:scrollIndicators="top"
+ android:scrollbars="vertical" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/navigation_bar_size"
+ android:layout_gravity="bottom"
+ android:background="#ff000000" />
+</merge>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 994d3c9..ef15195 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -34,4 +34,7 @@
<include android:id="@+id/qs_detail" layout="@layout/qs_detail" />
+ <include android:id="@+id/qs_customize" layout="@layout/qs_customize_panel"
+ android:visibility="gone" />
+
</com.android.systemui.qs.QSContainer>
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index 94b099e..3a7c1d1 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -18,9 +18,10 @@
android:id="@+id/recents_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@drawable/recents_tv_background_gradient"
android:clipChildren="false"
- android:clipToPadding="false" >
-
+ android:clipToPadding="false"
+ android:layoutDirection="rtl">
<com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
android:id="@+id/task_list"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/recents_task_card_view.xml b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
similarity index 65%
rename from packages/SystemUI/res/layout/recents_task_card_view.xml
rename to packages/SystemUI/res/layout/recents_tv_task_card_view.xml
index fa1daad..c5b1a7a 100644
--- a/packages/SystemUI/res/layout/recents_task_card_view.xml
+++ b/packages/SystemUI/res/layout/recents_tv_task_card_view.xml
@@ -20,28 +20,28 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_gravity="center"
- android:layout_centerInParent="true">
+ android:layout_centerInParent="true"
+ android:layoutDirection="ltr">
- <RelativeLayout
+ <LinearLayout
android:layout_width="@dimen/recents_tv_card_width"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
- android:layout_gravity="center">
- <ImageView
- android:id="@+id/card_view_thumbnail"
- android:layout_width="match_parent"
- android:layout_height="@dimen/recents_tv_card_height"
- android:scaleType="centerCrop"
- android:gravity="center"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"/>
-
- <RelativeLayout
+ android:layout_gravity="center"
+ android:orientation="vertical" >
+ <LinearLayout
android:id="@+id/card_info_field"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/card_view_thumbnail"
- android:background="@color/recents_tv_card_background_color" >
+ android:layout_height="wrap_content">
+ <ImageView
+ android:id="@+id/card_extra_badge"
+ android:layout_width="@dimen/recents_tv_card_extra_badge_size"
+ android:layout_height="@dimen/recents_tv_card_extra_badge_size"
+ android:layout_marginBottom="@dimen/recents_tv_icon_padding_bottom"
+ android:layout_marginEnd="@dimen/recents_tv_icon_padding_end"
+ android:scaleType="fitCenter"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true" />
<TextView
android:id="@+id/card_title_text"
android:layout_width="match_parent"
@@ -49,29 +49,21 @@
android:layout_alignParentTop="false"
android:includeFontPadding="true"
android:minLines="1"
- android:maxLines="2"
+ android:maxLines="1"
android:textColor="@color/recents_tv_card_title_text_color"
- android:ellipsize="end" />
- <TextView
- android:id="@+id/card_content_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_below="@id/card_title_text"
- android:includeFontPadding="true"
- android:minLines="1"
- android:maxLines="2"
- android:textColor="@color/recents_tv_card_content_text_color"
- android:ellipsize="end" />
- <ImageView
- android:id="@+id/card_extra_badge"
- android:layout_width="@dimen/recents_tv_card_extra_badge_size"
- android:layout_height="@dimen/recents_tv_card_extra_badge_size"
- android:scaleType="fitCenter"
- android:background="@android:color/transparent"
- android:contentDescription="@null"
- android:layout_centerVertical="true"
- android:layout_alignParentRight="true"/>
- </RelativeLayout>
- </RelativeLayout>
+ android:fontFamily="@string/font_roboto_regular"
+ android:textSize="@dimen/recents_tv_title_text_size"
+ android:layout_marginBottom="@dimen/recents_tv_text_padding_bottom"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ <ImageView
+ android:id="@+id/card_view_thumbnail"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/recents_tv_card_height"
+ android:scaleType="centerCrop"
+ android:gravity="center"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@id/card_title_text" />
+ </LinearLayout>
</com.android.systemui.recents.tv.views.TaskCardView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 818df3b..75195c4 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -43,7 +43,7 @@
android:singleLine="true"
android:ellipsize="start"
android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
- android:imeOptions="actionSend" />
+ android:imeOptions="actionSend|flagNoExtractUi" />
<FrameLayout
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 656941f..ccefb5f 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -43,8 +43,10 @@
android:id="@+id/qs_density_container"
android:layout="@layout/qs_panel"
android:layout_width="@dimen/notification_panel_width"
- android:layout_height="wrap_content"
- android:layout_gravity="@integer/notification_panel_layout_gravity" />
+ android:layout_height="match_parent"
+ android:layout_gravity="@integer/notification_panel_layout_gravity"
+ android:clipToPadding="false"
+ android:clipChildren="false" />
<com.android.systemui.statusbar.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
diff --git a/packages/SystemUI/res/layout/tv_pip_onboarding.xml b/packages/SystemUI/res/layout/tv_pip_onboarding.xml
index f031bb4..b0814cf 100644
--- a/packages/SystemUI/res/layout/tv_pip_onboarding.xml
+++ b/packages/SystemUI/res/layout/tv_pip_onboarding.xml
@@ -37,10 +37,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
android:fontFamily="sans-serif"
android:textSize="16sp"
android:textColor="#EEEEEE"
android:lineSpacingMultiplier="1.28"
+ android:gravity="top|center_horizontal"
android:text="@string/pip_onboarding_description" />
<Button
android:id="@+id/close"
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
index ebd362c..1ba423b 100644
--- a/packages/SystemUI/res/layout/tv_pip_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml
@@ -26,10 +26,17 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
- android:padding="3dp"
- android:textSize="13sp"
- android:textColor="#111111"
- android:background="#99EEEEEE"
+ android:paddingTop="6dp"
+ android:paddingBottom="6dp"
+ android:paddingStart="10dp"
+ android:paddingEnd="10dp"
+ android:textSize="14sp"
+ android:textColor="#EEEEEE"
+ android:fontFamily="sans-serif"
+ android:background="@drawable/tv_pip_overlay_background"
+ android:lineSpacingMultiplier="1.465"
+ android:gravity="center"
+ android:maxLines="2"
android:text="@string/pip_hold_home" />
<LinearLayout
android:id="@+id/guide_buttons"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 30d9ed7..7d85ef6 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skermkiekie geneem."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Raak om jou skermkiekie te sien."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Kon nie skermkiekie neem nie."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Kon nie skermkiekie stoor nie."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kan weens beperkte bergingspasie nie skermkiekie stoor nie."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Die program of jou organisasie laat nie toe dat skermkiekies geneem word nie."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB-lêeroordrag-opsies"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Werkmodus is aan."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Werkmodus is afgeskakel."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Werkmodus is aangeskakel."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Skermhelderheid"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G-data is laat wag"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data is laat wag"</string>
diff --git a/packages/SystemUI/res/values-af/strings_tv.xml b/packages/SystemUI/res/values-af/strings_tv.xml
new file mode 100644
index 0000000..adc1306
--- /dev/null
+++ b/packages/SystemUI/res/values-af/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Maak PIP toe"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Volskerm"</string>
+ <string name="pip_play" msgid="674145557658227044">"Speel"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Laat wag"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Hou "<b>"TUIS"</b>" om PIP te beheer"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Druk en hou die TUIS-\nknoppie om PIP te beheer"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Het dit"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 9692ae8..e870ea7 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ቅጽበታዊ ገጽ እይታ ተቀርጿል"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"የእርስዎን ቅጽበታዊ ገጽ እይታ ለማየት ይንኩ"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ቅጽበታዊ ገጽ እይታ መቅረጽ አልተቻለም::"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ቅጽበታዊ ገጽ ዕይታን በማስቀመጥ ጊዜ ችግር አጋጥሟል።"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ባለው የተገደበ የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም።"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"ቅጽበታዊ ገጽ እይታዎችን ማንሳት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም።"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"የUSB ፋይል ሰደዳ አማራጮች"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"የሥራ ሁነታ በርቷል።"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"የሥራ ሁነታ ጠፍቷል።"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"የሥራ ሁነታ በርቷል።"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ብሩህነት ያሳዩ"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2ጂ-3ጂ ውሂብ ላፍታ ቆሟል"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4ጂ ውሂብ ላፍታ ቆሟል"</string>
diff --git a/packages/SystemUI/res/values-am/strings_tv.xml b/packages/SystemUI/res/values-am/strings_tv.xml
new file mode 100644
index 0000000..544ae07
--- /dev/null
+++ b/packages/SystemUI/res/values-am/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIPን ዝጋ"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"ሙሉ ማያ ገጽ"</string>
+ <string name="pip_play" msgid="674145557658227044">"አጫውት"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"ለአፍታ አቁም"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIPን ለመቆጣጠር "<b>"መነሻ"</b>"ን ይያዙ"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIPን ለመቆጣጠር የመነሻ\nአዝራሩን ይጫኑ እና ይያዙ"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"ገባኝ"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 181f68b..100bc63 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -77,6 +77,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"تم التقاط لقطة الشاشة."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"المس لعرض لقطة الشاشة."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"تعذر التقاط لقطة الشاشة."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"حدثت مشكلة أثناء حفظ لقطة الشاشة."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"يتعذر حفظ لقطة الشاشة نظرًا لأن مساحة التخزين المتاحة محدودة."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"غير مسموح بالتقاط لقطات شاشة نظرًا لإذن يتعلق بالتطبيق أو بالمؤسسة."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"خيارات نقل الملفات عبر USB"</string>
@@ -224,6 +225,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"وضع العمل قيد التشغيل."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"تم تعطيل وضع العمل."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"تم تشغيل وضع العمل."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"سطوع الشاشة"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"بيانات شبكات الجيل الثاني والثالث متوقفة مؤقتًا"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"تم إيقاف بيانات شبكة الجيل الرابع مؤقتًا"</string>
diff --git a/packages/SystemUI/res/values-ar/strings_tv.xml b/packages/SystemUI/res/values-ar/strings_tv.xml
new file mode 100644
index 0000000..99c413cf
--- /dev/null
+++ b/packages/SystemUI/res/values-ar/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"إغلاق PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"ملء الشاشة"</string>
+ <string name="pip_play" msgid="674145557658227044">"تشغيل"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"إيقاف مؤقت"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"اضغط "<b>"الرئيسية"</b>" للتحكم في PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"اضغط مع الاستمرار على زر الرئيسية\nللتحكم في PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"حسنًا"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index 9e88f35..7763cb3 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skrinşot çəkildi."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Skrinşotunuza baxmaq üçün toxunun"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Skrinşot götürülə bilinmədi."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Skrinşot yadda saxlanarkən problem baş verdi."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Yaddaş ehtiyatının az olması səbəbindən skrinşotu yadda saxlamaq olmur."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Tətbiq və ya təşkilatınız tərəfindən skrinşot çəkməyə icazə verilmir."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB fayl transferi seçimləri"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"İş rejimi aktivdir."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"İş rejimi sönülüdür."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"İş rejimi yanılıdır."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Display brightness"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G məlumatlarına fasilə verildi"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G məlumatlarına fasilə verildi"</string>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings_tv.xml b/packages/SystemUI/res/values-az-rAZ/strings_tv.xml
new file mode 100644
index 0000000..6751bc9
--- /dev/null
+++ b/packages/SystemUI/res/values-az-rAZ/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP bağlayın"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Tam ekran"</string>
+ <string name="pip_play" msgid="674145557658227044">"Göstərin"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Fasilə verin"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP idarı etmək üçün "<b>"Əsas səhifəni"</b>" tutub saxlayın"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP idarə etmək üçün Əsas səhifə\n düyməsini basıb saxlayın"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Anladım"</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 3edc805..1198950 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -74,6 +74,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Snimak ekrana je napravljen."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Dodirnite da biste videli snimak ekrana."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nije moguće napraviti snimak ekrana."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Čuvanje snimka ekrana nije uspelo zbog ograničenog memorijskog prostora."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Aplikacija ili organizacija ne dozvoljavaju pravljenje snimaka ekrana."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opcije USB prenosa datoteka"</string>
@@ -221,6 +223,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Režim rada je uključen."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Režim rada je isključen."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Režim rada je uključen."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Osvetljenost ekrana"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G podaci su pauzirani"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G podaci su pauzirani"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
new file mode 100644
index 0000000..39a858c
--- /dev/null
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Zatvori PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Ceo ekran"</string>
+ <string name="pip_play" msgid="674145557658227044">"Pusti"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pauziraj"</string>
+ <string name="pip_hold_home" msgid="340086535668778109"><b>"POČETNI EKRAN"</b>" kont. PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Pritisnite i zadržite dugme POČETNI EKRAN\n da biste kontrolisali PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Važi"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 84757b7..876af0a 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Екранната снимка е заснета."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Докоснете, за да видите екранната си снимка."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Екранната снимка не можа да бъде заснета."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"При запазването на екранната снимка възникна проблем."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Екранната снимка не може да се запази поради ограничено място в хранилището."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Правенето на екранни снимки не е разрешено от приложението или организацията ви."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Опции за пренос на файлове чрез USB"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Работният режим е включен."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Работният режим е изключен."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Работният режим е включен."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Яркост на екрана"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Данните от 2G – 3G са поставени на пауза"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Данните от 4G са поставени на пауза"</string>
diff --git a/packages/SystemUI/res/values-bg/strings_tv.xml b/packages/SystemUI/res/values-bg/strings_tv.xml
new file mode 100644
index 0000000..38c10ab
--- /dev/null
+++ b/packages/SystemUI/res/values-bg/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Затваряне на PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Цял екран"</string>
+ <string name="pip_play" msgid="674145557658227044">"Пускане"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Пауза"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Контр. на PIP: Задр. "<b>"HOME"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"За контролиране на PIP\nнатиснете и задръжте HOME"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Разбрах"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 18f37e63..eaf450f 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"স্ক্রীনশট নেওয়া হযেছে৷"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"আপনার স্ক্রীনশট দেখতে স্পর্শ করুন৷"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"স্ক্রীনশট নেওয়া যায়নি৷"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"স্ক্রীনশট সংরক্ষণের সময়ে সমস্যা হয়েছে৷"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"সঞ্চয়স্থান সীমিত থাকায় স্ক্রীনশটটি সংরক্ষণ করা যাবে না৷"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"অ্যাপ্লিকেশান বা আপনার প্রতিষ্ঠান স্ক্রীনশটগুলি নেওয়া অনুমতি দেয়নি৷"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ফাইল স্থানান্তরের বিকল্পগুলি"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"কাজের মোড চালু আছে"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"কাজের মোড বন্ধ আছে।"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"কাজের মোড চালু আছে"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"প্রদর্শনের উজ্জ্বলতা"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ডেটা বিরতি দেওয়া হয়েছে"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ডেটা বিরতি দেওয়া হয়েছে"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings_tv.xml b/packages/SystemUI/res/values-bn-rBD/strings_tv.xml
new file mode 100644
index 0000000..7a95815
--- /dev/null
+++ b/packages/SystemUI/res/values-bn-rBD/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP বন্ধ করুন"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"পূর্ণ স্ক্রীন"</string>
+ <string name="pip_play" msgid="674145557658227044">"চালান"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"বিরাম দিন"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP নিয়ন্ত্রণ করতে "<b>"হোম"</b>" কী ধরে রাখুন"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP নিয়ন্ত্রণ করতে হোম\nবোতামটি টিপে ধরে রাখুন"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"বুঝেছি"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml
index 5666c5f..3befd44 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings.xml
@@ -74,6 +74,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekran snimljen."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Dodirnite za prikaz snimka ekrana."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Došlo je do greške prilikom snimanja ekrana."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Snimak ekrana se ne može sačuvati zbog manjka prostora za pohranu."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Aplikacija ili vaša organizacija ne dopuštaju pravljenje snimaka ekrana."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opcije USB prijenosa fajlova"</string>
@@ -221,6 +223,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Poslovni režim uključen."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Poslovni režim je isključen."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Poslovni režim je uključen."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Osvjetljenje ekrana"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G prijenos podataka je pauzirano"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G prijenos podataka je pauzirano"</string>
@@ -456,11 +462,11 @@
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Želiti li uključiti Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Da povežete tastaturu sa tabletom, prvo morate uključiti Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Uključi"</string>
- <string name="show_silently" msgid="6841966539811264192">"Nečujno pokaži obavijesti"</string>
+ <string name="show_silently" msgid="6841966539811264192">"Nečujno prikaži obavijesti"</string>
<string name="block" msgid="2734508760962682611">"Blokiraj sva obavještenja"</string>
<string name="do_not_silence" msgid="6878060322594892441">"Nemoj utišati"</string>
<string name="do_not_silence_block" msgid="4070647971382232311">"Nemoj utišati ili blokirati"</string>
- <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Pokaži kompletne postavke za određivanje značaja"</string>
+ <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Prikaži kompletne postavke za određivanje značaja"</string>
<string name="blocked_importance" msgid="5198578988978234161">"Blokirano"</string>
<string name="min_importance" msgid="1901894910809414782">"Minimalni značaj"</string>
<string name="low_importance" msgid="4109929986107147930">"Mali značaj"</string>
@@ -468,11 +474,11 @@
<string name="high_importance" msgid="1527066195614050263">"Visok značaj"</string>
<string name="max_importance" msgid="5089005872719563894">"Hitan značaj"</string>
<string name="notification_importance_blocked" msgid="2397192642657872872">"Nikada ne prikazuj ova obavještenja"</string>
- <string name="notification_importance_min" msgid="1938190340516905748">"Nečujno pokaži na dnu spiska obavještenja"</string>
+ <string name="notification_importance_min" msgid="1938190340516905748">"Nečujno prikaži na dnu spiska obavještenja"</string>
<string name="notification_importance_low" msgid="3657252049508213048">"Nečujno prikaži ova obavještenja"</string>
<string name="notification_importance_default" msgid="4466466472622442175">"Dozvolite zvuk na ovim obavještenjima"</string>
- <string name="notification_importance_high" msgid="2135428926525093825">"Kratko prikaži na ekranu i dozvoli zvuk i dozvoli zvuk"</string>
- <string name="notification_importance_max" msgid="5806278962376556491">"Pokaži na vrhu liste obaveštenja, kratko prikaži na ekranu i dozvoli zvuk"</string>
+ <string name="notification_importance_high" msgid="2135428926525093825">"Kratko prikaži na ekranu i dozvoli zvuk"</string>
+ <string name="notification_importance_max" msgid="5806278962376556491">"Prikaži na vrhu liste obavještenja, kratko prikaži na ekranu i dozvoli zvuk"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string>
<string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Boja i izgled"</string>
diff --git a/packages/SystemUI/res/values-bs-rBA/strings_tv.xml b/packages/SystemUI/res/values-bs-rBA/strings_tv.xml
new file mode 100644
index 0000000..1719318
--- /dev/null
+++ b/packages/SystemUI/res/values-bs-rBA/strings_tv.xml
@@ -0,0 +1,32 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Zatvori PIP"</string>
+ <!-- no translation found for pip_fullscreen (8604643018538487816) -->
+ <skip />
+ <!-- no translation found for pip_play (674145557658227044) -->
+ <skip />
+ <!-- no translation found for pip_pause (8412075640017218862) -->
+ <skip />
+ <string name="pip_hold_home" msgid="340086535668778109">"Za kontr. PIP držite "<b>"HOME"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Za kontrolu PIP \n držite dugme POČETAK"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Jasno mi je"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 3747be0..42596fe 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"S\'ha fet una captura de pantalla."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toca per veure la captura de pantalla."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"No s\'ha pogut fer una captura de pantalla."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"La captura de pantalla no es pot desar perquè no hi ha prou espai d\'emmagatzematge."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"L\'aplicació o l\'organització no permeten fer captures de pantalla."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opcions transf. fitxers USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"El mode de feina està activat."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"S\'ha desactivat el mode de feina."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"S\'ha activat el mode de feina."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Brillantor de la pantalla"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Les dades 2G-3G estan aturades"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Les dades 4G estan aturades"</string>
@@ -459,7 +465,7 @@
<string name="block" msgid="2734508760962682611">"Bloqueja totes les notificacions"</string>
<string name="do_not_silence" msgid="6878060322594892441">"No silenciïs"</string>
<string name="do_not_silence_block" msgid="4070647971382232311">"No silenciïs ni bloquegis"</string>
- <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Mostra la configuració de la importància completa"</string>
+ <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Mostra la configuració completa per a la importància"</string>
<string name="blocked_importance" msgid="5198578988978234161">"Bloquejades"</string>
<string name="min_importance" msgid="1901894910809414782">"Importància mínima"</string>
<string name="low_importance" msgid="4109929986107147930">"Importància baixa"</string>
diff --git a/packages/SystemUI/res/values-ca/strings_tv.xml b/packages/SystemUI/res/values-ca/strings_tv.xml
new file mode 100644
index 0000000..1aed2b4
--- /dev/null
+++ b/packages/SystemUI/res/values-ca/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Tanca PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Pantalla completa"</string>
+ <string name="pip_play" msgid="674145557658227044">"Reprodueix"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Posa en pausa"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Prem "<b>"INICI"</b>" per contr. PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Mantén premut botó INICI\n per controlar PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"D\'acord"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 48c9c55..3867673 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -75,6 +75,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Snímek obrazovky Snímek obrazovky pořízen."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Snímek obrazovky zobrazíte dotykem."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Snímek obrazovky se nepodařilo zachytit."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Snímek obrazovky nelze pořídit kvůli nedostatku místa v úložišti."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Aplikace nebo organizace zakazuje pořizování snímků obrazovky."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Možnosti přenosu souborů pomocí rozhraní USB"</string>
@@ -222,6 +224,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Pracovní režim zapnutý"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Pracovní režim je vypnutý."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Pracovní režim je zapnutý."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Jas displeje"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data 2G a 3G jsou pozastavena"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Data 4G jsou pozastavena"</string>
diff --git a/packages/SystemUI/res/values-cs/strings_tv.xml b/packages/SystemUI/res/values-cs/strings_tv.xml
new file mode 100644
index 0000000..2480c37
--- /dev/null
+++ b/packages/SystemUI/res/values-cs/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Ukončit PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Celá obrazovka"</string>
+ <string name="pip_play" msgid="674145557658227044">"Přehrát"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pozastavit"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Funkci PIP lze ovládat podržením tlačítka "<b>"PLOCHA"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Funkci PIP lze ovládat\npodržením tlačítka PLOCHA"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Rozumím"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 50d01ea..0356a18 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skærmbilledet er gemt."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Tryk for at se dit skærmbillede."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Skærmbilledet kunne ikke tages."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Skærmbilledet kan ikke gemmes pga. begrænset lagerplads."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Appen eller din organisation tillader ikke, at du tager skærmbilleder."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Muligheder for USB-filoverførsel"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Arbejdstilstand er slået til."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Arbejdstilstand er slået fra."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Arbejdstilstand er slået til."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Skærmens lysstyrke"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G-data er sat på pause"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data er sat på pause"</string>
diff --git a/packages/SystemUI/res/values-da/strings_tv.xml b/packages/SystemUI/res/values-da/strings_tv.xml
new file mode 100644
index 0000000..10a1ecd
--- /dev/null
+++ b/packages/SystemUI/res/values-da/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Luk PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Fuld skærm"</string>
+ <string name="pip_play" msgid="674145557658227044">"Afspil"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pause"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Hold "<b>"HOME"</b>" nede for at styre PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Tryk på knappen HOME,\nog hold den nede for at styre PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index fd8cf89..7e7b2ac 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot aufgenommen"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Zum Ansehen berühren"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot konnte nicht aufgenommen werden."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Die App oder Ihr Unternehmen lässt das Erstellen von Screenshots nicht zu."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB-Dateiübertragungsoptionen"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Arbeitsmodus an."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Arbeitsmodus deaktiviert."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Arbeitsmodus aktiviert."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Helligkeit des Displays"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-/3G-Daten pausiert"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-Daten pausiert"</string>
diff --git a/packages/SystemUI/res/values-de/strings_tv.xml b/packages/SystemUI/res/values-de/strings_tv.xml
new file mode 100644
index 0000000..aa1a9b2
--- /dev/null
+++ b/packages/SystemUI/res/values-de/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP schließen"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Vollbild"</string>
+ <string name="pip_play" msgid="674145557658227044">"Wiedergeben"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pausieren"</string>
+ <string name="pip_hold_home" msgid="340086535668778109"><b>"STARTBILDSCHIRMTASTE"</b>" drücken, um PIP zu steuern"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"STARTBILDSCHIRMTASTE\n gedrückt halten, um PIP zu steuern"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index b5f55d9..943c67d 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Λήφθηκε το στιγμιότυπο οθόνης ."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Αγγίξτε για να δείτε το στιγμιότυπο οθόνης σας"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Αδύνατη η αποθήκευση του στιγμιότυπου οθόνης."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Παρουσιάστηκε πρόβλημα κατά την αποθήκευση του στιγμιότυπου οθόνης."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Δεν είναι δυνατή η αποθήκευση του στιγμιότυπου οθόνης λόγω περιορισμένου χώρου αποθήκευσης."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Η λήψη στιγμιοτύπων οθόνης δεν επιτρέπεται από την εφαρμογή ή από τον οργανισμό σας."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Επιλογές μεταφοράς αρχείων μέσω USB"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Η λειτουργία εργασίας είναι ενεργή."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Η λειτουργία εργασίας απενεργοποιήθηκε."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Η λειτουργία εργασίας ενεργοποιήθηκε."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Φωτεινότητα οθόνης"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Τα δεδομένα 2G-3G τέθηκαν σε παύση"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Τα δεδομένα 4G τέθηκαν σε παύση"</string>
diff --git a/packages/SystemUI/res/values-el/strings_tv.xml b/packages/SystemUI/res/values-el/strings_tv.xml
new file mode 100644
index 0000000..c96f852
--- /dev/null
+++ b/packages/SystemUI/res/values-el/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Κλείσιμο PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Πλήρης οθόνη"</string>
+ <string name="pip_play" msgid="674145557658227044">"Αναπαραγωγή"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Παύση"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Κρατήστε το πλήκτρο "<b>"HOME"</b>" πατημένο για έλεγχο του PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Πατήστε παρατεταμένα το κουμπί HOME\nγια έλεγχο του PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Κατάλαβα"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index cbb1ecf..6367ed9 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Touch to view your screenshot."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Taking screenshots is not allowed by the app or your organisation."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
@@ -220,6 +221,8 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Work mode on."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Work mode turned off."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work mode turned on."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver turned off."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver turned on."</string>
<string name="accessibility_brightness" msgid="8003681285547803095">"Display brightness"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G data is paused"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data is paused"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings_tv.xml b/packages/SystemUI/res/values-en-rAU/strings_tv.xml
new file mode 100644
index 0000000..6f33655
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rAU/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Close PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Full screen"</string>
+ <string name="pip_play" msgid="674145557658227044">"Play"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pause"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Hold "<b>"HOME"</b>" to control PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Press and hold the HOME\nbutton to control PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Understood"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index cbb1ecf..6367ed9 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Touch to view your screenshot."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Taking screenshots is not allowed by the app or your organisation."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
@@ -220,6 +221,8 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Work mode on."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Work mode turned off."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work mode turned on."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver turned off."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver turned on."</string>
<string name="accessibility_brightness" msgid="8003681285547803095">"Display brightness"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G data is paused"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data is paused"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings_tv.xml b/packages/SystemUI/res/values-en-rGB/strings_tv.xml
new file mode 100644
index 0000000..6f33655
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rGB/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Close PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Full screen"</string>
+ <string name="pip_play" msgid="674145557658227044">"Play"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pause"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Hold "<b>"HOME"</b>" to control PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Press and hold the HOME\nbutton to control PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Understood"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index cbb1ecf..6367ed9 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot captured."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Touch to view your screenshot."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Couldn\'t capture screenshot."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problem encountered while saving screenshot."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Can\'t save screenshot due to limited storage space."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Taking screenshots is not allowed by the app or your organisation."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
@@ -220,6 +221,8 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Work mode on."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Work mode turned off."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work mode turned on."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Data Saver turned off."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Data Saver turned on."</string>
<string name="accessibility_brightness" msgid="8003681285547803095">"Display brightness"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G data is paused"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data is paused"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings_tv.xml b/packages/SystemUI/res/values-en-rIN/strings_tv.xml
new file mode 100644
index 0000000..6f33655
--- /dev/null
+++ b/packages/SystemUI/res/values-en-rIN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Close PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Full screen"</string>
+ <string name="pip_play" msgid="674145557658227044">"Play"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pause"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Hold "<b>"HOME"</b>" to control PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Press and hold the HOME\nbutton to control PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Understood"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 856c1c1..e60fd2e 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Se guardó la captura de pantalla."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toca para ver tu captura de pantalla."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"No se pudo guardar la captura de pantalla."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"No se puede guardar la captura de pantalla debido al almacenamiento limitado."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"La app o tu organización no permiten las capturas de pantalla."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opciones de transferencia de archivos por USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Modo de trabajo activado"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Se desactivó el modo de trabajo."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Se activó el modo de trabajo."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Brillo de pantalla"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Datos 2G-3G pausados"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Datos 4G pausados"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings_tv.xml b/packages/SystemUI/res/values-es-rUS/strings_tv.xml
new file mode 100644
index 0000000..1df219d
--- /dev/null
+++ b/packages/SystemUI/res/values-es-rUS/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Cerrar PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Pantalla completa"</string>
+ <string name="pip_play" msgid="674145557658227044">"Reproducir"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pausar"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Mantén presionado "<b>"INICIO"</b>" para controlar PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Mantén presionado botón\nINICIO para controlar PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Entendido"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index b3eda73..e387019 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captura guardada"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toca para ver la captura de pantalla"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"No se ha podido guardar la captura de pantalla."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"No se puede guardar la captura de pantalla porque no hay espacio de almacenamiento suficiente."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"La aplicación o tu organización no permiten que se realicen capturas de pantalla."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opciones de transferencia de archivos por USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Modo de trabajo activado."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Modo de trabajo desactivado."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modo de trabajo activado."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Brillo de la pantalla"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Datos 2G-3G pausados"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Datos 4G pausados"</string>
@@ -303,8 +309,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fijación de pantalla"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No se ha podido iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
- <skip />
+ <string name="recents_launch_disabled_message" msgid="1624523193008871793">"La aplicación <xliff:g id="APP">%s</xliff:g> se ha inhabilitado en modo seguro."</string>
<string name="recents_history_button_label" msgid="5153358867807604821">"Historial"</string>
<string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Borrar"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Esta aplicación no admite el modo multiventana."</string>
diff --git a/packages/SystemUI/res/values-es/strings_tv.xml b/packages/SystemUI/res/values-es/strings_tv.xml
new file mode 100644
index 0000000..fcc839e
--- /dev/null
+++ b/packages/SystemUI/res/values-es/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Cerrar PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Pantalla completa"</string>
+ <string name="pip_play" msgid="674145557658227044">"Reproducir"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pausar"</string>
+ <string name="pip_hold_home" msgid="340086535668778109"><b>"INICIO"</b>" pulsado: control PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"INICIO pulsado:\ncontrol PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Entendido"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 1279a2c..7e68757 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekraanipilt on jäädvustatud."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Puudutage kuvatõmmise vaatamiseks."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Kuvatõmmist ei saanud jäädvustada."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Piiratud salvestusruumi tõttu ei saa ekraanipilti salvestada."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Rakendus või teie organisatsioon ei luba ekraanipilte jäädvustada."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB-failiedastuse valikud"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Töörežiim on sees."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Töörežiim on välja lülitatud."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Töörežiim on sisse lülitatud."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Ekraani heledus"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G andmekasutus on peatatud"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G andmekasutus on peatatud"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings_tv.xml b/packages/SystemUI/res/values-et-rEE/strings_tv.xml
new file mode 100644
index 0000000..ae6cb30
--- /dev/null
+++ b/packages/SystemUI/res/values-et-rEE/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Sule PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Täisekraan"</string>
+ <string name="pip_play" msgid="674145557658227044">"Esita"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Peata"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP juht. hoidke all nuppu "<b>"AVAEKRAAN"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP juhtim. vajutage\npikalt nuppu AVAEKRAAN"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Selge"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 3f8e844..6a358d4 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Pantaila-argazkia atera da."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Pantaila-argazkia ikusteko, ukitu ezazu."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Ezin izan da pantaila-argazkia atera."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Ezin da atera pantaila-argazkia ez delako tokirik geratzen."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Aplikazioak edo erakundeak ez du onartzen pantaila-argazkiak ateratzea."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB fitxategiak transferitzeko aukerak"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Aktibatuta dago lan modua."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Desaktibatuta dago lan modua."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Aktibatuta dago lan modua."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Bistaratu distira"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G datuen erabilera eten da"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G datuen erabilera eten da"</string>
@@ -459,7 +465,7 @@
<string name="block" msgid="2734508760962682611">"Blokeatu jakinarazpen guztiak"</string>
<string name="do_not_silence" msgid="6878060322594892441">"Ez isilarazi"</string>
<string name="do_not_silence_block" msgid="4070647971382232311">"Ez isilarazi edo blokeatu"</string>
- <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Erakutsi garrantzia handiko jakinarazpenen ezarpenak"</string>
+ <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Erakutsi garrantzi handiko jakinarazpenen ezarpenak"</string>
<string name="blocked_importance" msgid="5198578988978234161">"Blokeatuta"</string>
<string name="min_importance" msgid="1901894910809414782">"Gutxieneko garrantzia"</string>
<string name="low_importance" msgid="4109929986107147930">"Garrantzi txikia"</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings_tv.xml b/packages/SystemUI/res/values-eu-rES/strings_tv.xml
new file mode 100644
index 0000000..80feff6
--- /dev/null
+++ b/packages/SystemUI/res/values-eu-rES/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Itxi PIPa"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Pantaila osoa"</string>
+ <string name="pip_play" msgid="674145557658227044">"Erreproduzitu"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pausatu"</string>
+ <string name="pip_hold_home" msgid="340086535668778109"><b>"HASIERA"</b>" PIP kontrolatzeko"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Eduki sakatuta HASIERA\nPIPa kontrolatzeko"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Ados"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index cc32518..2477a12 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"عکس صفحهنمایش گرفته شد."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"برای مشاهده عکس صفحهنمایشتان، لمس کنید."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"عکس صفحهنمایش گرفته نشد."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"به دلیل محدود بودن فضای ذخیرهسازی نمیتوانید عکس صفحهنمایش را ذخیره کنید."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"این برنامه یا سازمان شما اجازه نمیدهند عکس صفحهنمایش بگیرید."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"گزینههای انتقال فایل USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"حالت کار روشن."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"حالت کار خاموش شد."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"حالت کار روشن شد."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"روشنایی نمایشگر"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"داده 2G-3G موقتاً متوقف شده است"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"داده 4G موقتاً متوقف شده است"</string>
@@ -506,9 +512,9 @@
<string name="headset" msgid="4534219457597457353">"هدست"</string>
<string name="accessibility_status_bar_headphones" msgid="9156307120060559989">"هدفون وصل شد"</string>
<string name="accessibility_status_bar_headset" msgid="8666419213072449202">"هدست وصل شد"</string>
- <string name="data_saver" msgid="5037565123367048522">"صرفهجویی در مصرف داده"</string>
- <string name="accessibility_data_saver_on" msgid="8454111686783887148">"صرفهجویی در مصرف داده روشن است"</string>
- <string name="accessibility_data_saver_off" msgid="8841582529453005337">"صرفهجویی در مصرف داده خاموش است"</string>
+ <string name="data_saver" msgid="5037565123367048522">"صرفهجویی داده"</string>
+ <string name="accessibility_data_saver_on" msgid="8454111686783887148">"صرفهجویی داده روشن است"</string>
+ <string name="accessibility_data_saver_off" msgid="8841582529453005337">"صرفهجویی داده خاموش است"</string>
<string name="switch_bar_on" msgid="1142437840752794229">"روشن"</string>
<string name="switch_bar_off" msgid="8803270596930432874">"خاموش"</string>
<string name="nav_bar" msgid="1993221402773877607">"نوار پیمایش"</string>
diff --git a/packages/SystemUI/res/values-fa/strings_tv.xml b/packages/SystemUI/res/values-fa/strings_tv.xml
new file mode 100644
index 0000000..5130b50
--- /dev/null
+++ b/packages/SystemUI/res/values-fa/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"بستن PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"تمام صفحه"</string>
+ <string name="pip_play" msgid="674145557658227044">"پخش"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"مکث"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"کنترل PIP با نگهداشتن "<b>"HOME"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"برای کنترل PIP دکمه صفحه\nاصلی را فشار دهید و نگهدارید"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"متوجه شدم"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 8b3fb5e..6199536 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Kuvakaappaus tallennettu"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Katso kuvakaappaus koskettamalla."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Kuvakaappausta ei voitu tallentaa"</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kuvakaappauksen tallentaminen epäonnistui, sillä tallennustilaa ei ole riittävästi."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Sovellus tai organisaatiosi ei salli kuvakaappauksien tallentamista."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB-tiedostonsiirtoasetukset"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Työtila on käytössä."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Työtila poistettiin käytöstä."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Työtila otettiin käyttöön."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Näytön kirkkaus"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G-tiedonsiirto keskeytettiin"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-tiedonsiirto keskeytettiin"</string>
diff --git a/packages/SystemUI/res/values-fi/strings_tv.xml b/packages/SystemUI/res/values-fi/strings_tv.xml
new file mode 100644
index 0000000..a42c231
--- /dev/null
+++ b/packages/SystemUI/res/values-fi/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Sulje PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Koko näyttö"</string>
+ <string name="pip_play" msgid="674145557658227044">"Toista"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Keskeytä"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP: paina pitkään "<b>"aloituspain"</b>"."</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Hallinnoi PIP:tä: paina\naloitusnäyttöpainiketta pitkään."</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Selvä"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index e0beeca..6ed42ed 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Capture d\'écran réussie"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Appuyez pour afficher votre capture d\'écran."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Impossible de réaliser une capture d\'écran"</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossible d\'enregistrer la saisie d\'écran, car l\'espace de stockage est limité."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"L\'application ou votre organisation n\'autorise pas les saisies d\'écran."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Options transfert fichiers USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Mode Travail activé."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Le mode Travail est désactivé."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Le mode Travail est activé."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Luminosité de l\'écran"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Données 2G/3G désactivées"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Données 4G désactivées"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings_tv.xml b/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
new file mode 100644
index 0000000..29e94c6
--- /dev/null
+++ b/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Fermer mode IDI"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Plein écran"</string>
+ <string name="pip_play" msgid="674145557658227044">"Lecture"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Interrompre"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Maint. enf. "<b>"ACC."</b>" pr gér. mode IDI"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Maint. enf. \nACCUEIL pr gér. mode IDI"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 9ef6392..c08958d 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Capture d\'écran réussie"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Appuyez pour afficher votre capture d\'écran."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Impossible de réaliser une capture d\'écran"</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Les captures d\'écran ne sont pas autorisées par l\'application ou par votre organisation."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Options transfert fichiers USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Mode Travail activé"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Le mode Travail est désactivé."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Le mode Travail est activé."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Luminosité de l\'affichage"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Données 2G-3G désactivées"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Données 4G désactivées"</string>
diff --git a/packages/SystemUI/res/values-fr/strings_tv.xml b/packages/SystemUI/res/values-fr/strings_tv.xml
new file mode 100644
index 0000000..8504a8e
--- /dev/null
+++ b/packages/SystemUI/res/values-fr/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Fermer mode PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Plein écran"</string>
+ <string name="pip_play" msgid="674145557658227044">"Lire"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Suspendre"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Appui long "<b>"ACCUEIL"</b>" pour contrôler PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Appui long bouton ACCUEIL\npour contrôler PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index bb85708..7cb3cb1 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de pantalla gardada."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toca para ver a captura de pantalla."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Non se puido facer a captura de pantalla."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Non se pode gardar a captura de pantalla porque o espazo de almacenamento é limitado."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"A aplicación ou a túa organización non permite realizar capturas de pantalla."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opcións de transferencia USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Modo de traballo activado."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Desactivouse o modo de traballo."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Activouse o modo de traballo."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Brillo de pantalla"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Os datos 2G-3G están en pausa"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Os datos 4G están en pausa"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings_tv.xml b/packages/SystemUI/res/values-gl-rES/strings_tv.xml
new file mode 100644
index 0000000..6aab613
--- /dev/null
+++ b/packages/SystemUI/res/values-gl-rES/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Pechar PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Pantalla completa"</string>
+ <string name="pip_play" msgid="674145557658227044">"Reproducir"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pausar"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Premido "<b>"INICIO"</b>" para PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Premido INICIO\npara PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"De acordo"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 075f023..a8394a2 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"સ્ક્રીનશોટ કેપ્ચર કર્યો."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"તમારો સ્ક્રીનશોટ જોવા માટે ટચ કરો."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"સ્ક્રીનશોટ કેપ્ચર કરી શકાયો નથી."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"સ્ક્રીનશૉટ સાચવવામાં સમયા આવી."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"મર્યાદિત સંગ્રહ સ્થાનને કારણે સ્ક્રીનશોટ સાચવી શકાતો નથી."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"ઍપ્લિકેશન કે તમારી સંસ્થા દ્વારા સ્ક્રીનશોટ્સ લેવાની મંજૂરી નથી."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ફાઇલ ટ્રાન્સફર વિકલ્પો"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"કાર્ય મોડ ચાલુ."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"કાર્ય મોડ બંધ કર્યો."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"કાર્ય મોડ ચાલુ કર્યો."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"પ્રદર્શન તેજ"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ડેટા થોભાવ્યો છે"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ડેટા થોભાવ્યો છે"</string>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings_tv.xml b/packages/SystemUI/res/values-gu-rIN/strings_tv.xml
new file mode 100644
index 0000000..56f0298
--- /dev/null
+++ b/packages/SystemUI/res/values-gu-rIN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP બંધ કરો"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"પૂર્ણ સ્ક્રીન"</string>
+ <string name="pip_play" msgid="674145557658227044">"ચલાવો"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"થોભાવો"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP નિયંત્રિત કરવા માટે "<b>"હોમ"</b>" પકડી રાખો"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP નિયંત્રિત કરવા માટે હોમ\n બટન દબાવો અને પકડી રાખો"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"સમજાઈ ગયું"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 91a5d69..7618c02 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रीनशॉट कैप्चर किया गया."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"अपना स्क्रीनशॉट देखने के लिए स्पर्श करें."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रीनशॉट को कैप्चर नहीं किया जा सका."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"सीमित मेमोरी स्थान के कारण स्क्रीनशॉट सहेजा नहीं जा सकता."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"आपके ऐप्लिकेशन या आपके संगठन द्वारा स्क्रीनशॉट लेने की अनुमति नहीं है."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB फ़ाइल स्थानांतरण विकल्प"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"कार्य मोड चालू है."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"कार्य मोड बंद कर दिया गया."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"कार्य मोड चालू किया गया."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"स्क्रीन की स्क्रीन की रोशनी"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G डेटा रोक दिया गया है"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G डेटा रोक दिया गया है"</string>
diff --git a/packages/SystemUI/res/values-hi/strings_tv.xml b/packages/SystemUI/res/values-hi/strings_tv.xml
new file mode 100644
index 0000000..c4bb26b
--- /dev/null
+++ b/packages/SystemUI/res/values-hi/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP बंद करें"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"पूर्ण स्क्रीन"</string>
+ <string name="pip_play" msgid="674145557658227044">"चलाएं"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"रोकें"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP नियंत्रण हेतु "<b>"HOME"</b>" होल्ड करें"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP नियंत्रण हेतु HOME\nबटन दबाए रखें"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"समझ लिया"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index eaa777b..715f667 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -74,6 +74,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Zaslon je snimljen."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Dodirnite za prikaz snimke zaslona."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nije bilo moguće snimiti zaslon."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Prilikom spremanja snimke zaslona pojavio se problem."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Zaslon nije snimljen zbog ograničenog prostora za pohranu."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Snimanje zaslona ne dopušta aplikacija ili vaša organizacija."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opcije USB prijenosa datoteka"</string>
@@ -221,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Način rada uključen."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Način rada isključen."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Način rada uključen."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Svjetlina zaslona"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G – 3G podaci pauzirani"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G podaci pauzirani"</string>
@@ -304,8 +309,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"prikvačivanje zaslona"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
- <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
- <skip />
+ <string name="recents_launch_disabled_message" msgid="1624523193008871793">"Aplikacija <xliff:g id="APP">%s</xliff:g> onemogućena je u sigurnom načinu."</string>
<string name="recents_history_button_label" msgid="5153358867807604821">"Povijest"</string>
<string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Izbriši"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ta aplikacija ne podržava više prozora"</string>
diff --git a/packages/SystemUI/res/values-hr/strings_tv.xml b/packages/SystemUI/res/values-hr/strings_tv.xml
new file mode 100644
index 0000000..2a72055
--- /dev/null
+++ b/packages/SystemUI/res/values-hr/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Zatvori PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Cijeli zaslon"</string>
+ <string name="pip_play" msgid="674145557658227044">"Reproduciraj"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pauziraj"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Držite "<b>"POČETNI"</b>" za PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Pritisnite i držite POČETNI\nza upravljanje PIP-om"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Shvaćam"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 8e55c78..280cdb6 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Képernyőkép rögzítve."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Megérintésével megtekintheti a képernyőképet."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nem sikerült rögzíteni a képernyőképet."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Hiba történt a képernyőkép mentése során."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Nem menthet képernyőképet, mert kevés a tárhely."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Az alkalmazás vagy szervezete nem engedélyezi képernyőképek készítését."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB-fájlátvitel beállításai"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Munka mód be."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Munka mód kikapcsolva."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Munka mód bekapcsolva."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"A kijelző fényereje"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"A 2G és 3G adatforgalom szünetel."</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"A 4G adatforgalom szünetel"</string>
diff --git a/packages/SystemUI/res/values-hu/strings_tv.xml b/packages/SystemUI/res/values-hu/strings_tv.xml
new file mode 100644
index 0000000..ff4d37c
--- /dev/null
+++ b/packages/SystemUI/res/values-hu/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP bezárása"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Teljes képernyő"</string>
+ <string name="pip_play" msgid="674145557658227044">"Lejátszás"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Szüneteltetés"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP vezérlése a "<b>"HOME"</b>"-mal"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"A PIP vezérléséhez\ntartsa nyomva a HOME-ot"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Rendben"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 09e7f70b..bf9f80d 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Էկրանի հանույթը լուսանկարվել է:"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Հպեք ձեր էկրանի հանույթը տեսնելու համար:"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Չհաջողվեց լուսանկարել էկրանի հանույթը:"</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Չհաջողվեց պահել էկրանի պատկերը սահմանափակ հիշողության պատճառով:"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Այս հավելվածը կամ ձեր կազմակերպությունը չի թույլատրում Էկրանի պատկերի ստացումը:"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ֆայլերի փոխանցման ընտրանքներ"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Աշխատանքային ռեժիմը միացված է:"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Աշխատանքային ռեժիմն անջատվեց:"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Աշխատանքային ռեժիմը միացվեց:"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Ցուցադրել պայծառությունը"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2Գ-3Գ տվյալների օգտագործումը դադարեցված է"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4Գ տվյալների օգտագործումը դադարեցված է"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings_tv.xml b/packages/SystemUI/res/values-hy-rAM/strings_tv.xml
new file mode 100644
index 0000000..0ddb715
--- /dev/null
+++ b/packages/SystemUI/res/values-hy-rAM/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Փակել PIP-ն"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Լիէկրան"</string>
+ <string name="pip_play" msgid="674145557658227044">"Նվագարկել"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Դադարեցնել"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP-ն կառավարելու համար սեղմած պահեք "<b>"HOME"</b>" կոճակը"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP-ն կառավարելու համար սեղմեք և պահեք HOME\n կոճակը"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Պարզ է"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 46340a7..7723663 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Tangkapan layar diambil."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Sentuh untuk melihat tangkapan layar Anda."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Tidak dapat mengambil tangkapan layar."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Terjadi masalah saat menyimpan tangkapan layar."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Tidak dapat menyimpan tangkapan layar karena ruang penyimpanan terbatas."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Mengambil tangkapan layar tidak diizinkan oleh aplikasi atau organisasi."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opsi transfer file USB"</string>
@@ -220,6 +221,8 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Mode kerja aktif."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Mode kerja dinonaktifkan."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Mode kerja diaktifkan."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Penghemat Data nonaktif."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Penghemat Data diaktifkan."</string>
<string name="accessibility_brightness" msgid="8003681285547803095">"Kecerahan tampilan"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data 2G-3G dijeda"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Data 4G dijeda"</string>
diff --git a/packages/SystemUI/res/values-in/strings_tv.xml b/packages/SystemUI/res/values-in/strings_tv.xml
new file mode 100644
index 0000000..f631513
--- /dev/null
+++ b/packages/SystemUI/res/values-in/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Tutup PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Layar penuh"</string>
+ <string name="pip_play" msgid="674145557658227044">"Putar"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Jeda"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Tahan "<b>"LAYAR UTAMA"</b>" untuk mengontrol PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Tekan dan tahan tombol LAYAR UTAMA\nuntuk mengontrol PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Mengerti"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 89708de..6b4f191 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skjámynd var tekin."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Snertu til að skoða skjámyndina."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Ekki tókst að taka skjámynd."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Ekki tókst að vista skjámynd vegna takmarkaðs geymslupláss."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Forritið eða fyrirtækið þitt leyfir ekki að teknar séu skjámyndir."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Valkostir USB-skráaflutnings"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Kveikt á vinnustillingu."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Slökkt á vinnustillingu."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Kveikt á vinnustillingu."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Birtustig skjás"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Slökkt er á 2G- og 3G-gögnum"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Slökkt er á 4G-gögnum"</string>
diff --git a/packages/SystemUI/res/values-is-rIS/strings_tv.xml b/packages/SystemUI/res/values-is-rIS/strings_tv.xml
new file mode 100644
index 0000000..b44aaf0
--- /dev/null
+++ b/packages/SystemUI/res/values-is-rIS/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Loka mynd í mynd"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Allur skjárinn"</string>
+ <string name="pip_play" msgid="674145557658227044">"Spila"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Hlé"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Haltu "<b>"HOME"</b>"-hnappinum inni til að stjórna innfelldri mynd"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Haltu HOME\n-hnappinum inni til að stjórna innfelldri mynd"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Ég skil"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index d6622e3..6fc814a 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot acquisito."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Tocca per visualizzare il tuo screenshot."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Impossibile acquisire lo screenshot."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"L\'acquisizione di screenshot non è consentita dall\'app o dall\'organizzazione."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opzioni trasferimento file USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Modalità Lavoro attiva."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Modalità Lavoro disattivata."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modalità Lavoro attivata."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Luminosità dello schermo"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Dati 2G-3G sospesi"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Dati 4G sospesi"</string>
@@ -455,23 +461,23 @@
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Attivare il Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Per connettere la tastiera al tablet, devi prima attivare il Bluetooth."</string>
<string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Attiva"</string>
- <string name="show_silently" msgid="6841966539811264192">"Mostra silenziosamente le notifiche"</string>
+ <string name="show_silently" msgid="6841966539811264192">"Mostra le notifiche silenziosamente"</string>
<string name="block" msgid="2734508760962682611">"Blocca tutte le notifiche"</string>
- <string name="do_not_silence" msgid="6878060322594892441">"Non disattivare i suoni"</string>
- <string name="do_not_silence_block" msgid="4070647971382232311">"Non disattivare i suoni e non bloccare"</string>
- <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Mostra impostazioni importanza complete"</string>
+ <string name="do_not_silence" msgid="6878060322594892441">"Non silenziare"</string>
+ <string name="do_not_silence_block" msgid="4070647971382232311">"Non silenziare e non bloccare"</string>
+ <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Mostra impostazioni di importanza complete"</string>
<string name="blocked_importance" msgid="5198578988978234161">"Bloccata"</string>
- <string name="min_importance" msgid="1901894910809414782">"Importanza min"</string>
+ <string name="min_importance" msgid="1901894910809414782">"Importanza minima"</string>
<string name="low_importance" msgid="4109929986107147930">"Importanza scarsa"</string>
<string name="default_importance" msgid="8192107689995742653">"Importanza normale"</string>
<string name="high_importance" msgid="1527066195614050263">"Importanza elevata"</string>
<string name="max_importance" msgid="5089005872719563894">"Importanza urgente"</string>
<string name="notification_importance_blocked" msgid="2397192642657872872">"Non mostrare mai queste notifiche"</string>
- <string name="notification_importance_min" msgid="1938190340516905748">"Mostra silenziosamente nella parte inferiore dell\'elenco delle notifiche"</string>
- <string name="notification_importance_low" msgid="3657252049508213048">"Mostra silenziosamente queste notifiche"</string>
- <string name="notification_importance_default" msgid="4466466472622442175">"Attiva i suoni per queste notifiche"</string>
- <string name="notification_importance_high" msgid="2135428926525093825">"Apri sullo schermo e attiva l\'audio"</string>
- <string name="notification_importance_max" msgid="5806278962376556491">"Mostra in cima all\'elenco delle notifiche, apri sullo schermo e attiva l\'audio"</string>
+ <string name="notification_importance_min" msgid="1938190340516905748">"Mostra silenziosamente in fondo all\'elenco delle notifiche"</string>
+ <string name="notification_importance_low" msgid="3657252049508213048">"Mostra queste notifiche silenziosamente"</string>
+ <string name="notification_importance_default" msgid="4466466472622442175">"Attiva l\'audio per queste notifiche"</string>
+ <string name="notification_importance_high" msgid="2135428926525093825">"Visualizza sullo schermo e attiva l\'audio"</string>
+ <string name="notification_importance_max" msgid="5806278962376556491">"Mostra in cima all\'elenco delle notifiche, visualizza sullo schermo e attiva l\'audio"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Altre impostazioni"</string>
<string name="notification_done" msgid="5279426047273930175">"Fine"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"Colore e aspetto"</string>
diff --git a/packages/SystemUI/res/values-it/strings_tv.xml b/packages/SystemUI/res/values-it/strings_tv.xml
new file mode 100644
index 0000000..08f2b67
--- /dev/null
+++ b/packages/SystemUI/res/values-it/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Chiudi visualizzazione PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Schermo intero"</string>
+ <string name="pip_play" msgid="674145557658227044">"Riproduci"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pausa"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Tieni premuto "<b>"HOME"</b>" per controllare la visualizzazione PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Premi e tieni premuto HOME\nper controllare la visualizzazione PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index ed44624..2eda0e4 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -75,6 +75,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"צילום המסך בוצע."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"גע כדי להציג את צילום המסך שלך"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"לא ניתן לבצע צילום מסך."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"לא ניתן לשמור צילום מסך עקב שטח אחסון מוגבל."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"האפליקציה, או הארגון שלך, אינם מתירים לבצע צילומי מסך."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"אפשרויות העברת קבצים ב-USB"</string>
@@ -222,6 +224,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"מצב עבודה מופעל."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"מצב עבודה הושבת."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"מצב עבודה הופעל."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"בהירות תצוגה"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"השימוש בנתוני 2G-3G מושהה"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"השימוש בנתוני 4G מושהה"</string>
diff --git a/packages/SystemUI/res/values-iw/strings_tv.xml b/packages/SystemUI/res/values-iw/strings_tv.xml
new file mode 100644
index 0000000..74297e4
--- /dev/null
+++ b/packages/SystemUI/res/values-iw/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"סגור PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"מסך מלא"</string>
+ <string name="pip_play" msgid="674145557658227044">"הפעל"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"השהה"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"לחץ לחיצה ארוכה על "<b>"דף הבית"</b>" כדי לשלוט ב-PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"לחץ לחיצה ארוכה על לחצן דף הבית\nכדי לשלוט ב-PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"הבנתי"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index f104703..27bb232 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"スクリーンショットを取得しました。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"タップしてスクリーンショットを表示します。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"スクリーンショットをキャプチャできませんでした。"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"スクリーンショットの保存中に問題が発生しました。"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"空き容量が足りないため、スクリーンショットを保存できません。"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"アプリまたは組織によって許可されていないため、スクリーンショットは撮れません。"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USBファイル転送オプション"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Work モードがオンです。"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Work モードをオフにしました。"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Work モードをオンにしました。"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ディスプレイの明るさ"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G~3Gデータは一時停止中です"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4Gデータは一時停止中です"</string>
@@ -506,12 +511,9 @@
<string name="headset" msgid="4534219457597457353">"ヘッドセット"</string>
<string name="accessibility_status_bar_headphones" msgid="9156307120060559989">"ヘッドホンを接続しました"</string>
<string name="accessibility_status_bar_headset" msgid="8666419213072449202">"ヘッドセットを接続しました"</string>
- <!-- no translation found for data_saver (5037565123367048522) -->
- <skip />
- <!-- no translation found for accessibility_data_saver_on (8454111686783887148) -->
- <skip />
- <!-- no translation found for accessibility_data_saver_off (8841582529453005337) -->
- <skip />
+ <string name="data_saver" msgid="5037565123367048522">"データセーバー"</string>
+ <string name="accessibility_data_saver_on" msgid="8454111686783887148">"データセーバー ON"</string>
+ <string name="accessibility_data_saver_off" msgid="8841582529453005337">"データセーバー OFF"</string>
<string name="switch_bar_on" msgid="1142437840752794229">"ON"</string>
<string name="switch_bar_off" msgid="8803270596930432874">"OFF"</string>
<string name="nav_bar" msgid="1993221402773877607">"ナビゲーション バー"</string>
diff --git a/packages/SystemUI/res/values-ja/strings_tv.xml b/packages/SystemUI/res/values-ja/strings_tv.xml
new file mode 100644
index 0000000..be6693b
--- /dev/null
+++ b/packages/SystemUI/res/values-ja/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP を閉じる"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"全画面表示"</string>
+ <string name="pip_play" msgid="674145557658227044">"再生"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"一時停止"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP を管理するには ["<b>"ホーム"</b>"] を押し続けます"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP を管理するには\n[ホーム] ボタンを押し続けます"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"閉じる"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index b365eb3..41da191 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"სკრინშოტი გადაღებულია."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"შეეხეთ ეკრანის სურათის სანახავად."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ვერ მოხერხდა ეკრანის ანაბეჭდის გადაღება."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ეკრანის ანაბეჭდის შენახვა ვერ მოხერხდა შეზღუდული მეხსიერების გამო."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"ეკრანის ანაბეჭდების შექმნა არ არის ნებადართული აპის ან თქვენი ორგანიზაციის მიერ."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ფაილის ტრანსფერის პარამეტრები"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"სამსახურის რეჟიმი ჩართულია."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"სამსახურის რეჟიმი გამორთულია."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"სამსახურის რეჟიმი ჩართულია."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ეკრანის სიკაშკაშე"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G მონაცემები შეჩერებულია"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G მონაცემები შეჩერებულია"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings_tv.xml b/packages/SystemUI/res/values-ka-rGE/strings_tv.xml
new file mode 100644
index 0000000..97944c3
--- /dev/null
+++ b/packages/SystemUI/res/values-ka-rGE/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP-ის დახურვა"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"სრულ ეკრანზე"</string>
+ <string name="pip_play" msgid="674145557658227044">"დაკვრა"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"პაუზა"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP-ის სამართავად, გეჭიროთ "<b>"მთავარ ღილაკზე"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP-ის სამართავად, ხანგრძლივად\nდააჭირეთ მთავარ ღილაკს"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"გასაგებია"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index f8c54b6..bc56c8e 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот сақталды."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Скриншотты көру үшін түрту."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Скриншот жасалмады."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Скриншотты сақтау кезінде мәселе орын алды."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Жадтағы шектеулі бос орынға байланысты скриншотты сақтау мүмкін емес."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Қолданба немесе ұйым скриншоттар түсіруге рұқсат етпейді."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB файлын жіберу опциялары"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Жұмыс режимі қосулы."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Жұмыс режимі өшірілді."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Жұмыс режимі қосылды."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Дисплей жарықтығы"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G деректері кідіртілді"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G деректері кідіртілді"</string>
@@ -303,8 +308,7 @@
<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>
- <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
- <skip />
+ <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> қауіпсіз режимде өшіріледі."</string>
<string name="recents_history_button_label" msgid="5153358867807604821">"Тарих"</string>
<string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Тазалау"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Бұл қолданба көп терезені қолдамайды"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings_tv.xml b/packages/SystemUI/res/values-kk-rKZ/strings_tv.xml
new file mode 100644
index 0000000..f67157c
--- /dev/null
+++ b/packages/SystemUI/res/values-kk-rKZ/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP жабу"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Толық экран"</string>
+ <string name="pip_play" msgid="674145557658227044">"Ойнату"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Кідірту"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP бас. үшін "<b>"HOME"</b>" түй. ұс. тұр."</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP бас. үшін HOME\n түй. бас., ұс. тұр."</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Түсіндім"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 97772ad..cafa329 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ដើម្បីមើលរូបថតអេក្រង់របស់អ្នក។"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"មិនអាចចាប់យករូបថតអេក្រង់។"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"បានជួបប្រទះបញ្ហាខណៈពេលរក្សាទុកការថតអេក្រង់"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"មិនអាចរក្សាទុករូបថតអេក្រង់បានទេដោយសារទំហំផ្ទុកមានកម្រិត។"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"ការថតរូបអេក្រង់មិនត្រូវបានអនុញ្ញាតដោយកម្មវិធីនេះ ឬស្ថាប័នរបស់អ្នក។"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"ជម្រើសផ្ទេរឯកសារតាមយូអេសប៊ី"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"បើករបៀបការងារ"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"បានបិទរបៀបការងារ"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"បានបើករបៀបការងារ"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ពន្លឺការបង្ហាញ"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"ទិន្នន័យ 2G-3G ត្រូវបានផ្អាក"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"ទិន្នន័យ 4G ត្រូវបានផ្អាក"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings_tv.xml b/packages/SystemUI/res/values-km-rKH/strings_tv.xml
new file mode 100644
index 0000000..cc0a8b0
--- /dev/null
+++ b/packages/SystemUI/res/values-km-rKH/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"បិទ PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"ពេញអេក្រង់"</string>
+ <string name="pip_play" msgid="674145557658227044">"ចាក់"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"ផ្អាក"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"សង្កត់ប៊ូតុង "<b>"ដើម"</b>" ដើម្បីគ្រប់គ្រង PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"ចុច និងសង្កត់ប៊ូតុង ដើម\nដើម្បីគ្រប់គ្រង PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"យល់ហើយ"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 481b918c..62a1235 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಸೆರೆಹಿಡಿಯಲಾಗಿದೆ."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ಶಾಟ್ ವೀಕ್ಷಿಸಲು ಸ್ಪರ್ಶಿಸಿ."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಸೆರೆಹಿಡಿಯಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ಪರಿಮಿತ ಸಂಗ್ರಹಣೆ ಸ್ಥಳದ ಕಾರಣದಿಂದಾಗಿ ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"ಸ್ಕ್ರೀನ್ಶಾಟ್ಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುವುದನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ನಿಮ್ಮ ಸಂಸ್ಥೆ ಅನುಮತಿಸುವುದಿಲ್ಲ."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ಫೈಲ್ ವರ್ಗಾವಣೆ ಆಯ್ಕೆಗಳು"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"ಕೆಲಸದ ಮೋಡ್ ಆನ್ ಆಗಿದೆ."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"ಕೆಲಸದ ಮೋಡ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"ಕೆಲಸದ ಮೋಡ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ಹೊಳಪನ್ನು ಪ್ರದರ್ಶಿಸಿ"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ಡೇಟಾವನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ಡೇಟಾ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings_tv.xml b/packages/SystemUI/res/values-kn-rIN/strings_tv.xml
new file mode 100644
index 0000000..09d7b07
--- /dev/null
+++ b/packages/SystemUI/res/values-kn-rIN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP ಮುಚ್ಚಿ"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"ಪೂರ್ಣ ಪರದೆ"</string>
+ <string name="pip_play" msgid="674145557658227044">"ಪ್ಲೇ"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"ವಿರಾಮ"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP ನಿಯಂತ್ರಿಸಲು "<b>"HOME"</b>" ಕೀಯನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP ನಿಯಂತ್ರಿಸಲು HOME\n ಬಟನ್ ಒತ್ತಿರಿ ಮತ್ತು ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"ಅರ್ಥವಾಯಿತು"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index a3f4ff1..fca1dba 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"캡쳐화면 저장됨"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"캡쳐화면을 보려면 터치하세요."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"캡쳐화면을 캡쳐하지 못했습니다."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"저장용량이 부족하여 스크린샷을 저장할 수 없습니다."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"앱이나 조직에서 스크린샷 촬영을 허용하지 않습니다."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB 파일 전송 옵션"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"작업 모드가 사용 설정되었습니다."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"작업 모드가 사용 중지되었습니다."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"작업 모드가 사용 설정되었습니다."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"디스플레이 밝기"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G 데이터 사용 중지됨"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G 데이터 사용 중지됨"</string>
diff --git a/packages/SystemUI/res/values-ko/strings_tv.xml b/packages/SystemUI/res/values-ko/strings_tv.xml
new file mode 100644
index 0000000..ea12d5e
--- /dev/null
+++ b/packages/SystemUI/res/values-ko/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP 닫기"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"전체화면"</string>
+ <string name="pip_play" msgid="674145557658227044">"재생"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"일시중지"</string>
+ <string name="pip_hold_home" msgid="340086535668778109"><b>"HOME"</b>"을 눌러 PIP 제어"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"HOME\n버튼을 길게 눌러 PIP 제어"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"확인"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 66a34f0..8894820 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот тартылды."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Тийип, скриншотту көрүңүз."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Скриншот кылынбай жатат."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Сактагычта бош орун аз болгондуктан скриншот сакталбай жатат."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Скриншот тартууга колдонмо же ишканаңыз уруксат бербейт."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB менен файл өткөрүү мүмкүнчүлүктөрү"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Иштөө режими күйүк."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Иштөө режими өчүрүлдү."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Иштөө режими күйгүзүлдү."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Жарыктыгын көрсөтүү"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G дайындары тындырылды."</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G дайындары тындырылды"</string>
@@ -457,8 +463,8 @@
<string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Күйгүзүү"</string>
<string name="show_silently" msgid="6841966539811264192">"Эскертмелер үнсүз көрсөтүлсүн"</string>
<string name="block" msgid="2734508760962682611">"Бардык эскертмелерди бөгөттөө"</string>
- <string name="do_not_silence" msgid="6878060322594892441">"Үнсүз кылынбасын"</string>
- <string name="do_not_silence_block" msgid="4070647971382232311">"Үнсүз кылынып же бөгөттөлбөсүн"</string>
+ <string name="do_not_silence" msgid="6878060322594892441">"Үнү менен көрсөтүлсүн"</string>
+ <string name="do_not_silence_block" msgid="4070647971382232311">"Үнү менен көрсөтүлүп бөгөттөлбөсүн"</string>
<string name="tuner_full_importance_settings" msgid="8103289238676424226">"Маанилүүлүк жөндөөлөрү толук көрсөтүлсүн"</string>
<string name="blocked_importance" msgid="5198578988978234161">"Бөгөттөлгөн"</string>
<string name="min_importance" msgid="1901894910809414782">"Маанилүүлүгү эң төмөн"</string>
@@ -469,7 +475,7 @@
<string name="notification_importance_blocked" msgid="2397192642657872872">"Бул эскертмелер эч качан көрсөтүлбөсүн"</string>
<string name="notification_importance_min" msgid="1938190340516905748">"Эскертмелер тизмесинин соңунда үнсүз көрсөтүлсүн"</string>
<string name="notification_importance_low" msgid="3657252049508213048">"Бул эскертмелер үнсүз көрсөтүлсүн"</string>
- <string name="notification_importance_default" msgid="4466466472622442175">"Бул эскертмелер добуш чыгарышына уруксат берилсин"</string>
+ <string name="notification_importance_default" msgid="4466466472622442175">"Бул эскертмелер үнү менен көрсөтүлсүн"</string>
<string name="notification_importance_high" msgid="2135428926525093825">"Үн менен коштолуп, экранга чыгарылсын"</string>
<string name="notification_importance_max" msgid="5806278962376556491">"Эскертмелер тизмесинин эң башында көрсөтүлүп, үн менен коштолуп, экранга чыгарылсын"</string>
<string name="notification_more_settings" msgid="816306283396553571">"Дагы жөндөөлөр"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings_tv.xml b/packages/SystemUI/res/values-ky-rKG/strings_tv.xml
new file mode 100644
index 0000000..e95bc89
--- /dev/null
+++ b/packages/SystemUI/res/values-ky-rKG/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP\'ти жабуу"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Толук экран"</string>
+ <string name="pip_play" msgid="674145557658227044">"Ойнотуу"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Тындыруу"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP\'ти көзөмөлдөө үчүн "<b>"БАШКЫ БЕТ"</b>" баскычын кармап туруңуз"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP\'ти көзөмөлдөө үчүн БАШКЫ БЕТ\nбаскычын басып, кармап туруңуз"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Түшүндүм"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 26a81c8..585984c 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -22,6 +22,9 @@
<!-- Standard notification gravity -->
<integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer>
+ <!-- The size of the initial peek area at the bottom of the stack (above the nav bar). -->
+ <dimen name="recents_initial_bottom_peek_size">@dimen/recents_task_bar_height</dimen>
+
<dimen name="docked_divider_handle_width">2dp</dimen>
<dimen name="docked_divider_handle_height">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 8000621f..80c1e01 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ຖ່າຍຮູບໜ້າຈໍແລ້ວ"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ແຕະເພື່ອເບິ່ງພາບໜ້າຈໍຂອງທ່ານ."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ເກີດບັນຫາໃນການບັນທຶກພາບໜ້າຈໍຂອງທ່ານ."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້ເນື່ອງຈາກພື້ນທີ່ຈັດເກັບຂໍ້ມູນມີຈຳກັດ."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"ແອັບ ຫຼື ອົງກອນຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ມີການຖ່າຍຮູບໜ້າຈໍ."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ໂຕເລືອກການຍ້າຍໄຟລ໌"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"ໂໝດການເຮັດວຽກເປີດຢູ່."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"ໂໝດການເຮັດວຽກປິດຢູ່."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"ໂໝດການເຮັດວຽກເປີດຢູ່."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ຄວາມແຈ້ງຂອງຈໍ"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"ຂໍ້ມູນ 2G-3G ຢຸດຊົ່ວຄາວແລ້ວ"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"ຂໍ້ມູນ 4G ຢຸດຊົ່ວຄາວແລ້ວ"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings_tv.xml b/packages/SystemUI/res/values-lo-rLA/strings_tv.xml
new file mode 100644
index 0000000..f6bc833
--- /dev/null
+++ b/packages/SystemUI/res/values-lo-rLA/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"ປິດ PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"ເຕັມໜ້າຈໍ"</string>
+ <string name="pip_play" msgid="674145557658227044">"ຫຼິ້ນ"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"ຢຸດຊົ່ວຄາວ"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"ກົດ "<b>"HOME"</b>" ຄ້າງໄວ້ເພື່ອຄວບຄຸມ PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"ກົດປຸ່ມ HOME\nຄ້າງໄວ້ເພື່ອຄວບຄຸມ PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"ເຂົ້າໃຈແລ້ວ"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index bb49c39..4299efa 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -75,6 +75,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekrano kopija užfiksuota."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Palieskite, kad peržiūrėtumėte ekrano kopiją."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nepavyko užfiksuoti ekrano kopijos."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Jūsų organizacijoje arba naudojant šią programą neleidžiama daryti ekrano kopijų"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB failo perdavimo parinktys"</string>
@@ -222,6 +224,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Darbo režimas įjungtas."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Darbo režimas išjungtas."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Darbo režimas įjungtas."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Ekrano šviesumas"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G duomenys pristabdyti"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G duomenys pristabdyti"</string>
diff --git a/packages/SystemUI/res/values-lt/strings_tv.xml b/packages/SystemUI/res/values-lt/strings_tv.xml
new file mode 100644
index 0000000..140b55d
--- /dev/null
+++ b/packages/SystemUI/res/values-lt/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Uždaryti PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Visas ekranas"</string>
+ <string name="pip_play" msgid="674145557658227044">"Leisti"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pristabdyti"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Kad vald. PIP, pal. pasp. m. "<b>"PAGRINDINIS"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Kad vald. PIP, pal.\npasp. m. PAGRINDINIS"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Supratau"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 574889e..ad00ad0 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -74,6 +74,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekrānuzņēmums ir uzņemts."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Pieskarieties, lai skatītu ekrānuzņēmumu."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nevarēja uzņemt ekrānuzņēmumu."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Nevar saglabāt ekrānuzņēmumu, jo krātuvē nepietiek vietas."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Lietotne vai jūsu organizācija neatļauj veikt ekrānuzņēmumus."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB failu pārsūtīšanas opcijas"</string>
@@ -221,6 +223,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Darba režīms ir ieslēgts."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Darba režīms ir izslēgts."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Darba režīms ir ieslēgts."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Ekrāna spilgtums"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G datu lietojums ir apturēts"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G datu lietojums ir apturēts"</string>
diff --git a/packages/SystemUI/res/values-lv/strings_tv.xml b/packages/SystemUI/res/values-lv/strings_tv.xml
new file mode 100644
index 0000000..d7ca370
--- /dev/null
+++ b/packages/SystemUI/res/values-lv/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Aizvērt PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Pilnekrāna režīms"</string>
+ <string name="pip_play" msgid="674145557658227044">"Atskaņot"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Apturēt"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Turiet taustiņu "<b>"SĀKUMS"</b>", lai kontrolētu PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Nospiediet un turiet pogu SĀKUMS,\n lai kontrolētu PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Labi"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 58b7b75..4116b6f 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Сликата на екранот е снимена."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Допрете за да ја видите сликата на екранот."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Сликата на екранот не можеше да се сними."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Сликата од екранот не може да се зачува поради ограничена меморија."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Апликацијата или вашата организација не дозволува создавање слики од екранот."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Пренос на датотека со УСБ"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Режимот на работа е вклучен."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Режимот на работа е исклучен."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Режимот на работа е вклучен."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Осветленост на екранот"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Податоците 2G-3G се паузирани"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Податоците 4G се паузирани"</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings_tv.xml b/packages/SystemUI/res/values-mk-rMK/strings_tv.xml
new file mode 100644
index 0000000..5f88cb5
--- /dev/null
+++ b/packages/SystemUI/res/values-mk-rMK/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Затвори PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Цел екран"</string>
+ <string name="pip_play" msgid="674145557658227044">"Пушти"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Пауза"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Задржете "<b>"ДОМА"</b>" за кон. PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Притиснете и задржете го копчето\nДОМА за контролирање PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Разбрав"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 6627645..f861a00 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"സ്ക്രീൻഷോട്ട് എടുത്തു."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"നിങ്ങളുടെ സ്ക്രീൻഷോട്ട് കാണാനായി സ്പർശിക്കുക."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"സ്ക്രീൻഷോട്ട് എടുക്കാൻ കഴിഞ്ഞില്ല."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"സ്റ്റോറേജ് ഇടം പരിമിതമായതിനാൽ സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കാൻ കഴിയില്ല."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"സ്ക്രീൻഷോട്ടുകൾ എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ഫയൽ കൈമാറൽ ഓപ്ഷനുകൾ"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"പ്രവർത്തന മോഡ് ഓണാണ്."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"പ്രവർത്തന മോഡ് ഓഫാക്കി."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"പ്രവർത്തന മോഡ് ഓണാക്കി."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ഡിസ്പ്ലേ തെളിച്ചം"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ഡാറ്റ താൽക്കാലികമായി നിർത്തി"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ഡാറ്റ താൽക്കാലികമായി നിർത്തി"</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings_tv.xml b/packages/SystemUI/res/values-ml-rIN/strings_tv.xml
new file mode 100644
index 0000000..0094bb6
--- /dev/null
+++ b/packages/SystemUI/res/values-ml-rIN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP അടയ്ക്കുക"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"പൂര്ണ്ണ സ്ക്രീന്"</string>
+ <string name="pip_play" msgid="674145557658227044">"പ്ലേ ചെയ്യുക"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"തൽക്കാലം നിർത്തൂ"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP നിയന്ത്രിക്കാൻ "<b>"ഹോം"</b>" പിടിക്കുക"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP നിയന്ത്രിക്കാൻ ഹോം\nബട്ടൺ അമർത്തിപ്പിടിക്കുക"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"മനസ്സിലായി"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index a69be1f..73df535 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -71,6 +71,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Дэлгэцийн агшинг авсан."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Дэлгэцийн агшныг харах бол хүрнэ үү."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Дэлгэцийн агшинг авч чадсангүй."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Дэлгэцийн агшинг хадгалахад алдаа гарлаа."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Хадгалах сангийн багтаамж бага байгаа тул дэлгэцийн авсан зургийг хадгалах боломжгүй байна."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Дэлгэцийн зураг авахыг апп эсвэл танай байгууллагаас зөвшөөрөөгүй байна."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB файл шилжүүлэх сонголт"</string>
@@ -218,6 +219,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Ажлын горимыг асаасан."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Ажлын горимыг унтраасан."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Ажлын горимыг асаасан."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Дэлгэцийн гэрэлтэлт"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G дата-г түр зогсоосон байна"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G дата-г түр зогсоосон байна"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings_tv.xml b/packages/SystemUI/res/values-mn-rMN/strings_tv.xml
new file mode 100644
index 0000000..78b7cc8
--- /dev/null
+++ b/packages/SystemUI/res/values-mn-rMN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP-г хаах"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Бүтэн дэлгэц"</string>
+ <string name="pip_play" msgid="674145557658227044">"Тоглуулах"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Түр зогсоох"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP-г удирдахын тулд "<b>"HOME"</b>" товчлуурыг дарна уу"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP-г удирдахын тулд HOME\nтовчлуурыг дараад хүлээнэ үү"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Ойлголоо"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index d99678c..a3cb736 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रीनशॉट कॅप्चर केला."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"आपला स्क्रीनशॉट पाहण्यासाठी स्पर्श करा."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रीनशॉट कॅप्चर करू शकलो नाही."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"मर्यादित संचय जागेमुळे स्क्रीनशॉट जतन करू शकत नाही."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"अॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्यास अनुमती नाही."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB फाईल स्थानांतरण पर्याय"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"कार्य मोड चालू."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"कार्य मोड बंद केला."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"कार्य मोड चालू केला."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"प्रदर्शन चमक"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G डेटास विराम दिला आहे"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G डेटास विराम दिला आहे"</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings_tv.xml b/packages/SystemUI/res/values-mr-rIN/strings_tv.xml
new file mode 100644
index 0000000..fd76c53
--- /dev/null
+++ b/packages/SystemUI/res/values-mr-rIN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP बंद करा"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"पूर्ण स्क्रीन"</string>
+ <string name="pip_play" msgid="674145557658227044">"प्ले करा"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"विराम द्या"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP नियंत्रित करण्यासाठी "<b>"मुख्यपृष्ठ"</b>" धरून ठेवा"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP नियंत्रित करण्यासाठी\nमुख्यपृष्ठ बटण धरून ठेवा"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"समजले"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index a716eef..75fdf62 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Tangkapan skrin ditangkap."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Sentuh untuk melihat tangkapan skrin anda."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Tidak dapat menangkap tangkapan skrin."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Tidak dapat menyimpan tangkapan skrin kerana ruang storan terhad."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Apl atau organisasi anda tidak membenarkan pengambilan tangkapan skrin."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Pilihan pemindahan fail USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Mod kerja hidup."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Mod kerja dimatikan."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Mod kerja dihidupkan."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Kecerahan paparan"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data 2G-3G dijeda"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Data 4G dijeda"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings_tv.xml b/packages/SystemUI/res/values-ms-rMY/strings_tv.xml
new file mode 100644
index 0000000..5fc98b9
--- /dev/null
+++ b/packages/SystemUI/res/values-ms-rMY/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Tutup PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Skrin penuh"</string>
+ <string name="pip_play" msgid="674145557658227044">"Main"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Jeda"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Thn "<b>"SKRN UTMA"</b>" utk kwl PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Tkn & thn butang SKRIN UTAMA\nutk mengawal PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 503b91f..d7597a5 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား ဖမ်းယူပြီး"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"သင့်ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား ကြည့်ရှုရန် ထိပါ"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား မဖမ်းစီးနိုင်ပါ"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ဖန်သားပြင်ဓာတ်ပုံဖမ်းယူမှုကို သိမ်းဆည်းရာတွင် ပြဿနာကြုံခဲ့သည်။"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ။"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို အက်ပ်မှ သို့မဟုတ် သင့်အဖွဲ့အစည်းမှ ခွင့်မပြုပါ။"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ဖိုင်ပြောင်း ရွေးမှုများ"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"အလုပ် မုဒ်ကို ဖွင့်ထားပါသည်။"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"အလုပ် မုဒ်ကို ပိတ်ထားပါသည်။"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"အလုပ် မုဒ်ကို ဖွင့်ထားပါသည်။"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"တောက်ပမှုကို ပြရန်"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ဒေတာ ခေတ္တရပ်တန့်သည်"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data ခေတ္တရပ်တန့်သည်"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings_tv.xml b/packages/SystemUI/res/values-my-rMM/strings_tv.xml
new file mode 100644
index 0000000..8491e37
--- /dev/null
+++ b/packages/SystemUI/res/values-my-rMM/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP ကိုပိတ်ပါ"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"မျက်နှာပြင် အပြည့်"</string>
+ <string name="pip_play" msgid="674145557658227044">"ဖွင့်ပါ"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"ဆိုင်းငံ့ပါ"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP ကိုထိန်းချုပ်ရန် "<b>"ပင်မ"</b>" ခလုတ်ကို ဖိထားပါ"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP ကိုထိန်းချုပ်ရန် ပင်မ\nခလုတ်ကို နှိပ်ပြီးဖိထားပါ"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"ရပါပြီ"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index b3de239..5b0c6eb 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skjermdumpen er lagret."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Trykk for å se skjermdumpen."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Kan ikke lagre skjermdumpen."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kan ikke lagre skjermdumpen på grunn av begrenset lagringsplass."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Appen eller organisasjonen din tillater ikke at du tar skjermdumper."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Altern. for USB-filoverføring"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Arbeidsmodusen er på."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Arbeidsmodusen er slått av."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Arbeidsmodusen er slått på."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Lysstyrken på skjermen"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G- og 3G-data er satt på pause"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data er satt på pause"</string>
diff --git a/packages/SystemUI/res/values-nb/strings_tv.xml b/packages/SystemUI/res/values-nb/strings_tv.xml
new file mode 100644
index 0000000..8574d66
--- /dev/null
+++ b/packages/SystemUI/res/values-nb/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Lukk PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Fullskjerm"</string>
+ <string name="pip_play" msgid="674145557658227044">"Spill av"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Sett på pause"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Hold inne "<b>"STARTSIDE"</b>" for å kontrollere PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Trykk og hold STARTSIDE-\nknappen for å kontrollere PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Greit"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 4b78552..42067ab 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"स्क्रिनसट क्याप्चर गरियो।"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"तपाईँको स्क्रिनसट हेर्न छुनुहोस्।"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"स्क्रिनसट क्याप्चर गर्न सकिएन।"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"स्क्रिनसटलाई सुरक्षित गर्दा समस्या भयो।"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"सीमित भण्डारण स्थान उपलब्ध रहेको हुनाले स्क्रिनसटलाई सुरक्षित गर्न सकिँदैन।"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"अनुप्रयोग वा तपाईँको संगठनले स्क्रिनसट लिन अनुमति दॅिंदैन।"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB फाइल सार्ने विकल्पहरू"</string>
@@ -220,6 +221,8 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"कार्य मोड अन।"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"कार्य मोड बन्द भयो।"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"कार्य मोड सक्रिय भयो।"</string>
+ <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"डेटा सेभरलाई निष्क्रिय पारियो।"</string>
+ <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"डेटा सेभरलाई सक्रिय गरियो।"</string>
<string name="accessibility_brightness" msgid="8003681285547803095">"प्रदर्शन चमक"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G डेटा रोकिएको छ"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G डेटा रोकिएको छ"</string>
@@ -303,8 +306,7 @@
<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>
- <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
- <skip />
+ <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> लाई सुरक्षित-मोडमा असक्षम गरिएको छ।"</string>
<string name="recents_history_button_label" msgid="5153358867807604821">"इतिहास"</string>
<string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"मेटाउनुहोस्"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"यस अनुप्रयोगले बहु-विन्डोलाई समर्थन गर्दैन"</string>
@@ -456,11 +458,11 @@
<string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लुटुथ सक्रिय पार्ने हो?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"आफ्नो ट्याब्लेटसँग किबोर्ड जोड्न, पहिले तपाईँले ब्लुटुथ सक्रिय गर्नुपर्छ।"</string>
<string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"सक्रिय पार्नुहोस्"</string>
- <string name="show_silently" msgid="6841966539811264192">"सूचनाहरूलाई बिना आवाज देखाउनुहोस्"</string>
+ <string name="show_silently" msgid="6841966539811264192">"सूचनाहरूलाई बिना आवाज देखाउने"</string>
<string name="block" msgid="2734508760962682611">"सबै सूचनाहरूलाई रोक्नुहोस्"</string>
<string name="do_not_silence" msgid="6878060322594892441">"मौन नगर्नुहोस्"</string>
<string name="do_not_silence_block" msgid="4070647971382232311">"मौन नगर्नुहोस् वा नरोक्नुहोस्"</string>
- <string name="tuner_full_importance_settings" msgid="8103289238676424226">"पूर्ण महत्त्व सेटिङहरू देखाउनुहोस्"</string>
+ <string name="tuner_full_importance_settings" msgid="8103289238676424226">"पूर्ण महत्त्व सेटिङहरू देखाउने"</string>
<string name="blocked_importance" msgid="5198578988978234161">"रोकियो"</string>
<string name="min_importance" msgid="1901894910809414782">"न्यूनतम महत्त्व"</string>
<string name="low_importance" msgid="4109929986107147930">"न्यून महत्त्व"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings_tv.xml b/packages/SystemUI/res/values-ne-rNP/strings_tv.xml
new file mode 100644
index 0000000..68c86ca
--- /dev/null
+++ b/packages/SystemUI/res/values-ne-rNP/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP लाई बन्द गर्नुहोस्"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"पूर्ण स्क्रिन"</string>
+ <string name="pip_play" msgid="674145557658227044">"प्ले गर्नुहोस्"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"रोक्नुहोस्"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP लाई नियन्त्रण गर्न "<b>"गृह"</b>" कुञ्जीलाई थिचिरहनुहोस्"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP लाई नियन्त्रण गर्न गृह\nबटनलाई थिचेर होल्ड गर्नुहोस्"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"बुझेँ"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 9698cdf..6f15baf 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot gemaakt."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Raak aan om je screenshot te bekijken."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot is niet gemaakt."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Er is een probleem opgetreden bij het opslaan van het screenshot."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Kan screenshot niet opslaan vanwege beperkte opslagruimte."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Het maken van screenshots wordt niet toegestaan door de app of je organisatie."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opties voor USB-bestandsoverdracht"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Werkmodus aan."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Werkmodus uitgeschakeld."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Werkmodus ingeschakeld."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Helderheid van het scherm"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G/3G-data zijn onderbroken"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data zijn onderbroken"</string>
diff --git a/packages/SystemUI/res/values-nl/strings_tv.xml b/packages/SystemUI/res/values-nl/strings_tv.xml
new file mode 100644
index 0000000..c116f81
--- /dev/null
+++ b/packages/SystemUI/res/values-nl/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP sluiten"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Volledig scherm"</string>
+ <string name="pip_play" msgid="674145557658227044">"Afspelen"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Onderbreken"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Bedien PIP met "<b>"HOME"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Houd HOME ingedrukt\nom PIP te bedienen"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index 6fb2c70..d07c4d2 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ਸਕ੍ਰੀਨਸ਼ੌਟ ਕੈਪਚਰ ਕੀਤਾ।"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ਆਪਣਾ ਸਕ੍ਰੀਨਸ਼ੌਟ ਦੇਖਣ ਲਈ ਛੋਹਵੋ।"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ਸਕ੍ਰੀਨਸ਼ੌਟ ਕੈਪਚਰ ਨਹੀਂ ਕਰ ਸਕਿਆ।"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਕਰਨ ਦੌਰਾਨ ਸਮੱਸਿਆ ਆਈ।"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ਸੀਮਿਤ ਸਟੋਰੇਜ ਥਾਂ ਦੇ ਕਾਰਨ ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਸਕਦਾ।"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਦੁਆਰਾ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਮਨਜ਼ੂਰੀ ਨਹੀਂ ਹੈ।"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਚੋਣਾਂ"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"ਕੰਮ ਮੋਡ ਚਾਲੂ ਹੈ।"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"ਕੰਮ ਮੋਡ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"ਕੰਮ ਮੋਡ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ਡਿਸਪਲੇ ਚਮਕ"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ਡਾਟਾ ਰੁਕ ਗਿਆ ਹੈ"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ਡਾਟਾ ਰੁਕ ਗਿਆ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings_tv.xml b/packages/SystemUI/res/values-pa-rIN/strings_tv.xml
new file mode 100644
index 0000000..1bf28af
--- /dev/null
+++ b/packages/SystemUI/res/values-pa-rIN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP ਬੰਦ ਕਰੋ"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
+ <string name="pip_play" msgid="674145557658227044">"ਚਲਾਓ"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"ਰੋਕੋ"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP ਕੰਟਰੋਲ ਕਰਨ ਲਈ "<b>"ਹੋਮ"</b>" ਦਬਾਈ ਰੱਖੋ"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP ਕੰਟਰੋਲ ਕਰਨ ਲਈ ਹੋਮ\nਬਟਨ ਦਬਾਈ ਰੱਖੋ"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"ਸਮਝ ਲਿਆ"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 974685c..8a1fff6 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -75,6 +75,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Wykonano zrzut ekranu."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Dotknij, aby wyświetlić zrzut ekranu."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nie udało się wykonać zrzutu ekranu."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Nie można zapisać zrzutu ekranu, bo brakuje miejsca w pamięci."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Nie możesz wykonać zrzutu ekranu, bo nie zezwala na to aplikacja lub Twoja organizacja."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB – opcje przesyłania plików"</string>
@@ -222,6 +224,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Tryb pracy włączony."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Tryb pracy wyłączony."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Tryb pracy włączony."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Jasność wyświetlacza"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Transmisja danych 2G-3G została wstrzymana"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Transmisja danych 4G została wstrzymana"</string>
diff --git a/packages/SystemUI/res/values-pl/strings_tv.xml b/packages/SystemUI/res/values-pl/strings_tv.xml
new file mode 100644
index 0000000..b804146
--- /dev/null
+++ b/packages/SystemUI/res/values-pl/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Zamknij PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Pełny ekran"</string>
+ <string name="pip_play" msgid="674145557658227044">"Odtwórz"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Wstrzymaj"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Przytrzymaj "<b>"EKRAN GŁÓWNY"</b>", by sterować PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Przytrzymaj EKRAN GŁÓWNY,\nby sterować PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 3797adc..1cf0ed9 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de tela obtida."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toque para visualizar a captura de tela."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter a captura de tela."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Não é possível salvar a captura de tela, porque não há espaço suficiente."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Capturas de tela não são permitidas pelo app ou por sua organização."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opções transf. arq. por USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Modo de trabalho ativado."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Modo de trabalho desativado."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modo de trabalho ativado."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Brilho da tela"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Os dados 2G e 3G foram pausados"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Os dados 4G foram pausados"</string>
@@ -303,8 +309,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixação de tela"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
- <skip />
+ <string name="recents_launch_disabled_message" msgid="1624523193008871793">"O app <xliff:g id="APP">%s</xliff:g> está desativado no modo de segurança."</string>
<string name="recents_history_button_label" msgid="5153358867807604821">"Histórico"</string>
<string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Limpar"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Este app não é compatível com o modo de várias janelas"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings_tv.xml b/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
new file mode 100644
index 0000000..21d36e0
--- /dev/null
+++ b/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Fechar PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Tela cheia"</string>
+ <string name="pip_play" msgid="674145557658227044">"Reproduzir"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pausar"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Mantenha "<b>"INÍCIO"</b>" pressionado para controlar o PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Mantenha o botão INÍCIO\npressionado para controlar o PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Entendi"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 7ee2554..520abc4 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de ecrã efetuada"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toque para ver a captura de ecrã"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter captura de ecrã."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Problema encontrado ao guardar a captura de ecrã."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Não é possível guardar a captura de ecrã devido a espaço de armazenamento limitado."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"A aplicação ou a sua entidade não tem autorização para tirar capturas de ecrã."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opções de transm. de fich. USB"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Modo de trabalho ativado."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"O modo de trabalho foi desativado."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"O modo de trabalho foi ativado."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Brilho do visor"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Dados 2G-3G em pausa"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Dados 4G em pausa"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings_tv.xml b/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
new file mode 100644
index 0000000..8b1b032
--- /dev/null
+++ b/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Fechar PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Ecrã inteiro"</string>
+ <string name="pip_play" msgid="674145557658227044">"Reproduzir"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Interromper"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Prima sem soltar o botão "<b>"HOME"</b>" para controlar o PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Prima sem soltar o botão HOME\npara controlar o PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Compreendi"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 3797adc..1cf0ed9 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captura de tela obtida."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Toque para visualizar a captura de tela."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Não foi possível obter a captura de tela."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Não é possível salvar a captura de tela, porque não há espaço suficiente."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Capturas de tela não são permitidas pelo app ou por sua organização."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opções transf. arq. por USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Modo de trabalho ativado."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Modo de trabalho desativado."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modo de trabalho ativado."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Brilho da tela"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Os dados 2G e 3G foram pausados"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Os dados 4G foram pausados"</string>
@@ -303,8 +309,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixação de tela"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
- <skip />
+ <string name="recents_launch_disabled_message" msgid="1624523193008871793">"O app <xliff:g id="APP">%s</xliff:g> está desativado no modo de segurança."</string>
<string name="recents_history_button_label" msgid="5153358867807604821">"Histórico"</string>
<string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Limpar"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Este app não é compatível com o modo de várias janelas"</string>
diff --git a/packages/SystemUI/res/values-pt/strings_tv.xml b/packages/SystemUI/res/values-pt/strings_tv.xml
new file mode 100644
index 0000000..21d36e0
--- /dev/null
+++ b/packages/SystemUI/res/values-pt/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Fechar PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Tela cheia"</string>
+ <string name="pip_play" msgid="674145557658227044">"Reproduzir"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pausar"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Mantenha "<b>"INÍCIO"</b>" pressionado para controlar o PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Mantenha o botão INÍCIO\npressionado para controlar o PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Entendi"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index c2bec08..113f2bc 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -74,6 +74,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Captură de ecran realizată."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Atingeți pentru a vedea captura de ecran."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Captura de ecran nu a putut fi realizată."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opțiuni pentru transferul de fișiere prin USB"</string>
@@ -221,6 +223,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Modul de lucru este activat."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Modul de lucru a fost dezactivat."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modul de lucru a fost activat."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Luminozitatea ecranului"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Conexiunea de date 2G – 3G este întreruptă"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Conexiunea de date 4G este întreruptă"</string>
diff --git a/packages/SystemUI/res/values-ro/strings_tv.xml b/packages/SystemUI/res/values-ro/strings_tv.xml
new file mode 100644
index 0000000..e30b2f9
--- /dev/null
+++ b/packages/SystemUI/res/values-ro/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Închideți PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Ecran complet"</string>
+ <string name="pip_play" msgid="674145557658227044">"Redați"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Întrerupeți"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Apăsați lung "<b>"ACASĂ"</b>" pentru a controla PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Apăsați lung ACASĂ\npentru a controla PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Am înțeles"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 8ece7fa..c131284 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -75,6 +75,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Скриншот сохранен"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Нажмите, чтобы просмотреть"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Не удалось сохранить скриншот."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Не удалось сохранить скриншот: недостаточно места."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Параметры передачи через USB"</string>
@@ -222,6 +224,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Рабочий режим включен."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Рабочий режим отключен."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Рабочий режим включен."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Яркость экрана"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Передача данных 2G и 3G приостановлена"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Передача данных 4G приостановлена"</string>
diff --git a/packages/SystemUI/res/values-ru/strings_tv.xml b/packages/SystemUI/res/values-ru/strings_tv.xml
new file mode 100644
index 0000000..f25b460
--- /dev/null
+++ b/packages/SystemUI/res/values-ru/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"\"Кадр в кадре\" – выйти"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Во весь экран"</string>
+ <string name="pip_play" msgid="674145557658227044">"Воспроизвести"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Приостановить"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Управляйте кадром в кадре, удерживая кнопку "<b>"ГЛАВНАЯ"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Управляйте кадром в кадре,\nудерживая кнопку \"ГЛАВНАЯ\""</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"ОК"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index b485320..5a09d3e 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"තිර රුව ග්රහණය කරන ලදි."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ඔබගේ තිර රුව බැලීමට ස්පර්ශ කරන්න."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"තිර රුව ග්රහණය කිරීමට නොහැකි විය."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"තිර රුව සුරකින අතරතුර ගැටලුවක් ඇති විය."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"තිර රූ ගැනීමට යෙදුම හෝ ඔබගේ සංවිධානය ඉඩ නොදේ."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ගොනු හුවමාරු විකල්ප"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"වැඩ ප්රකාරය ක්රියාත්මකයි."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"වැඩ ප්රකාරය ක්රියාවිරහිත කරන ලදී."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"වැඩ ප්රකාරය ක්රියාත්මක කරන ලදී."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"දීප්තිය දර්ශනය කරන්න"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G දත්ත විරාම කර ඇත"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G දත්ත විරාම කර ඇත"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings_tv.xml b/packages/SystemUI/res/values-si-rLK/strings_tv.xml
new file mode 100644
index 0000000..b2c6485
--- /dev/null
+++ b/packages/SystemUI/res/values-si-rLK/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP වසන්න"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"සම්පූර්ණ තිරය"</string>
+ <string name="pip_play" msgid="674145557658227044">"ධාවනය කරන්න"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"විරාමය"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP පාලනයට "<b>"HOME"</b>" අල්ලාගන්න"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP පාලනයට HOME\nඔබා අල්ලාගන්න"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"හරි, තේරුණා"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 40daf19..26d6d2f 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -75,6 +75,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Snímka obrazovky bola zaznamenaná."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Snímku obrazovky zobrazíte dotykom."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Snímku obrazovky sa nepodarilo zachytiť."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Snímku obrazovky nie je možné vytvoriť z dôvodu nedostatku miesta v úložisku."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Vytváranie snímok obrazovky je zakázané aplikáciou alebo vašou organizáciou."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Možnosti prenosu súborov USB"</string>
@@ -222,6 +224,8 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Pracovný režim – zap."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Pracovný režim je vypnutý."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Pracovný režim je zapnutý."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"Šetrič dát bol vypnutý."</string>
+ <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"Šetrič dát bol zapnutý."</string>
<string name="accessibility_brightness" msgid="8003681285547803095">"Jas displeja"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Dátové prenosy 2G a 3G sú pozastavené"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Dátové prenosy 4G sú pozastavené"</string>
diff --git a/packages/SystemUI/res/values-sk/strings_tv.xml b/packages/SystemUI/res/values-sk/strings_tv.xml
new file mode 100644
index 0000000..46f88f9
--- /dev/null
+++ b/packages/SystemUI/res/values-sk/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Zavrieť režim PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Celá obrazovka"</string>
+ <string name="pip_play" msgid="674145557658227044">"Prehrať"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pozastaviť"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Režim PIP ovládajte pomocou tlačidla "<b>"PLOCHA"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Režim PIP ovládajte stlačením a podržaním tlačidla PLOCHA\n"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Dobre"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 756c43d..b268f18 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -75,6 +75,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Posnetek zaslona je shranjen."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Dotaknite se, če si želite ogledati posnetek zaslona."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Posnetka zaslona ni bilo mogoče shraniti."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Pri shranjevanju posnetka zaslona je prišlo do težave."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Aplikacija ali organizacija ne dovoljuje posnetkov zaslona."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Možnosti prenosa datotek prek USB-ja"</string>
@@ -222,6 +223,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Način za delo vklopljen."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Način za delo je izklopljen."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Način za delo je vklopljen."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Svetlost zaslona"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Prenos podatkov v omrežju 2G/3G je zaustavljen"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Prenos podatkov v omrežju 4G je zaustavljen"</string>
diff --git a/packages/SystemUI/res/values-sl/strings_tv.xml b/packages/SystemUI/res/values-sl/strings_tv.xml
new file mode 100644
index 0000000..e1d1604
--- /dev/null
+++ b/packages/SystemUI/res/values-sl/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Zapri način PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Celozaslonsko"</string>
+ <string name="pip_play" msgid="674145557658227044">"Predvajanje"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Zaustavitev"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Pridr. "<b>"HOME"</b>" za up. n. PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Pridržite gumb HOME\nza upravljanje načina PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Razumem"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index 8b3861e..b3e6ea8 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Pamja e ekranit u kap."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Prek për të parë pamjen e ekranit tënd."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Nuk mundi të kapte pamjen e ekranit."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Pamja e ekranit nuk mund të ruhet për shkak të hapësirës ruajtëse të kufizuar."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opsionet e transferimit të dosjeve të USB-së"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Modaliteti i punës është i aktivizuar."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Modaliteti i punës është i çaktivizuar."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Modaliteti i punës është i aktivizuar."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Ndriçimi i ekranit"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Të dhënat 2G-3G janë ndërprerë"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Të dhënat 4G janë ndërprerë"</string>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings_tv.xml b/packages/SystemUI/res/values-sq-rAL/strings_tv.xml
new file mode 100644
index 0000000..28bfbb5
--- /dev/null
+++ b/packages/SystemUI/res/values-sq-rAL/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Mbyll PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Ekrani i plotë"</string>
+ <string name="pip_play" msgid="674145557658227044">"Luaj"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pauzë"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Mbaj shtypur "<b>"HOME"</b>" për të kontrolluar PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Shtyp dhe mbaj shtypur butonin HOME\npër të kontrolluar PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"E kuptova"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 991e9e27..9c26ebd 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -74,6 +74,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Снимак екрана је направљен."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Додирните да бисте видели снимак екрана."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Није могуће направити снимак екрана."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Чување снимка екрана није успело због ограниченог меморијског простора."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Апликација или организација не дозвољавају прављење снимака екрана."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Опције USB преноса датотека"</string>
@@ -221,6 +223,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Режим рада је укључен."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Режим рада је искључен."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Режим рада је укључен."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Осветљеност екрана"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G подаци су паузирани"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G подаци су паузирани"</string>
diff --git a/packages/SystemUI/res/values-sr/strings_tv.xml b/packages/SystemUI/res/values-sr/strings_tv.xml
new file mode 100644
index 0000000..4b03b68
--- /dev/null
+++ b/packages/SystemUI/res/values-sr/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Затвори PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Цео екран"</string>
+ <string name="pip_play" msgid="674145557658227044">"Пусти"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Паузирај"</string>
+ <string name="pip_hold_home" msgid="340086535668778109"><b>"ПОЧЕТНИ ЕКРАН"</b>" конт. PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Притисните и задржите дугме ПОЧЕТНИ ЕКРАН\n да бисте контролисали PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Важи"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index d35fad5..0fb5731 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skärmdumpen har tagits."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Tryck här om du vill visa skärmdumpen."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Det gick inte att ta någon skärmdump."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Det går inte att spara skärmdumpen eftersom lagringsutrymmet inte räcker."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Appen eller organisationen tillåter inte att du tar skärmdumpar."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Överföringsalternativ"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Arbetsläget aktiverat."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Arbetsläget har inaktiverats."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Arbetsläget har aktiverats."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Skärmens ljusstyrka"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G- och 3G-data har pausats"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data har pausats"</string>
diff --git a/packages/SystemUI/res/values-sv/strings_tv.xml b/packages/SystemUI/res/values-sv/strings_tv.xml
new file mode 100644
index 0000000..dc65877
--- /dev/null
+++ b/packages/SystemUI/res/values-sv/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Stäng PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Helskärm"</string>
+ <string name="pip_play" msgid="674145557658227044">"Spela upp"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pausa"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Styr PIP med "<b>"startknappen"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Håll ned startknappen\n för att styra PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 4978d94..a716281 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Picha ya skrini imenaswa."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Gusa ili kuona picha yako ya skrini."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Haikuweza kunasa picha ya skrini"</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Haina nafasi ya kutosha kuhifadhi picha ya skrini."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Shirika au programu yako haikuruhusu upige picha za skrini."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Machaguo ya uhamisho wa faili la USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Hali ya kazi imewashwa."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Hali ya kazi imezimwa."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Hali ya kazi imewashwa."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Ung\'aavu wa skrini"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Data ya 2G-3G imesitishwa"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Data ya 4G imesitishwa"</string>
@@ -461,7 +467,7 @@
<string name="do_not_silence_block" msgid="4070647971382232311">"Usinyamazishe wala kuzuia"</string>
<string name="tuner_full_importance_settings" msgid="8103289238676424226">"Onyesha mipangilio kamili ya umuhimu"</string>
<string name="blocked_importance" msgid="5198578988978234161">"Amezuiwa"</string>
- <string name="min_importance" msgid="1901894910809414782">"Umuhimu wa kiwango cha chini zaidi"</string>
+ <string name="min_importance" msgid="1901894910809414782">"Umuhimu wa kiwango cha chini"</string>
<string name="low_importance" msgid="4109929986107147930">"Umuhimu kiwango cha chini"</string>
<string name="default_importance" msgid="8192107689995742653">"Umuhimu wa kiwango cha kawaida"</string>
<string name="high_importance" msgid="1527066195614050263">"Umuhimu wa kiwango cha juu"</string>
diff --git a/packages/SystemUI/res/values-sw/strings_tv.xml b/packages/SystemUI/res/values-sw/strings_tv.xml
new file mode 100644
index 0000000..9b3799a
--- /dev/null
+++ b/packages/SystemUI/res/values-sw/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Funga PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Skrini nzima"</string>
+ <string name="pip_play" msgid="674145557658227044">"Cheza"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Sitisha"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Shikilia kitufe cha "<b>"HOME"</b>" ili udhibiti PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Bonyeza na ushikilie kitufe cha\nHOME ili udhibiti PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Nimeelewa"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 122413d..a2fa3b9 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -99,6 +99,9 @@
<!-- The top padding for the task stack. -->
<dimen name="recents_stack_top_padding">40dp</dimen>
+ <!-- The size of the initial peek area at the bottom of the stack (above the nav bar). -->
+ <dimen name="recents_initial_bottom_peek_size">100dp</dimen>
+
<!-- The side padding for the task stack. -->
<dimen name="recents_stack_left_right_padding">64dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index 66a1496..59fe9dd 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"ஸ்கிரீன் ஷாட் எடுக்கப்பட்டது."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"உங்கள் ஸ்க்ரீன் ஷாட்டைப் பார்க்க தொடவும்."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ஸ்க்ரீன் ஷாட்டை எடுக்க முடியவில்லை."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"போதுமான சேமிப்பிடம் இல்லாததால் ஸ்கிரீன்ஷாட்டைச் சேமிக்க முடியவில்லை."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"பயன்பாடு அல்லது உங்கள் நிறுவனம் ஸ்கிரீன்ஷாட்டுகளை எடுக்க அனுமதிக்கவில்லை."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB கோப்பு இடமாற்ற விருப்பங்கள்"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"பணிப் பயன்முறை இயக்கப்பட்டுள்ளது."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"பணிப் பயன்முறை முடக்கப்பட்டது."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"பணிப் பயன்முறை இயக்கப்பட்டது."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"திரை பிரகாசம்"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G டேட்டா இடைநிறுத்தப்பட்டது"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G டேட்டா இடைநிறுத்தப்பட்டது"</string>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings_tv.xml b/packages/SystemUI/res/values-ta-rIN/strings_tv.xml
new file mode 100644
index 0000000..d4d661a
--- /dev/null
+++ b/packages/SystemUI/res/values-ta-rIN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIPஐ மூடு"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"முழுத்திரை"</string>
+ <string name="pip_play" msgid="674145557658227044">"இயக்கு"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"இடைநிறுத்து"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIPஐக் கட்டுப்படுத்த, "<b>"முகப்பைப்"</b>" பிடித்திருக்கவும்"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIPஐக் கட்டுப்படுத்த, முகப்புப்\nபொத்தானை அழுத்தவும், பிடிக்கவும்"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"சரி"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index d02b353..c7f9b42 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"స్క్రీన్షాట్ క్యాప్చర్ చేయబడింది."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"మీ స్క్రీన్షాట్ను వీక్షించడానికి తాకండి."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"స్క్రీన్షాట్ను క్యాప్చర్ చేయడం సాధ్యపడలేదు."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"పరిమిత నిల్వ స్థలం కారణంగా స్క్రీన్షాట్ను సేవ్ చేయడం సాధ్యపడదు."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"స్క్రీన్షాట్లు తీయడానికి అనువర్తనం లేదా మీ సంస్థ అనుమతించలేదు."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB ఫైల్ బదిలీ ఎంపికలు"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"పని మోడ్ ఆన్లో ఉంది."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"పని మోడ్ ఆఫ్ చేయబడింది."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"పని మోడ్ ఆన్ చేయబడింది."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ప్రదర్శన ప్రకాశం"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G డేటా పాజ్ చేయబడింది"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G డేటా పాజ్ చేయబడింది"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings_tv.xml b/packages/SystemUI/res/values-te-rIN/strings_tv.xml
new file mode 100644
index 0000000..4c18743
--- /dev/null
+++ b/packages/SystemUI/res/values-te-rIN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIPని మూసివేయి"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"పూర్తి స్క్రీన్"</string>
+ <string name="pip_play" msgid="674145557658227044">"ప్లే చేయి"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"పాజ్ చేయి"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP నియం. "<b>"HOME"</b>"నొక్కిఉంచండి"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIPని నియంత్రించడానికి HOME\nబటన్ను నొక్కి ఉంచండి"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"అర్థమైంది"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 9b1ea74..98029ed 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"จับภาพหน้าจอแล้ว"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"แตะเพื่อดูภาพหน้าจอของคุณ"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"ไม่สามารถจับภาพหน้าจอ"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"พบปัญหาขณะกำลังบันทึกภาพหน้าจอ"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"ไม่สามารถบันทึกภาพหน้าจอเนื่องจากพื้นที่เก็บข้อมูลมีจำกัด"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"แอปหรือองค์กรของคุณไม่อนุญาตให้จับภาพหน้าจอ"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"ตัวเลือกการถ่ายโอนไฟล์ USB"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"โหมดการทำงานเปิดอยู่"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"ปิดโหมดการทำงานแล้ว"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"เปิดโหมดการทำงานแล้ว"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ความสว่างของหน้าจอ"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"หยุดการใช้ข้อมูล 2G-3G ชั่วคราวแล้ว"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"หยุดการใช้ข้อมูล 4G ชั่วคราวแล้ว"</string>
diff --git a/packages/SystemUI/res/values-th/strings_tv.xml b/packages/SystemUI/res/values-th/strings_tv.xml
new file mode 100644
index 0000000..f1c24ec
--- /dev/null
+++ b/packages/SystemUI/res/values-th/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"ปิด PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"เต็มหน้าจอ"</string>
+ <string name="pip_play" msgid="674145557658227044">"เล่น"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"หยุดชั่วคราว"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"กด "<b>"HOME"</b>" ค้างไว้เพื่อควบคุม PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"กดปุ่ม HOME ค้างไว้\nเพื่อควบคุม PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"รับทราบ"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index d131c84..d93fd96 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Nakuha ang screenshot."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Pindutin upang tingnan ang iyong screenshot."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Hindi makuha ang screenshot."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Nagkaroon ng problema habang sine-save ang screenshot."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Hindi ma-save ang screenshot dahil sa limitadong espasyo ng storage."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Hindi pinapayagan ng app o ng iyong organisasyon ang pagkuha ng mga screenshot."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Opsyon paglipat ng USB file"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Naka-on ang work mode."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Na-off ang work mode."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Na-on ang work mode."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Liwanag ng display"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Naka-pause ang 2G-3G data"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Naka-pause ang 4G data"</string>
diff --git a/packages/SystemUI/res/values-tl/strings_tv.xml b/packages/SystemUI/res/values-tl/strings_tv.xml
new file mode 100644
index 0000000..85a4f5b
--- /dev/null
+++ b/packages/SystemUI/res/values-tl/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Isara ang PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Full screen"</string>
+ <string name="pip_play" msgid="674145557658227044">"I-play"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"I-pause"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"I-hold ang "<b>"HOME"</b>" para makontrol ang PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"I-hold ang button na HOME\nupang makontrol ang PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index fd9e14c..12be4c3 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Ekran görüntüsü alındı."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Ekran görüntünüzü izlemek için dokunun."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Ekran görüntüsü alınamadı."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Depolama alanı sınırlı olduğundan ekran görüntüsü kaydedilemiyor."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Uygulama veya kuruluşunuz, ekran görüntüsü alınmasına izin vermiyor."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB dosya aktarım seçenekleri"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Çalışma modu açık."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Çalışma modu kapatıldı."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Çalışma modu açıldı."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Ekran parlaklığı"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G veri kullanımı duraklatıldı"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G veri kullanımı duraklatıldı"</string>
diff --git a/packages/SystemUI/res/values-tr/strings_tv.xml b/packages/SystemUI/res/values-tr/strings_tv.xml
new file mode 100644
index 0000000..ea136b1
--- /dev/null
+++ b/packages/SystemUI/res/values-tr/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP\'yi kapat"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Tam ekran"</string>
+ <string name="pip_play" msgid="674145557658227044">"Oynat"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Duraklat"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP\'yi kontrol etmek için "<b>"ANA EKRAN"</b>"\'ı basılı tutun"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP\'yi kontrol etmek için\nANA EKRAN düğmesini basılı tutun"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Anladım"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index e5bf06d..e7721f1 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -75,6 +75,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Знімок екрана зроблено."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Торкніться, щоб переглянути знімок екрана."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Не вдалося зробити знімок екрана."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Додаток або ваша організація не дозволяють робити знімки екрана."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Парам.передав.файлів через USB"</string>
@@ -222,6 +224,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Робочий режим увімкнено."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Робочий режим вимкнено."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Робочий режим увімкнено."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Яскравість дисплея"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Передавання даних 2G–3G призупинено"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Передавання даних 4G призупинено"</string>
diff --git a/packages/SystemUI/res/values-uk/strings_tv.xml b/packages/SystemUI/res/values-uk/strings_tv.xml
new file mode 100644
index 0000000..22bcf52
--- /dev/null
+++ b/packages/SystemUI/res/values-uk/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Закрити PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"На весь екран"</string>
+ <string name="pip_play" msgid="674145557658227044">"Відтворити"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Призупинити"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Щоб керувати PIP, утримуйте кнопку "<b>"ГОЛОВНИЙ ЕКРАН"</b></string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Щоб керувати PIP,\nутримуйте кнопку \"ГОЛОВНИЙ ЕКРАН\""</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 7a10732..dd76828 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"اسکرین شاٹ کیپچر کیا گیا۔"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"اپنے اسکرین شاٹ دیکھنے کیلئے چھوئیں۔"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"اسکرین شاٹ کیپچر نہیں کر سکے۔"</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"محدود اسٹوریج جگہ کی وجہ سے اسکرین شاٹس نہیں لئے جا سکتے۔"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"ایپ یا آپ کی تنظیم کی جانب سے اسکرین شاٹس لینے کی اجازت نہیں ہے۔"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB فائل منتقل کرنیکے اختیارات"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"کام موڈ آن ہے۔"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"کام موڈ آف ہو گیا۔"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"کام موڈ آن ہو گیا۔"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"ڈسپلے کی چمک"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ڈیٹا موقوف کر دیا گیا"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ڈیٹا موقوف کر دیا گیا"</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings_tv.xml b/packages/SystemUI/res/values-ur-rPK/strings_tv.xml
new file mode 100644
index 0000000..4929f81
--- /dev/null
+++ b/packages/SystemUI/res/values-ur-rPK/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP بند کریں"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"فُل اسکرین"</string>
+ <string name="pip_play" msgid="674145557658227044">"چلائیں"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"موقوف کریں"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"PIP کنٹرول کرنے کیلئے "<b>"ہوم"</b>" پکڑے رکھیں"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"PIP کنٹرول کرنے کیلئے\nہوم بٹن دبائیں اور پکڑے رکھیں"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"سمجھ آ گئی"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 79729dc..21400b3 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Skrinshot saqlandi."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Ko‘rish uchun bu yerga bosing."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Skrinshot saqlanmadi."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Xotirada joy kamligi uchun skrinshotni saqlab bo‘lmadi."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Ilova yoki tashkilotingiz skrinshot olishni taqiqlagan."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB fayl ko‘chirish moslamalari"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Ish rejimi yoniq."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Ish rejimi o‘chirib qo‘yildi."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Ishchi rejim yoqildi."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Ekran yorqinligi"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G internet to‘xtatib qo‘yildi"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G internet to‘xtatib qo‘yildi"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings_tv.xml b/packages/SystemUI/res/values-uz-rUZ/strings_tv.xml
new file mode 100644
index 0000000..d6ffb67
--- /dev/null
+++ b/packages/SystemUI/res/values-uz-rUZ/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"PIP’ni yopish"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"To‘liq ekran"</string>
+ <string name="pip_play" msgid="674145557658227044">"Ijro"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Pauza"</string>
+ <string name="pip_hold_home" msgid="340086535668778109"><b>"BOSHIGA"</b>" tugmasini bosib turib, PIP’ni boshqaring"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"BOSHIGA tugmasini bosib turib,\nPIP’ni boshqaring"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index e8babe5..8f947af 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Đã chụp ảnh màn hình."</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Chạm để xem ảnh chụp màn hình của bạn."</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Không thể chụp ảnh màn hình."</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"Đã gặp phải sự cố khi đang lưu ảnh chụp màn hình."</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Không thể lưu ảnh chụp màn hình do giới hạn dung lượng bộ nhớ."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Ứng dụng hoặc tổ chức của bạn không cho phép chụp ảnh màn hình."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Tùy chọn truyền tệp USB"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Chế độ làm việc bật."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Chế độ làm việc đã tắt."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Chế độ làm việc đã bật."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Độ sáng màn hình"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"Đã tạm dừng dữ liệu 2G-3G"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Đã tạm dừng dữ liệu 4G"</string>
@@ -303,8 +308,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"khóa màn hình"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Không thể khởi động <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
- <skip />
+ <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> bị tắt ở chế độ an toàn."</string>
<string name="recents_history_button_label" msgid="5153358867807604821">"Lịch sử"</string>
<string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Xóa"</string>
<string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ứng dụng này không hỗ trợ chế độ nhiều cửa sổ"</string>
diff --git a/packages/SystemUI/res/values-vi/strings_tv.xml b/packages/SystemUI/res/values-vi/strings_tv.xml
new file mode 100644
index 0000000..48ba444
--- /dev/null
+++ b/packages/SystemUI/res/values-vi/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Đóng PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Toàn màn hình"</string>
+ <string name="pip_play" msgid="674145557658227044">"Phát"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Tạm dừng"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Giữ "<b>"HOME"</b>" để đ.khiển PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Nhấn và giữ nút HOME\nđể điều khiển PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"OK"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index f828306..74d787f 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"已抓取屏幕截图。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"触摸可查看您的屏幕截图。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"无法抓取屏幕截图。"</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"由于存储空间有限,无法保存屏幕截图。"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"此应用或贵单位不允许进行屏幕截图。"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB文件传输选项"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"工作模式开启。"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"工作模式已关闭。"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"工作模式已开启。"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"屏幕亮度"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G 数据网络已暂停使用"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G 数据网络已暂停使用"</string>
@@ -458,10 +464,10 @@
<string name="show_silently" msgid="6841966539811264192">"显示通知,但不发出提示音"</string>
<string name="block" msgid="2734508760962682611">"屏蔽所有通知"</string>
<string name="do_not_silence" msgid="6878060322594892441">"不静音"</string>
- <string name="do_not_silence_block" msgid="4070647971382232311">"不静音或不屏蔽"</string>
+ <string name="do_not_silence_block" msgid="4070647971382232311">"不静音也不屏蔽"</string>
<string name="tuner_full_importance_settings" msgid="8103289238676424226">"显示完整的重要性设置"</string>
<string name="blocked_importance" msgid="5198578988978234161">"屏蔽"</string>
- <string name="min_importance" msgid="1901894910809414782">"最低重要性"</string>
+ <string name="min_importance" msgid="1901894910809414782">"重要性最低"</string>
<string name="low_importance" msgid="4109929986107147930">"重要性:低"</string>
<string name="default_importance" msgid="8192107689995742653">"重要性:一般"</string>
<string name="high_importance" msgid="1527066195614050263">"重要性:高"</string>
@@ -470,8 +476,8 @@
<string name="notification_importance_min" msgid="1938190340516905748">"在通知列表底部显示,但不发出提示音"</string>
<string name="notification_importance_low" msgid="3657252049508213048">"显示这些通知,但不发出提示音"</string>
<string name="notification_importance_default" msgid="4466466472622442175">"允许这些通知发出提示音"</string>
- <string name="notification_importance_high" msgid="2135428926525093825">"在屏幕上短暂显示,并发出提示音"</string>
- <string name="notification_importance_max" msgid="5806278962376556491">"在通知列表顶部显示,同时在屏幕上短暂显示,并发出提示音"</string>
+ <string name="notification_importance_high" msgid="2135428926525093825">"在屏幕上短暂显示,并允许发出提示音"</string>
+ <string name="notification_importance_max" msgid="5806278962376556491">"在通知列表顶部显示,同时在屏幕上短暂显示,并允许发出提示音"</string>
<string name="notification_more_settings" msgid="816306283396553571">"更多设置"</string>
<string name="notification_done" msgid="5279426047273930175">"完成"</string>
<string name="color_and_appearance" msgid="1254323855964993144">"颜色和外观"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings_tv.xml b/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
new file mode 100644
index 0000000..860df35
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"关闭 PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"全屏"</string>
+ <string name="pip_play" msgid="674145557658227044">"播放"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"暂停"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"按住"<b>"主屏幕"</b>"即可控制 PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"按住主屏幕\n按钮即可控制 PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"知道了"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index bd379b7..c7b2fff 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -73,6 +73,7 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"已擷取螢幕畫面。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"輕觸即可查看螢幕擷取畫面。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"無法擷取螢幕畫面。"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="7887826345701753830">"儲存螢幕擷圖時發生問題。"</string>
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"由於儲存空間有限,因此無法儲存螢幕擷取畫面。"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"此應用程式或您的機構禁止擷取螢幕畫面。"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB 檔案傳輸選項"</string>
@@ -220,6 +221,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"工作模式已開啟。"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"已關閉工作模式。"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"已開啟工作模式。"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"顯示光暗度"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"已暫停 2G-3G 數據"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"已暫停 4G 數據"</string>
@@ -506,9 +511,9 @@
<string name="headset" msgid="4534219457597457353">"耳機"</string>
<string name="accessibility_status_bar_headphones" msgid="9156307120060559989">"已連接至耳機"</string>
<string name="accessibility_status_bar_headset" msgid="8666419213072449202">"已連接至耳機"</string>
- <string name="data_saver" msgid="5037565123367048522">"數據節省程式"</string>
- <string name="accessibility_data_saver_on" msgid="8454111686783887148">"數據節省程式已開啟"</string>
- <string name="accessibility_data_saver_off" msgid="8841582529453005337">"數據節省程式已關閉"</string>
+ <string name="data_saver" msgid="5037565123367048522">"數據節省模式"</string>
+ <string name="accessibility_data_saver_on" msgid="8454111686783887148">"數據節省模式已開啟"</string>
+ <string name="accessibility_data_saver_off" msgid="8841582529453005337">"數據節省模式已關閉"</string>
<string name="switch_bar_on" msgid="1142437840752794229">"開啟"</string>
<string name="switch_bar_off" msgid="8803270596930432874">"關閉"</string>
<string name="nav_bar" msgid="1993221402773877607">"導覽列"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings_tv.xml b/packages/SystemUI/res/values-zh-rHK/strings_tv.xml
new file mode 100644
index 0000000..8e5a2df
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rHK/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"關閉 PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"全螢幕"</string>
+ <string name="pip_play" msgid="674145557658227044">"播放"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"暫停"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"按住"<b>"主按鈕"</b>"即可控制 PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"按住主按鈕\n即可控制 PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"知道了"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index d580b28..636f2fb 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"已拍攝螢幕擷取畫面。"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"輕觸即可查看螢幕擷取畫面。"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"無法拍攝螢幕擷取畫面。"</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"由於儲存空間有限,因此無法儲存螢幕擷取畫面。"</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"這個應用程式或貴機構禁止擷取螢幕畫面。"</string>
<string name="usb_preference_title" msgid="6551050377388882787">"USB 檔案傳輸選項"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"工作模式已開啟。"</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"工作模式已關閉。"</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"工作模式已開啟。"</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"螢幕亮度"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"已暫停 2G-3G 數據連線"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"已暫停 4G 數據連線"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings_tv.xml b/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
new file mode 100644
index 0000000..60edac2
--- /dev/null
+++ b/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"關閉子母畫面"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"全螢幕"</string>
+ <string name="pip_play" msgid="674145557658227044">"播放"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"暫停"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"按住「主畫面」"<b></b>"按鈕即可控制子母畫面"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"按住「主畫面」按鈕\n即可控制子母畫面"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"我知道了"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 9382b26..8d2a51b 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -73,6 +73,8 @@
<string name="screenshot_saved_title" msgid="6461865960961414961">"Umfanekiso weskrini uqoshiwe"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"Thinta ukubona imifanekiso yakho yeskrini"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"Yehlulekile ukulondoloza umfanekiso weskrini."</string>
+ <!-- no translation found for screenshot_failed_to_save_unknown_text (7887826345701753830) -->
+ <skip />
<string name="screenshot_failed_to_save_text" msgid="2592658083866306296">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe."</string>
<string name="screenshot_failed_to_capture_text" msgid="7602391003979898374">"Ukuthatha izithombe-skrini akuvunyelwe uhlelo lokusebenza noma inhlangano yakho."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"Okukhethwa kokudluliswa kwefayela ye-USB"</string>
@@ -220,6 +222,10 @@
<string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Imodi yomsebenzi ivuliwe."</string>
<string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Imodi yomsebenzi ivaliwe."</string>
<string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Imodi yomsebenzi ivuliwe."</string>
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
+ <skip />
+ <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
+ <skip />
<string name="accessibility_brightness" msgid="8003681285547803095">"Bonisa ukukhanya"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G idatha imisiwe"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G idatha imisiwe"</string>
diff --git a/packages/SystemUI/res/values-zu/strings_tv.xml b/packages/SystemUI/res/values-zu/strings_tv.xml
new file mode 100644
index 0000000..b901f90
--- /dev/null
+++ b/packages/SystemUI/res/values-zu/strings_tv.xml
@@ -0,0 +1,29 @@
+<?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"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pip_close" msgid="3480680679023423574">"Vala i-PIP"</string>
+ <string name="pip_fullscreen" msgid="8604643018538487816">"Iskrini esigcwele"</string>
+ <string name="pip_play" msgid="674145557658227044">"Dlala"</string>
+ <string name="pip_pause" msgid="8412075640017218862">"Misa isikhashana"</string>
+ <string name="pip_hold_home" msgid="340086535668778109">"Bamba "<b>"IKHAYA"</b>" ukuze ulawule i-PIP"</string>
+ <string name="pip_onboarding_description" msgid="2627737116380318292">"Cindezela uphinde ubambe inkinobho YASEKHAYA\nukuze ulawule i-PIP"</string>
+ <string name="pip_onboarding_button" msgid="3957426748484904611">"Ngiyezwa"</string>
+</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 30acc72a..d65ab04 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -165,4 +165,7 @@
<!-- Keyboard shortcuts colors -->
<color name="ksh_system_group_color">#ff00bcd4</color>
<color name="ksh_application_group_color">#fff44336</color>
+
+ <!-- Background color of edit overflow -->
+ <color name="qs_edit_overflow_bg">#455A64</color>
</resources>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 6f4c983..af99aae 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -18,7 +18,5 @@
-->
<resources>
<color name="recents_tv_card_background_color">#FF37474F</color>
- <color name="recents_tv_card_title_text_color">#FFEEEEEE</color>
- <color name="recents_tv_card_content_text_color">#99EEEEEE</color>
- <color name="recents_tv_card_source_text_color">#99EEEEEE</color>
+ <color name="recents_tv_card_title_text_color">#CCEEEEEE</color>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8a7f90b..6a9f456 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -58,19 +58,19 @@
<item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item>
<!-- Height of a small notification in the status bar-->
- <dimen name="notification_min_height">86dp</dimen>
+ <dimen name="notification_min_height">92dp</dimen>
<!-- Height of a small notification in the status bar which was used before android N -->
<dimen name="notification_min_height_legacy">64dp</dimen>
<!-- Height of a large notification in the status bar -->
- <dimen name="notification_max_height">276dp</dimen>
+ <dimen name="notification_max_height">284dp</dimen>
<!-- Height of a heads up notification in the status bar for legacy custom views -->
<dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
<!-- Height of a heads up notification in the status bar -->
- <dimen name="notification_max_heads_up_height">141dp</dimen>
+ <dimen name="notification_max_heads_up_height">148dp</dimen>
<!-- Height of a the summary ("more card") notification on keyguard. -->
<dimen name="notification_summary_height">44dp</dimen>
@@ -178,7 +178,7 @@
<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">150dp</dimen>
+ <dimen name="qs_header_gear_translation">16dp</dimen>
<dimen name="qs_page_indicator_size">12dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
@@ -292,6 +292,12 @@
<!-- The amount to allow the stack to overscroll. -->
<dimen name="recents_stack_overscroll">24dp</dimen>
+ <!-- The size of the initial peek area at the top of the stack (below the status bar). -->
+ <dimen name="recents_initial_top_peek_size">8dp</dimen>
+
+ <!-- The size of the initial peek area at the bottom of the stack (above the nav bar). -->
+ <dimen name="recents_initial_bottom_peek_size">100dp</dimen>
+
<!-- The size of the peek area at the top of the stack (below the status bar). -->
<dimen name="recents_layout_focused_top_peek_size">@dimen/recents_history_button_height</dimen>
@@ -304,6 +310,9 @@
<!-- The padding between freeform workspace tasks -->
<dimen name="recents_freeform_workspace_task_padding">8dp</dimen>
+ <!-- The offsets the tasks animate from when recents is launched while docking -->
+ <dimen name="recents_task_view_launched_while_docking_offset">144dp</dimen>
+
<!-- Space reserved for the cards behind the top card in the bottom stack -->
<dimen name="bottom_stack_peek_amount">12dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index bf32cc7..b589110 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -18,15 +18,21 @@
-->
<resources>
<!-- Dimens for recents card in the recents view on tv -->
- <dimen name="recents_tv_card_width">150dip</dimen>
- <dimen name="recents_tv_card_height">85dip</dimen>
- <dimen name="recents_tv_card_extra_badge_size">16dip</dimen>
+ <dimen name="recents_tv_card_width">268dip</dimen>
+ <dimen name="recents_tv_card_height">151dip</dimen>
+ <dimen name="recents_tv_card_extra_badge_size">20dip</dimen>
+ <dimen name="recents_tv_banner_width">114dip</dimen>
+ <dimen name="recents_tv_banner_height">64dip</dimen>
+ <dimen name="recents_tv_banner_margin_top">16dip</dimen>
+ <dimen name="recents_tv_icon_padding_bottom">8dip</dimen>
+ <dimen name="recents_tv_icon_padding_end">12dip</dimen>
+ <dimen name="recents_tv_text_padding_bottom">12dip</dimen>
<!-- Padding for grid view in recents view on tv -->
<dimen name="recents_tv_grid_row_padding">56dip</dimen>
<dimen name="recents_tv_gird_row_top_padding">57dip</dimen>
- <dimen name="recents_tv_grid_max_row_height">200dip</dimen>
- <dimen name="recents_tv_gird_card_spacing">8dip</dimen>
+ <dimen name="recents_tv_grid_max_row_height">268dip</dimen>
+ <dimen name="recents_tv_gird_card_spacing">20dip</dimen>
<!-- Values for focus animation -->
<dimen name="recents_tv_unselected_item_z">6dp</dimen>
@@ -34,4 +40,7 @@
<!-- Extra space around the PIP and its outline in PIP onboarding activity -->
<dimen name="tv_pip_bounds_space">3dp</dimen>
+
+ <!-- Values for text on recents cards on tv -->
+ <dimen name="recents_tv_title_text_size">12sp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index b9eee2e..9697ea6 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -48,6 +48,7 @@
<item type="id" name="transformation_start_y_tag"/>
<item type="id" name="transformation_start_scale_x_tag"/>
<item type="id" name="transformation_start_scale_y_tag"/>
+ <item type="id" name="custom_background_color"/>
<!-- Whether the icon is from a notification for which targetSdk < L -->
<item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1f239c3..8af413c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -185,7 +185,9 @@
<string name="screenshot_saved_text">Touch to view your screenshot.</string>
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
<string name="screenshot_failed_title">Couldn\'t capture screenshot.</string>
- <!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
+ <!-- Notification text displayed when we fail to save a screenshot for unknown reasons. [CHAR LIMIT=100] -->
+ <string name="screenshot_failed_to_save_unknown_text">Problem encountered while saving screenshot.</string>
+ <!-- Notification text displayed when we fail to save a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_save_text">Can\'t save screenshot due to limited storage space.</string>
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
<string name="screenshot_failed_to_capture_text">Taking screenshots is not allowed by the app or your organization.</string>
@@ -523,6 +525,10 @@
<string name="accessibility_quick_settings_work_mode_changed_off">Work mode turned off.</string>
<!-- Announcement made when the work mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_work_mode_changed_on">Work mode turned on.</string>
+ <!-- Announcement made when the Data Saver changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_data_saver_changed_off">Data Saver turned off.</string>
+ <!-- Announcement made when the Data Saver changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_data_saver_changed_on">Data Saver turned on.</string>
<!-- Content description of the display brightness slider (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_brightness">Display brightness</string>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 4f382ea..0e1fe8f 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -17,25 +17,25 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Picture-in-Picture menu -->
+ <!-- Picture-in-Picture (PIP) menu -->
<eat-comment />
- <!-- Button to close PIP on PIP UI -->
- <string name="pip_close" translatable="false">Close PIP</string>
- <!-- Button to move PIP screen to the fullscreen on PIP UI -->
- <string name="pip_fullscreen" translatable="false">Full screen</string>
- <!-- Button to play the current media on PIP UI -->
- <string name="pip_play" translatable="false">Play</string>
- <!-- Button to pause the current media on PIP UI -->
- <string name="pip_pause" translatable="false">Pause</string>
- <!-- Button to close PIP overlay menu on PIP UI -->
- <string name="pip_cancel" translatable="false">Cancel</string>
- <!-- Overlay text on PIP -->
- <string name="pip_hold_home" translatable="false">Hold HOME to control PIP</string>
-
- <!-- Picture-in-Picture onboarding screen -->
+ <!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=16] -->
+ <string name="pip_close">Close PIP</string>
+ <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=16] -->
+ <string name="pip_fullscreen">Full screen</string>
+ <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=16] -->
+ <string name="pip_play">Play</string>
+ <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=16] -->
+ <string name="pip_pause">Pause</string>
+ <!-- Overlay text on picture-in-picture (PIP) to indicate that longpress HOME key to control PIP [CHAR LIMIT=52] -->
+ <string name="pip_hold_home">Hold <b>HOME</b> to control PIP</string>
+ <!-- Picture-in-Picture (PIP) onboarding screen -->
<eat-comment />
- <!-- Description for onboarding screen. -->
- <string name="pip_onboarding_description" translatable="false">Press and hold the HOME\nbutton to close or control it</string>
- <!-- Button to close onboarding screen. -->
- <string name="pip_onboarding_button" translatable="false">Got it</string>
+ <!-- Description for picture-in-picture (PIP) onboarding screen to indicate that longpress HOME key to control PIP. [CHAR LIMIT=NONE] -->
+ <string name="pip_onboarding_description">Press and hold the HOME button to control PIP</string>
+ <!-- Button to close picture-in-picture (PIP) onboarding screen. -->
+ <string name="pip_onboarding_button">Got it</string>
+ <!-- Font for Recents -->
+ <!-- DO NOT TRANSLATE -->
+ <string name="font_roboto_regular" translatable="false">sans-serif</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 11df681..2660926 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -323,6 +323,10 @@
<item name="android:textSize">16sp</item>
</style>
+ <style name="TextAppearance.NotificationGuts.Radio">
+ <item name="android:alpha">.87</item>
+ </style>
+
<style name="TextAppearance.NotificationGuts.Button">
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">true</item>
@@ -335,4 +339,8 @@
<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>
+
</resources>
diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml
index 3f0caab..263e1a4 100644
--- a/packages/SystemUI/res/values/styles_tv.xml
+++ b/packages/SystemUI/res/values/styles_tv.xml
@@ -22,4 +22,11 @@
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
+
+ <style name="RecentsTvTheme.Wallpaper" parent="@android:style/Theme.Material.NoActionBar.Overscan">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 051921a..358674c 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -78,6 +78,7 @@
private float mOldHeight;
private float mNaturalHeight;
private float mInitialTouchFocusY;
+ private float mInitialTouchX;
private float mInitialTouchY;
private float mInitialTouchSpan;
private float mLastFocusY;
@@ -291,7 +292,8 @@
}
if (mWatchingForPull) {
final float yDiff = ev.getRawY() - mInitialTouchY;
- if (yDiff > mTouchSlop) {
+ final float xDiff = ev.getRawX() - mInitialTouchX;
+ if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) {
if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
mWatchingForPull = false;
if (mResizedView != null && !isFullyExpanded(mResizedView)) {
@@ -316,6 +318,7 @@
mWatchingForPull = false;
}
mInitialTouchY = ev.getY();
+ mInitialTouchX = ev.getX();
break;
case MotionEvent.ACTION_CANCEL:
@@ -409,12 +412,14 @@
mWatchingForPull = mScrollAdapter != null &&
isInside(mScrollAdapter.getHostView(), x, y);
mResizedView = findView(x, y);
+ mInitialTouchX = ev.getX();
mInitialTouchY = ev.getY();
break;
case MotionEvent.ACTION_MOVE: {
if (mWatchingForPull) {
final float yDiff = ev.getRawY() - mInitialTouchY;
- if (yDiff > mTouchSlop) {
+ final float xDiff = ev.getRawX() - mInitialTouchX;
+ if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) {
if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
mWatchingForPull = false;
if (mResizedView != null && !isFullyExpanded(mResizedView)) {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index f6dcc11..81ba23f 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -32,6 +32,7 @@
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.statusbar.FlingAnimationUtils;
public class SwipeHelper implements Gefingerpoken {
static final String TAG = "com.android.systemui.SwipeHelper";
@@ -58,6 +59,7 @@
private float mMinSwipeProgress = 0f;
private float mMaxSwipeProgress = 1f;
+ private FlingAnimationUtils mFlingAnimationUtils;
private float mPagingTouchSlop;
private Callback mCallback;
private Handler mHandler;
@@ -95,6 +97,8 @@
mFalsingThreshold = context.getResources().getDimensionPixelSize(
R.dimen.swipe_helper_falsing_threshold);
mFalsingManager = FalsingManager.getInstance(context);
+ mFlingAnimationUtils = new FlingAnimationUtils(context,
+ MAX_ESCAPE_ANIMATION_DURATION / 1000f /* maxLengthSeconds */);
}
public void setLongPressListener(LongPressListener listener) {
@@ -320,7 +324,8 @@
* @param velocity The desired pixels/second speed at which the view should move
*/
public void dismissChild(final View view, float velocity) {
- dismissChild(view, velocity, null, 0, false, 0);
+ dismissChild(view, velocity, null /* endAction */, 0 /* delay */,
+ velocity == 0 /* useAccelerateInterpolator */, 0 /* fixedDuration */);
}
/**
@@ -377,10 +382,11 @@
}
if (useAccelerateInterpolator) {
anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ anim.setDuration(duration);
} else {
- anim.setInterpolator(Interpolators.LINEAR);
+ mFlingAnimationUtils.applyDismissing(anim, getTranslation(animView),
+ newPos, velocity, getSize(animView));
}
- anim.setDuration(duration);
if (delay > 0) {
anim.setStartDelay(delay);
}
@@ -511,35 +517,16 @@
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- if (mCurrView != null) {
- float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
- mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
- float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
- float velocity = getVelocity(mVelocityTracker);
- float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
+ if (mCurrView == null) {
+ break;
+ }
+ mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, getMaxVelocity());
+ float velocity = getVelocity(mVelocityTracker);
- float translation = getTranslation(mCurrView);
- // Decide whether to dismiss the current view
- boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
- Math.abs(translation) > 0.4 * getSize(mCurrView);
- boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
- (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
- (velocity > 0) == (translation > 0);
- boolean falsingDetected = mCallback.isAntiFalsingNeeded();
-
- if (mFalsingManager.isClassiferEnabled()) {
- falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
- } else {
- falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
- }
-
- boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
- && !falsingDetected && (childSwipedFastEnough || childSwipedFarEnough)
- && ev.getActionMasked() == MotionEvent.ACTION_UP;
-
- if (dismissChild) {
+ if (!handleUpEvent(ev, mCurrView, velocity, getTranslation(mCurrView))) {
+ if (isDismissGesture(ev)) {
// flingadingy
- dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
+ dismissChild(mCurrView, swipedFastEnough() ? velocity : 0f);
} else {
// snappity
mCallback.onDragCancelled(mCurrView);
@@ -556,6 +543,46 @@
return (int) (mFalsingThreshold * factor);
}
+ private float getMaxVelocity() {
+ return MAX_DISMISS_VELOCITY * mDensityScale;
+ }
+
+ protected float getEscapeVelocity() {
+ return SWIPE_ESCAPE_VELOCITY * mDensityScale;
+ }
+
+ protected boolean swipedFarEnough() {
+ float translation = getTranslation(mCurrView);
+ return DISMISS_IF_SWIPED_FAR_ENOUGH && Math.abs(translation) > 0.4 * getSize(mCurrView);
+ }
+
+ protected boolean isDismissGesture(MotionEvent ev) {
+ boolean falsingDetected = mCallback.isAntiFalsingNeeded();
+ if (mFalsingManager.isClassiferEnabled()) {
+ falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
+ } else {
+ falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
+ }
+ return !falsingDetected && (swipedFastEnough() || swipedFarEnough())
+ && ev.getActionMasked() == MotionEvent.ACTION_UP
+ && mCallback.canChildBeDismissed(mCurrView);
+ }
+
+ protected boolean swipedFastEnough() {
+ float velocity = getVelocity(mVelocityTracker);
+ float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
+ float translation = getTranslation(mCurrView);
+ boolean ret = (Math.abs(velocity) > getEscapeVelocity()) &&
+ (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
+ (velocity > 0) == (translation > 0);
+ return ret;
+ }
+
+ protected boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
+ float translation) {
+ return false;
+ }
+
public interface Callback {
View getChildAtPosition(MotionEvent ev);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 913b2b3..2e94bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -23,7 +23,6 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.shortcut.ShortcutKeyDispatcher;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -72,8 +71,8 @@
}
public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
- View headsUpScrim, boolean scrimSrcEnabled) {
- return new ScrimController(scrimBehind, scrimInFront, headsUpScrim, scrimSrcEnabled);
+ View headsUpScrim) {
+ return new ScrimController(scrimBehind, scrimInFront, headsUpScrim);
}
public <T> T createInstance(Class<T> classType) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
index 86bea87..bad739fd 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AccelerationClassifier.java
@@ -36,6 +36,11 @@
}
@Override
+ public String getTag() {
+ return "ACC";
+ }
+
+ @Override
public void onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
index dba731a..526e5fa 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java
@@ -54,6 +54,11 @@
}
@Override
+ public String getTag() {
+ return "ANG";
+ }
+
+ @Override
public void onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 89d20de..cb761a9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -48,4 +48,6 @@
*/
public void onSensorChanged(SensorEvent event) {
}
+
+ public abstract String getTag();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
index 299d0e3..610e219 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java
@@ -25,6 +25,11 @@
}
@Override
+ public String getTag() {
+ return "DIR";
+ }
+
+ @Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
Point firstPoint = stroke.getPoints().get(0);
Point lastPoint = stroke.getPoints().get(stroke.getPoints().size() - 1);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
index 8924694..77fda20 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DurationCountClassifier.java
@@ -25,6 +25,11 @@
}
@Override
+ public String getTag() {
+ return "DUR";
+ }
+
+ @Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
return DurationCountEvaluator.evaluate(stroke.getDurationSeconds() / stroke.getCount());
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
index 78bc0dd..de8a188 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointLengthClassifier.java
@@ -24,6 +24,11 @@
}
@Override
+ public String getTag() {
+ return "END_LNGTH";
+ }
+
+ @Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
return EndPointLengthEvaluator.evaluate(stroke.getEndPointLength());
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
index 652d969..9b6ddc8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/EndPointRatioClassifier.java
@@ -26,6 +26,11 @@
}
@Override
+ public String getTag() {
+ return "END_RTIO";
+ }
+
+ @Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
float ratio;
if (stroke.getTotalLength() == 0.0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
new file mode 100644
index 0000000..1338d9d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.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.classifier;
+
+import android.app.ActivityThread;
+import android.app.Application;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Keeps track of interesting falsing data.
+ *
+ * By default the log only gets collected on userdebug builds. To turn it on on user:
+ * adb shell setprop debug.falsing_log true
+ *
+ * The log gets dumped as part of the SystemUI services. To dump on demand:
+ * adb shell dumpsys activity service com.android.systemui SystemBars | grep -A 999 FALSING | less
+ *
+ * To dump into logcat:
+ * adb shell setprop debug.falsing_logcat true
+ *
+ * To adjust the log buffer size:
+ * adb shell setprop debug.falsing_log_size 200
+ */
+public class FalsingLog {
+ public static final boolean ENABLED = SystemProperties.getBoolean("debug.falsing_log",
+ Build.IS_DEBUGGABLE);
+ private static final boolean LOGCAT = SystemProperties.getBoolean("debug.falsing_logcat",
+ false);
+
+ public static final boolean VERBOSE = false;
+
+ private static final int MAX_SIZE = SystemProperties.getInt("debug.falsing_log_size", 100);
+
+ private static final String TAG = "FalsingLog";
+
+ private final ArrayDeque<String> mLog = new ArrayDeque<>(MAX_SIZE);
+ private final SimpleDateFormat mFormat = new SimpleDateFormat("MM-dd HH:mm:ss", Locale.US);
+
+ private static FalsingLog sInstance;
+
+ private FalsingLog() {
+ }
+
+ public static void v(String tag, String s) {
+ if (!VERBOSE) {
+ return;
+ }
+ if (LOGCAT) {
+ Log.v(TAG, tag + "\t" + s);
+ }
+ log("V", tag, s);
+ }
+
+ public static void i(String tag, String s) {
+ if (LOGCAT) {
+ Log.i(TAG, tag + "\t" + s);
+ }
+ log("I", tag, s);
+ }
+
+ public static void w(String tag, String s) {
+ if (LOGCAT) {
+ Log.w(TAG, tag + "\t" + s);
+ }
+ log("W", tag, s);
+ }
+
+ public static void e(String tag, String s) {
+ if (LOGCAT) {
+ Log.e(TAG, tag + "\t" + s);
+ }
+ log("E", tag, s);
+ }
+
+ public static synchronized void log(String level, String tag, String s) {
+ if (!ENABLED) {
+ return;
+ }
+ if (sInstance == null) {
+ sInstance = new FalsingLog();
+ }
+
+ if (sInstance.mLog.size() >= MAX_SIZE) {
+ sInstance.mLog.removeFirst();
+ }
+ String entry = new StringBuilder().append(sInstance.mFormat.format(new Date()))
+ .append(" ").append(level).append(" ")
+ .append(tag).append(" ").append(s).toString();
+ sInstance.mLog.add(entry);
+ }
+
+ public static synchronized void dump(PrintWriter pw) {
+ pw.println("FALSING LOG:");
+ if (!ENABLED) {
+ pw.println("Disabled, to enable: setprop debug.falsing_log 1");
+ pw.println();
+ return;
+ }
+ if (sInstance == null || sInstance.mLog.isEmpty()) {
+ pw.println("<empty>");
+ pw.println();
+ return;
+ }
+ for (String s : sInstance.mLog) {
+ pw.println(s);
+ }
+ pw.println();
+ }
+
+ public static synchronized void wtf(String tag, String s) {
+ if (!ENABLED) {
+ return;
+ }
+ e(tag, s);
+
+ Application application = ActivityThread.currentApplication();
+ String fileMessage = "";
+ if (Build.IS_DEBUGGABLE && application != null) {
+ File f = new File(application.getDataDir(), "falsing-"
+ + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()) + ".txt");
+ PrintWriter pw = null;
+ try {
+ pw = new PrintWriter(f);
+ dump(pw);
+ pw.close();
+ fileMessage = "Log written to " + f.getAbsolutePath();
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to write falsing log", e);
+ } finally {
+ if (pw != null) {
+ pw.close();
+ }
+ }
+ } else {
+ Log.e(TAG, "Unable to write log, build must be debuggable.");
+ }
+
+ Log.wtf(TAG, tag + " " + s + "; " + fileMessage);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index c09376b..937f7d3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -31,6 +31,8 @@
import com.android.systemui.analytics.DataCollector;
import com.android.systemui.statusbar.StatusBarState;
+import java.io.PrintWriter;
+
/**
* When the phone is locked, listens to touch, sensor and phone events and sends them to
* DataCollector and HumanInteractionClassifier.
@@ -102,8 +104,14 @@
}
private boolean shouldSessionBeActive() {
- return isEnabled() && mScreenOn &&
- (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED);
+ if (FalsingLog.ENABLED && FalsingLog.VERBOSE)
+ FalsingLog.v("shouldBeActive", new StringBuilder()
+ .append("enabled=").append(isEnabled() ? 1 : 0)
+ .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
+ .append(" mState=").append(StatusBarState.toShortString(mState))
+ .toString()
+ );
+ return isEnabled() && mScreenOn && (mState == StatusBarState.KEYGUARD);
}
private boolean sessionEntrypoint() {
@@ -122,6 +130,9 @@
}
private void onSessionStart() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled());
+ }
mBouncerOn = false;
mSessionActive = true;
@@ -154,6 +165,16 @@
* @return true if the classifier determined that this is not a human interacting with the phone
*/
public boolean isFalseTouch() {
+ if (FalsingLog.ENABLED) {
+ if (!mSessionActive) {
+ FalsingLog.wtf("isFalseTouch", new StringBuilder()
+ .append("Session is not active, yet there's a query for a false touch.")
+ .append(" enabled=").append(isEnabled() ? 1 : 0)
+ .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
+ .append(" mState=").append(StatusBarState.toShortString(mState))
+ .toString());
+ }
+ }
return mHumanInteractionClassifier.isFalseTouch();
}
@@ -173,6 +194,12 @@
}
public void setStatusBarState(int state) {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("setStatusBarState", new StringBuilder()
+ .append("from=").append(StatusBarState.toShortString(mState))
+ .append(" to=").append(StatusBarState.toShortString(state))
+ .toString());
+ }
mState = state;
if (shouldSessionBeActive()) {
sessionEntrypoint();
@@ -182,6 +209,11 @@
}
public void onScreenTurningOn() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onScreenTurningOn", new StringBuilder()
+ .append("from=").append(mScreenOn ? 1 : 0)
+ .toString());
+ }
mScreenOn = true;
if (sessionEntrypoint()) {
mDataCollector.onScreenTurningOn();
@@ -189,6 +221,11 @@
}
public void onScreenOnFromTouch() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onScreenOnFromTouch", new StringBuilder()
+ .append("from=").append(mScreenOn ? 1 : 0)
+ .toString());
+ }
mScreenOn = true;
if (sessionEntrypoint()) {
mDataCollector.onScreenOnFromTouch();
@@ -196,17 +233,30 @@
}
public void onScreenOff() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onScreenOff", new StringBuilder()
+ .append("from=").append(mScreenOn ? 1 : 0)
+ .toString());
+ }
mDataCollector.onScreenOff();
mScreenOn = false;
sessionExitpoint(false /* force */);
}
public void onSucccessfulUnlock() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onSucccessfulUnlock", "");
+ }
mDataCollector.onSucccessfulUnlock();
sessionExitpoint(true /* force */);
}
public void onBouncerShown() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onBouncerShown", new StringBuilder()
+ .append("from=").append(mBouncerOn ? 1 : 0)
+ .toString());
+ }
if (!mBouncerOn) {
mBouncerOn = true;
mDataCollector.onBouncerShown();
@@ -214,6 +264,11 @@
}
public void onBouncerHidden() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onBouncerHidden", new StringBuilder()
+ .append("from=").append(mBouncerOn ? 1 : 0)
+ .toString());
+ }
if (mBouncerOn) {
mBouncerOn = false;
mDataCollector.onBouncerHidden();
@@ -221,6 +276,9 @@
}
public void onQsDown() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onQsDown", "");
+ }
mHumanInteractionClassifier.setType(Classifier.QUICK_SETTINGS);
mDataCollector.onQsDown();
}
@@ -230,6 +288,9 @@
}
public void onTrackingStarted() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onTrackingStarted", "");
+ }
mHumanInteractionClassifier.setType(Classifier.UNLOCK);
mDataCollector.onTrackingStarted();
}
@@ -251,6 +312,9 @@
}
public void onNotificatonStartDraggingDown() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onNotificatonStartDraggingDown", "");
+ }
mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DRAG_DOWN);
mDataCollector.onNotificatonStartDraggingDown();
}
@@ -264,6 +328,9 @@
}
public void onNotificatonStartDismissing() {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onNotificatonStartDismissing", "");
+ }
mHumanInteractionClassifier.setType(Classifier.NOTIFICATION_DISMISS);
mDataCollector.onNotificatonStartDismissing();
}
@@ -281,6 +348,9 @@
}
public void onAffordanceSwipingStarted(boolean rightCorner) {
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("onAffordanceSwipingStarted", "");
+ }
if (rightCorner) {
mHumanInteractionClassifier.setType(Classifier.RIGHT_AFFORDANCE);
} else {
@@ -311,4 +381,14 @@
mHumanInteractionClassifier.onTouchEvent(event);
}
}
+
+ public void dump(PrintWriter pw) {
+ pw.println("FALSING MANAGER");
+ pw.print("classifierEnabled="); pw.println(isClassiferEnabled() ? 1 : 0);
+ pw.print("mSessionActive="); pw.println(mSessionActive ? 1 : 0);
+ pw.print("mBouncerOn="); pw.println(mSessionActive ? 1 : 0);
+ pw.print("mState="); pw.println(StatusBarState.toShortString(mState));
+ pw.print("mScreenOn="); pw.println(mScreenOn ? 1 : 0);
+ pw.println();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
index 45eb9ad..5e35d76 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java
@@ -23,6 +23,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.MotionEvent;
import java.util.ArrayDeque;
@@ -43,14 +44,12 @@
private final Handler mHandler = new Handler();
private final Context mContext;
- private ArrayList<StrokeClassifier> mStrokeClassifiers = new ArrayList<>();
- private ArrayList<GestureClassifier> mGestureClassifiers = new ArrayList<>();
- private ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>();
- private final int mStrokeClassifiersSize;
- private final int mGestureClassifiersSize;
+ private final StrokeClassifier[] mStrokeClassifiers;
+ private final GestureClassifier[] mGestureClassifiers;
+ private final ArrayDeque<MotionEvent> mBufferedEvents = new ArrayDeque<>();
+ private final HistoryEvaluator mHistoryEvaluator;
private final float mDpi;
- private HistoryEvaluator mHistoryEvaluator;
private boolean mEnableClassifier = false;
private int mCurrentType = Classifier.GENERIC;
@@ -68,25 +67,27 @@
// If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
// were to be used separately. Due negligible differences in xdpi and ydpi we can just
// take the average.
+ // TODO: make this respect DPI changes.
mDpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
mClassifierData = new ClassifierData(mDpi);
mHistoryEvaluator = new HistoryEvaluator();
- mStrokeClassifiers.add(new AnglesClassifier(mClassifierData));
- mStrokeClassifiers.add(new SpeedClassifier(mClassifierData));
- mStrokeClassifiers.add(new DurationCountClassifier(mClassifierData));
- mStrokeClassifiers.add(new EndPointRatioClassifier(mClassifierData));
- mStrokeClassifiers.add(new EndPointLengthClassifier(mClassifierData));
- mStrokeClassifiers.add(new AccelerationClassifier(mClassifierData));
- mStrokeClassifiers.add(new SpeedAnglesClassifier(mClassifierData));
- mStrokeClassifiers.add(new LengthCountClassifier(mClassifierData));
- mStrokeClassifiers.add(new DirectionClassifier(mClassifierData));
+ mStrokeClassifiers = new StrokeClassifier[]{
+ new AnglesClassifier(mClassifierData),
+ new SpeedClassifier(mClassifierData),
+ new DurationCountClassifier(mClassifierData),
+ new EndPointRatioClassifier(mClassifierData),
+ new EndPointLengthClassifier(mClassifierData),
+ new AccelerationClassifier(mClassifierData),
+ new SpeedAnglesClassifier(mClassifierData),
+ new LengthCountClassifier(mClassifierData),
+ new DirectionClassifier(mClassifierData),
+ };
- mGestureClassifiers.add(new PointerCountClassifier(mClassifierData));
- mGestureClassifiers.add(new ProximityClassifier(mClassifierData));
-
- mStrokeClassifiersSize = mStrokeClassifiers.size();
- mGestureClassifiersSize = mGestureClassifiers.size();
+ mGestureClassifiers = new GestureClassifier[] {
+ new PointerCountClassifier(mClassifierData),
+ new ProximityClassifier(mClassifierData)
+ };
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(HIC_ENABLE), false,
@@ -150,21 +151,30 @@
private void addTouchEvent(MotionEvent event) {
mClassifierData.update(event);
- for (int i = 0; i < mStrokeClassifiersSize; i++) {
- mStrokeClassifiers.get(i).onTouchEvent(event);
+ for (StrokeClassifier c : mStrokeClassifiers) {
+ c.onTouchEvent(event);
}
- for (int i = 0; i < mGestureClassifiersSize; i++) {
- mGestureClassifiers.get(i).onTouchEvent(event);
+ for (GestureClassifier c : mGestureClassifiers) {
+ c.onTouchEvent(event);
}
int size = mClassifierData.getEndingStrokes().size();
for (int i = 0; i < size; i++) {
Stroke stroke = mClassifierData.getEndingStrokes().get(i);
float evaluation = 0.0f;
- for (int j = 0; j < mStrokeClassifiersSize; j++) {
- evaluation += mStrokeClassifiers.get(j).getFalseTouchEvaluation(
- mCurrentType, stroke);
+ StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("stroke") : null;
+ for (StrokeClassifier c : mStrokeClassifiers) {
+ float e = c.getFalseTouchEvaluation(mCurrentType, stroke);
+ if (FalsingLog.ENABLED) {
+ String tag = c.getTag();
+ sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e);
+ }
+ evaluation += e;
+ }
+
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i(" addTouchEvent", sb.toString());
}
mHistoryEvaluator.addStroke(evaluation);
}
@@ -172,8 +182,17 @@
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
float evaluation = 0.0f;
- for (int i = 0; i < mGestureClassifiersSize; i++) {
- evaluation += mGestureClassifiers.get(i).getFalseTouchEvaluation(mCurrentType);
+ StringBuilder sb = FalsingLog.ENABLED ? new StringBuilder("gesture") : null;
+ for (GestureClassifier c : mGestureClassifiers) {
+ float e = c.getFalseTouchEvaluation(mCurrentType);
+ if (FalsingLog.ENABLED) {
+ String tag = c.getTag();
+ sb.append(" ").append(e >= 1f ? tag : tag.toLowerCase()).append("=").append(e);
+ }
+ evaluation += e;
+ }
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i(" addTouchEvent", sb.toString());
}
mHistoryEvaluator.addGesture(evaluation);
setType(Classifier.GENERIC);
@@ -184,18 +203,25 @@
@Override
public void onSensorChanged(SensorEvent event) {
- for (int i = 0; i < mStrokeClassifiers.size(); i++) {
- mStrokeClassifiers.get(i).onSensorChanged(event);
+ for (Classifier c : mStrokeClassifiers) {
+ c.onSensorChanged(event);
}
- for (int i = 0; i < mGestureClassifiers.size(); i++) {
- mGestureClassifiers.get(i).onSensorChanged(event);
+ for (Classifier c : mGestureClassifiers) {
+ c.onSensorChanged(event);
}
}
public boolean isFalseTouch() {
if (mEnableClassifier) {
- return mHistoryEvaluator.getEvaluation() >= 5.0f;
+ float evaluation = mHistoryEvaluator.getEvaluation();
+ boolean result = evaluation >= 5.0f;
+ if (FalsingLog.ENABLED) {
+ FalsingLog.i("isFalseTouch", new StringBuilder()
+ .append("eval=").append(evaluation).append(" result=")
+ .append(result ? 1 : 0).toString());
+ }
+ return result;
}
return false;
}
@@ -203,4 +229,9 @@
public boolean isEnabled() {
return mEnableClassifier;
}
+
+ @Override
+ public String getTag() {
+ return "HIC";
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
index cedf467..53678a6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java
@@ -28,6 +28,11 @@
}
@Override
+ public String getTag() {
+ return "LEN_CNT";
+ }
+
+ @Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
return LengthCountEvaluator.evaluate(stroke.getTotalLength()
/ Math.max(1.0f, stroke.getCount() - 2));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
index 5097b63..136c433 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
@@ -29,6 +29,11 @@
}
@Override
+ public String getTag() {
+ return "PTR_CNT";
+ }
+
+ @Override
public void onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 6995064..62adfc8 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -35,6 +35,11 @@
}
@Override
+ public String getTag() {
+ return "PROX";
+ }
+
+ @Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
update(event.values[0] < event.sensor.getMaximumRange(), event.timestamp);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
index d58274d..6df72b1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java
@@ -41,6 +41,11 @@
}
@Override
+ public String getTag() {
+ return "SPD_ANG";
+ }
+
+ @Override
public void onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
index 81b78c7..01fcc37 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedClassifier.java
@@ -27,6 +27,11 @@
}
@Override
+ public String getTag() {
+ return "SPD";
+ }
+
+ @Override
public float getFalseTouchEvaluation(int type, Stroke stroke) {
float duration = (float) stroke.getDurationNanos() / NANOS_TO_SECONDS;
if (duration == 0.0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 6029c23..02b860c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -684,7 +684,7 @@
doKeyguardLocked(null);
mUpdateMonitor.registerCallback(mUpdateCallback);
}
- mIsPerUserLock = StorageManager.isFileBasedEncryptionEnabled();
+ mIsPerUserLock = StorageManager.isFileEncryptedNativeOrEmulated();
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
maybeSendUserPresentBroadcast();
@@ -811,7 +811,7 @@
intent.putExtra("seq", mDelayedShowingSequence);
PendingIntent sender = PendingIntent.getBroadcast(mContext,
0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
+ mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
+ mDelayedShowingSequence);
doKeyguardLaterForChildProfilesLocked();
@@ -828,7 +828,8 @@
lockIntent.putExtra(Intent.EXTRA_USER_ID, info.id);
PendingIntent lockSender = PendingIntent.getBroadcast(
mContext, 0, lockIntent, PendingIntent.FLAG_CANCEL_CURRENT);
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, userWhen, lockSender);
+ mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ userWhen, lockSender);
}
}
}
@@ -1542,13 +1543,22 @@
try {
mStatusBarKeyguardViewManager.keyguardGoingAway();
+ int flags = 0;
+ if (mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock()
+ || mWakeAndUnlocking) {
+ flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
+ }
+ if (mStatusBarKeyguardViewManager.isGoingToNotificationShade()) {
+ flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
+ }
+ if (mStatusBarKeyguardViewManager.isUnlockWithWallpaper()) {
+ flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+ }
+
// Don't actually hide the Keyguard at the moment, wait for window
// manager until it tells us it's safe to do so with
// startKeyguardExitAnimation.
- ActivityManagerNative.getDefault().keyguardGoingAway(
- mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock()
- || mWakeAndUnlocking,
- mStatusBarKeyguardViewManager.isGoingToNotificationShade());
+ ActivityManagerNative.getDefault().keyguardGoingAway(flags);
} catch (RemoteException e) {
Log.e(TAG, "Error while calling WindowManager", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index f39f302..ab612dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -16,8 +16,10 @@
package com.android.systemui.media;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
import android.media.AudioAttributes;
import android.media.IAudioService;
import android.media.IRingtonePlayer;
@@ -25,15 +27,20 @@
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Audio.AudioColumns;
import android.util.Log;
+import com.android.internal.util.Preconditions;
import com.android.systemui.SystemUI;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -180,6 +187,34 @@
return Ringtone.getTitle(getContextForUser(user), uri,
false /*followSettingsUri*/, false /*allowRemote*/);
}
+
+ @Override
+ public ParcelFileDescriptor openRingtone(Uri uri) {
+ final UserHandle user = Binder.getCallingUserHandle();
+ final ContentResolver resolver = getContextForUser(user).getContentResolver();
+
+ // Only open the requested Uri if it's a well-known ringtone or
+ // other sound from the platform media store, otherwise this opens
+ // up arbitrary access to any file on external storage.
+ if (uri.toString().startsWith(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())) {
+ try (Cursor c = resolver.query(uri, new String[] {
+ MediaStore.Audio.AudioColumns.IS_RINGTONE,
+ MediaStore.Audio.AudioColumns.IS_ALARM,
+ MediaStore.Audio.AudioColumns.IS_NOTIFICATION
+ }, null, null, null)) {
+ if (c.moveToFirst()) {
+ if (c.getInt(0) != 0 || c.getInt(1) != 0 || c.getInt(2) != 0) {
+ try {
+ return resolver.openFileDescriptor(uri, "r");
+ } catch (IOException e) {
+ throw new SecurityException(e);
+ }
+ }
+ }
+ }
+ }
+ throw new SecurityException("Uri is not ringtone, alarm, or notification: " + uri);
+ }
};
private Context getContextForUser(UserHandle user) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 8ccf60d..24b45cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -167,6 +167,11 @@
}
};
+ public int getColumnCount() {
+ if (mPages.size() == 0) return 0;
+ return mPages.get(0).mColumns;
+ }
+
public static class TilePage extends TileLayout {
private int mMaxRows = 3;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PathInterpolatorBuilder.java b/packages/SystemUI/src/com/android/systemui/qs/PathInterpolatorBuilder.java
new file mode 100644
index 0000000..b8cb92a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/PathInterpolatorBuilder.java
@@ -0,0 +1,153 @@
+/*
+ * 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;
+
+import android.graphics.Path;
+import android.view.animation.BaseInterpolator;
+import android.view.animation.Interpolator;
+
+public class PathInterpolatorBuilder {
+
+ // This governs how accurate the approximation of the Path is.
+ private static final float PRECISION = 0.002f;
+
+ private float[] mX; // x coordinates in the line
+ private float[] mY; // y coordinates in the line
+ private float[] mDist; // Cumulative percentage length of the line
+
+ public PathInterpolatorBuilder(Path path) {
+ initPath(path);
+ }
+
+ public PathInterpolatorBuilder(float controlX, float controlY) {
+ initQuad(controlX, controlY);
+ }
+
+ public PathInterpolatorBuilder(float controlX1, float controlY1, float controlX2,
+ float controlY2) {
+ initCubic(controlX1, controlY1, controlX2, controlY2);
+ }
+
+ private void initQuad(float controlX, float controlY) {
+ Path path = new Path();
+ path.moveTo(0, 0);
+ path.quadTo(controlX, controlY, 1f, 1f);
+ initPath(path);
+ }
+
+ private void initCubic(float x1, float y1, float x2, float y2) {
+ Path path = new Path();
+ path.moveTo(0, 0);
+ path.cubicTo(x1, y1, x2, y2, 1f, 1f);
+ initPath(path);
+ }
+
+ private void initPath(Path path) {
+ float[] pointComponents = path.approximate(PRECISION);
+
+ int numPoints = pointComponents.length / 3;
+ if (pointComponents[1] != 0 || pointComponents[2] != 0
+ || pointComponents[pointComponents.length - 2] != 1
+ || pointComponents[pointComponents.length - 1] != 1) {
+ throw new IllegalArgumentException("The Path must start at (0,0) and end at (1,1)");
+ }
+
+ mX = new float[numPoints];
+ mY = new float[numPoints];
+ mDist = new float[numPoints];
+ float prevX = 0;
+ float prevFraction = 0;
+ int componentIndex = 0;
+ for (int i = 0; i < numPoints; i++) {
+ float fraction = pointComponents[componentIndex++];
+ float x = pointComponents[componentIndex++];
+ float y = pointComponents[componentIndex++];
+ if (fraction == prevFraction && x != prevX) {
+ throw new IllegalArgumentException(
+ "The Path cannot have discontinuity in the X axis.");
+ }
+ if (x < prevX) {
+ throw new IllegalArgumentException("The Path cannot loop back on itself.");
+ }
+ mX[i] = x;
+ mY[i] = y;
+ if (i > 0) {
+ float dx = mX[i] - mX[i - 1];
+ float dy = mY[i] - mY[i - 1];
+ float dist = (float) Math.sqrt(dx * dx + dy * dy);
+ mDist[i] = mDist[i - 1] + dist;
+ }
+ prevX = x;
+ prevFraction = fraction;
+ }
+ // Scale down dist to 0-1.
+ float max = mDist[mDist.length - 1];
+ for (int i = 0; i < numPoints; i++) {
+ mDist[i] /= max;
+ }
+ }
+
+ public Interpolator getXInterpolator() {
+ return new PathInterpolator(mDist, mX);
+ }
+
+ public Interpolator getYInterpolator() {
+ return new PathInterpolator(mDist, mY);
+ }
+
+ private static class PathInterpolator extends BaseInterpolator {
+ private final float[] mX; // x coordinates in the line
+ private final float[] mY; // y coordinates in the line
+
+ private PathInterpolator(float[] xs, float[] ys) {
+ mX = xs;
+ mY = ys;
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ if (t <= 0) {
+ return 0;
+ } else if (t >= 1) {
+ return 1;
+ }
+ // Do a binary search for the correct x to interpolate between.
+ int startIndex = 0;
+ int endIndex = mX.length - 1;
+
+ while (endIndex - startIndex > 1) {
+ int midIndex = (startIndex + endIndex) / 2;
+ if (t < mX[midIndex]) {
+ endIndex = midIndex;
+ } else {
+ startIndex = midIndex;
+ }
+ }
+
+ float xRange = mX[endIndex] - mX[startIndex];
+ if (xRange == 0) {
+ return mY[startIndex];
+ }
+
+ float tInRange = t - mX[startIndex];
+ float fraction = tInRange / xRange;
+
+ float startY = mY[startIndex];
+ float endY = mY[endIndex];
+ return startY + (fraction * (endY - startY));
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index c643d67..88b6caa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,10 +14,11 @@
package com.android.systemui.qs;
+import android.graphics.Path;
import android.util.Log;
import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnLayoutChangeListener;
-import android.view.animation.PathInterpolator;
import android.widget.TextView;
import com.android.systemui.qs.PagedTileLayout.PageListener;
import com.android.systemui.qs.QSPanel.QSTileLayout;
@@ -25,18 +26,22 @@
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
-public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener {
+public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener,
+ OnAttachStateChangeListener, Tunable {
private static final String TAG = "QSAnimator";
- public static final PathInterpolator TRANSLATION_Y_INTERPOLATOR =
- new PathInterpolator(.1f, .3f, 1, 1);
+ 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 = .84f;
private final ArrayList<View> mAllViews = new ArrayList<>();
private final ArrayList<View> mTopFiveQs = new ArrayList<>();
@@ -44,34 +49,79 @@
private final QSPanel mQsPanel;
private final QSContainer mQsContainer;
+ private PagedTileLayout mPagedLayout;
+
private boolean mOnFirstPage = true;
private TouchAnimator mFirstPageAnimator;
private TouchAnimator mFirstPageDelayedAnimator;
+ private TouchAnimator mTranslationXAnimator;
private TouchAnimator mTranslationYAnimator;
private TouchAnimator mNonfirstPageAnimator;
+ private TouchAnimator mLastRowAnimator;
+
+ private boolean mOnKeyguard;
+
+ private boolean mAllowFancy;
+ private boolean mFullRows;
+ private int mNumQuickTiles;
public QSAnimator(QSContainer container, QuickQSPanel quickPanel, QSPanel panel) {
mQsContainer = container;
mQuickQsPanel = quickPanel;
mQsPanel = panel;
+ mQsPanel.addOnAttachStateChangeListener(this);
container.addOnLayoutChangeListener(this);
QSTileLayout tileLayout = mQsPanel.getTileLayout();
if (tileLayout instanceof PagedTileLayout) {
- ((PagedTileLayout) tileLayout).setPageListener(this);
+ mPagedLayout = ((PagedTileLayout) tileLayout);
+ mPagedLayout.setPageListener(this);
} else {
Log.w(TAG, "QS Not using page layout");
}
}
+ public void setOnKeyguard(boolean onKeyguard) {
+ mOnKeyguard = onKeyguard;
+ if (mOnKeyguard) {
+ clearAnimationState();
+ }
+ }
+
public void setHost(QSTileHost qsh) {
qsh.addCallback(this);
}
@Override
+ public void onViewAttachedToWindow(View v) {
+ TunerService.get(mQsContainer.getContext()).addTunable(this, ALLOW_FANCY_ANIMATION,
+ MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ TunerService.get(mQsContainer.getContext()).removeTunable(this);
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (ALLOW_FANCY_ANIMATION.equals(key)) {
+ mAllowFancy = newValue == null || Integer.parseInt(newValue) != 0;
+ if (!mAllowFancy) {
+ clearAnimationState();
+ }
+ } else if (MOVE_FULL_ROWS.equals(key)) {
+ mFullRows = newValue == null || Integer.parseInt(newValue) != 0;
+ } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
+ mNumQuickTiles = QuickQSPanel.getNumQuickTiles(mQsContainer.getContext());
+ clearAnimationState();
+ }
+ updateAnimators();
+ }
+
+ @Override
public void onPageChanged(boolean isFirst) {
if (mOnFirstPage == isFirst) return;
if (!isFirst) {
- setPosition(1);
clearAnimationState();
}
mOnFirstPage = isFirst;
@@ -79,33 +129,37 @@
private void updateAnimators() {
TouchAnimator.Builder firstPageBuilder = new Builder();
+ TouchAnimator.Builder translationXBuilder = new Builder();
TouchAnimator.Builder translationYBuilder = new Builder();
- TouchAnimator.Builder firstPageDelayedBuilder = new Builder();
+ TouchAnimator.Builder lastRowBuilder = new Builder();
+
Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles();
int count = 0;
int[] loc1 = new int[2];
int[] loc2 = new int[2];
- firstPageDelayedBuilder.setStartDelay(EXPANDED_TILE_DELAY);
- firstPageBuilder.setListener(this);
- translationYBuilder.setInterpolator(TRANSLATION_Y_INTERPOLATOR);
- // Fade in the tiles/labels as we reach the final position.
- firstPageDelayedBuilder.addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1);
+ int lastYDiff = 0;
+
+ clearAnimationState();
mAllViews.clear();
mTopFiveQs.clear();
+
+ mAllViews.add((View) mQsPanel.getTileLayout());
+
for (QSTile<?> tile : tiles) {
QSTileBaseView tileView = mQsPanel.getTileView(tile);
final TextView label = ((QSTileView) tileView).getLabel();
- if (count++ < 5) {
+ final View tileIcon = tileView.getIcon();
+ if (count < mNumQuickTiles && mAllowFancy) {
// Quick tiles.
QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
- final View tileIcon = tileView.getIcon();
getRelativePosition(loc1, quickTileView.getIcon(), mQsContainer);
getRelativePosition(loc2, tileIcon, mQsContainer);
final int xDiff = loc2[0] - loc1[0];
final int yDiff = loc2[1] - loc1[1];
+ lastYDiff = yDiff;
// Move the quick tile right from its location to the new one.
- firstPageBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
+ translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
// Counteract the parent translation on the tile. So we have a static base to
@@ -114,25 +168,59 @@
// Move the real tile's label from the quick tile position to its final
// location.
- firstPageBuilder.addFloat(label, "translationX", -xDiff, 0);
+ translationXBuilder.addFloat(label, "translationX", -xDiff, 0);
translationYBuilder.addFloat(label, "translationY", -yDiff, 0);
mTopFiveQs.add(tileIcon);
mAllViews.add(tileIcon);
mAllViews.add(quickTileView);
+ } else if (mFullRows && isIconInAnimatedRow(count)) {
+ firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
+ translationYBuilder.addFloat(label, "translationY", -lastYDiff, 0);
+ translationYBuilder.addFloat(tileIcon, "translationY", -lastYDiff, 0);
+ mAllViews.add(tileIcon);
+ } else {
+ lastRowBuilder.addFloat(tileView, "alpha", 0, 1);
}
mAllViews.add(tileView);
mAllViews.add(label);
+ count++;
}
- mFirstPageAnimator = firstPageBuilder.build();
- mFirstPageDelayedAnimator = firstPageDelayedBuilder.build();
- mTranslationYAnimator = translationYBuilder.build();
+ if (mAllowFancy) {
+ 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);
+ translationXBuilder.setInterpolator(interpolatorBuilder.getXInterpolator());
+ translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
+ mTranslationXAnimator = translationXBuilder.build();
+ mTranslationYAnimator = translationYBuilder.build();
+ }
mNonfirstPageAnimator = new TouchAnimator.Builder()
.addFloat(mQuickQsPanel, "alpha", 1, 0)
+ .setListener(mNonFirstPageListener)
.setEndDelay(.5f)
.build();
}
+ private boolean isIconInAnimatedRow(int count) {
+ if (mPagedLayout == null) {
+ return false;
+ }
+ final int columnCount = mPagedLayout.getColumnCount();
+ return count < ((mNumQuickTiles + columnCount - 1) / columnCount) * columnCount;
+ }
+
private void getRelativePosition(int[] loc1, View view, View parent) {
loc1[0] = 0 + view.getWidth() / 2;
loc1[1] = 0;
@@ -148,11 +236,16 @@
public void setPosition(float position) {
if (mFirstPageAnimator == null) return;
- if (mOnFirstPage) {
+ if (mOnKeyguard) {
+ return;
+ }
+ if (mOnFirstPage && mAllowFancy) {
mQuickQsPanel.setAlpha(1);
mFirstPageAnimator.setPosition(position);
mFirstPageDelayedAnimator.setPosition(position);
+ mTranslationXAnimator.setPosition(position);
mTranslationYAnimator.setPosition(position);
+ mLastRowAnimator.setPosition(position);
} else {
mNonfirstPageAnimator.setPosition(position);
}
@@ -186,12 +279,17 @@
private void clearAnimationState() {
final int N = mAllViews.size();
mQuickQsPanel.setAlpha(0);
+ mQuickQsPanel.setVisibility(View.INVISIBLE);
for (int i = 0; i < N; i++) {
View v = mAllViews.get(i);
v.setAlpha(1);
v.setTranslationX(1);
v.setTranslationY(1);
}
+ final int N2 = mTopFiveQs.size();
+ for (int i = 0; i < N2; i++) {
+ mTopFiveQs.get(i).setVisibility(View.VISIBLE);
+ }
}
@Override
@@ -207,6 +305,14 @@
mQsPanel.post(mUpdateAnimators);
}
+ private final TouchAnimator.Listener mNonFirstPageListener =
+ new TouchAnimator.ListenerAdapter() {
+ @Override
+ public void onAnimationStarted() {
+ mQuickQsPanel.setVisibility(View.VISIBLE);
+ }
+ };
+
private Runnable mUpdateAnimators = new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index c59da8d..5b05e84 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
+import android.graphics.Point;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -26,7 +27,9 @@
import android.widget.FrameLayout;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.phone.BaseStatusBarHeader;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -39,6 +42,8 @@
private static final String TAG = "QSContainer";
private static final boolean DEBUG = false;
+ private final Point mSizePoint = new Point();
+
private int mHeightOverride = -1;
private QSPanel mQSPanel;
private QSDetail mQSDetail;
@@ -51,6 +56,8 @@
private long mDelay;
private QSAnimator mQSAnimator;
+ private QSCustomizer mQSCustomizer;
+ private NotificationPanelView mPanelView;
public QSContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -65,21 +72,33 @@
mHeader = (BaseStatusBarHeader) findViewById(R.id.header);
mQSAnimator = new QSAnimator(this, (QuickQSPanel) mHeader.findViewById(R.id.quick_qs_panel),
mQSPanel);
+ mQSCustomizer = (QSCustomizer) findViewById(R.id.qs_customize);
+ mQSCustomizer.setQsContainer(this);
}
public void setHost(QSTileHost qsh) {
- mQSPanel.setHost(qsh);
+ mQSPanel.setHost(qsh, mQSCustomizer);
mHeader.setQSPanel(mQSPanel);
mQSDetail.setHost(qsh);
mQSAnimator.setHost(qsh);
}
+ public void setPanelView(NotificationPanelView panelView) {
+ mPanelView = panelView;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Since we control our own bottom, be whatever size we want.
// Otherwise the QSPanel ends up with 0 height when the window is only the
// size of the status bar.
super.onMeasure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
+
+ // QSCustomizer is always be the height of the screen, but do this after
+ // other measuring to avoid changing the height of the QSContainer.
+ getDisplay().getRealSize(mSizePoint);
+ mQSCustomizer.measure(widthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(mSizePoint.y, MeasureSpec.EXACTLY));
}
@Override
@@ -88,6 +107,10 @@
updateBottom();
}
+ public boolean isCustomizing() {
+ return mQSCustomizer.isCustomizing();
+ }
+
/**
* Overrides the height of this view (post-layout), so that the content is clipped to that
* height and the background is set to that height.
@@ -104,6 +127,9 @@
* during closing the detail panel, this already returns the smaller height.
*/
public int getDesiredHeight() {
+ if (isCustomizing()) {
+ return getHeight();
+ }
if (mQSDetail.isClosingDetail()) {
return mQSPanel.getGridHeight() + mHeader.getCollapsedHeight() + getPaddingBottom();
} else {
@@ -111,9 +137,18 @@
}
}
+ public void notifyCustomizeChanged() {
+ // The customize state changed, so our height changed.
+ updateBottom();
+ // Let the panel know the position changed and it needs to update where notifications
+ // and whatnot are.
+ mPanelView.onQsHeightChanged();
+ }
+
private void updateBottom() {
int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
- int height = (int) (mQsExpansion * (heightOverride - mHeader.getCollapsedHeight()))
+ int height = mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight()
+ : (int) (mQsExpansion * (heightOverride - mHeader.getCollapsedHeight()))
+ mHeader.getCollapsedHeight();
setBottom(getTop() + height);
mQSDetail.setBottom(getTop() + height);
@@ -138,6 +173,10 @@
return mQSPanel;
}
+ public QSCustomizer getCustomizer() {
+ return mQSCustomizer;
+ }
+
public boolean isShowingDetail() {
return mQSPanel.isShowingCustomize() || mQSDetail.isShowingDetail();
}
@@ -156,6 +195,7 @@
public void setKeyguardShowing(boolean keyguardShowing) {
if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
mKeyguardShowing = keyguardShowing;
+ mQSAnimator.setOnKeyguard(keyguardShowing);
updateQsState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 71c1913d..899b0ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -155,12 +155,6 @@
return mHost.createTile(subPanel);
}
- protected void createCustomizePanel() {
- mCustomizePanel = (QSCustomizer) LayoutInflater.from(mContext)
- .inflate(R.layout.qs_customize_panel, null);
- mCustomizePanel.setHost(mHost);
- }
-
public void setBrightnessMirror(BrightnessMirrorController c) {
super.onFinishInflate();
ToggleSlider brightnessSlider = (ToggleSlider) findViewById(R.id.brightness_slider);
@@ -173,12 +167,15 @@
mCallback = callback;
}
- public void setHost(QSTileHost host) {
+ public void setHost(QSTileHost host, QSCustomizer customizer) {
mHost = host;
mHost.addCallback(this);
setTiles(mHost.getTiles());
mFooter.setHost(host);
- createCustomizePanel();
+ mCustomizePanel = customizer;
+ if (mCustomizePanel != null) {
+ mCustomizePanel.setHost(mHost);
+ }
}
public QSTileHost getHost() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 8b826ee..d0e034b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -26,6 +26,8 @@
import com.android.systemui.R;
import com.android.systemui.qs.QSTile.SignalState;
import com.android.systemui.qs.QSTile.State;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
@@ -35,6 +37,8 @@
*/
public class QuickQSPanel extends QSPanel {
+ public static final String NUM_QUICK_TILES = "sysui_qqs_count";
+
private int mMaxTiles;
private QSPanel mFullPanel;
private View mHeader;
@@ -52,8 +56,15 @@
}
@Override
- protected void createCustomizePanel() {
- // No customizing from the header.
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ TunerService.get(mContext).addTunable(mNumTiles, NUM_QUICK_TILES);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ TunerService.get(mContext).removeTunable(mNumTiles);
}
public void setQSPanelAndHeader(QSPanel fullPanel, View header) {
@@ -86,6 +97,7 @@
public void setMaxTiles(int maxTiles) {
mMaxTiles = maxTiles;
+ setTiles(mHost.getTiles());
}
@Override
@@ -114,6 +126,17 @@
super.setTiles(quickTiles);
}
+ private final Tunable mNumTiles = new Tunable() {
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ setMaxTiles(getNumQuickTiles(mContext));
+ }
+ };
+
+ public static int getNumQuickTiles(Context context) {
+ return TunerService.get(context).getValue(NUM_QUICK_TILES, 5);
+ }
+
private static class HeaderTileLayout extends LinearLayout implements QSTileLayout {
private final Space mEndSpacer;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 35ade58..37f2528 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -14,6 +14,7 @@
package com.android.systemui.qs;
+import android.util.FloatProperty;
import android.util.MathUtils;
import android.util.Property;
import android.view.View;
@@ -74,6 +75,19 @@
}
}
+ private static final FloatProperty<TouchAnimator> POSITION =
+ new FloatProperty<TouchAnimator>("position") {
+ @Override
+ public void setValue(TouchAnimator touchAnimator, float value) {
+ touchAnimator.setPosition(value);
+ }
+
+ @Override
+ public Float get(TouchAnimator touchAnimator) {
+ return touchAnimator.mLastT;
+ }
+ };
+
public static class ListenerAdapter implements Listener {
@Override
public void onAnimationAtStart() { }
@@ -152,6 +166,9 @@
return View.SCALE_Y;
}
}
+ if (target instanceof TouchAnimator && "position".equals(property)) {
+ return POSITION;
+ }
return Property.of(target.getClass(), cls, property);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 5e02428..72a59d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -25,6 +25,7 @@
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -32,8 +33,10 @@
import android.widget.Toolbar;
import android.widget.Toolbar.OnMenuItemClickListener;
import com.android.systemui.R;
+import com.android.systemui.qs.QSContainer;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.QSTileHost;
@@ -59,20 +62,16 @@
private RecyclerView mRecyclerView;
private TileAdapter mTileAdapter;
private Toolbar mToolbar;
+ private boolean mCustomizing;
+ private NotificationsQuickSettingsContainer mNotifQsContainer;
+ private QSContainer mQsContainer;
public QSCustomizer(Context context, AttributeSet attrs) {
- super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
+ super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
mClipper = new QSDetailClipper(this);
- }
- public void setHost(QSTileHost host) {
- mHost = host;
- mPhoneStatusBar = host.getPhoneStatusBar();
- }
+ LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
mToolbar = (Toolbar) findViewById(com.android.internal.R.id.action_bar);
TypedValue value = new TypedValue();
mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
@@ -102,26 +101,47 @@
mRecyclerView.setItemAnimator(animator);
}
+ public void setHost(QSTileHost host) {
+ mHost = host;
+ mPhoneStatusBar = host.getPhoneStatusBar();
+ }
+
+ public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
+ mNotifQsContainer = notificationsQsContainer;
+ }
+
+ public void setQsContainer(QSContainer qsContainer) {
+ mQsContainer = qsContainer;
+ }
+
public void show(int x, int y) {
if (!isShown) {
isShown = true;
- mPhoneStatusBar.getStatusBarWindow().addView(this);
setTileSpecs();
- mClipper.animateCircularClip(x, y, true, null);
+ setVisibility(View.VISIBLE);
+ mClipper.animateCircularClip(x, y, true, mExpandAnimationListener);
new TileQueryHelper(mContext, mHost).setListener(mTileAdapter);
+ mNotifQsContainer.setCustomizerAnimating(true);
}
}
public void hide(int x, int y) {
if (isShown) {
isShown = false;
+ setCustomizing(false);
save();
mClipper.animateCircularClip(x, y, false, mCollapseAnimationListener);
+ mNotifQsContainer.setCustomizerAnimating(true);
}
}
+ private void setCustomizing(boolean customizing) {
+ mCustomizing = customizing;
+ mQsContainer.notifyCustomizeChanged();
+ }
+
public boolean isCustomizing() {
- return isShown;
+ return mCustomizing;
}
@Override
@@ -155,19 +175,34 @@
mTileAdapter.saveSpecs(mHost);
}
+ private final AnimatorListener mExpandAnimationListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setCustomizing(true);
+ mNotifQsContainer.setCustomizerAnimating(false);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mNotifQsContainer.setCustomizerAnimating(false);
+ }
+ };
+
private final AnimatorListener mCollapseAnimationListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!isShown) {
- mPhoneStatusBar.getStatusBarWindow().removeView(QSCustomizer.this);
+ setVisibility(View.GONE);
}
+ mNotifQsContainer.setCustomizerAnimating(false);
}
@Override
public void onAnimationCancel(Animator animation) {
if (!isShown) {
- mPhoneStatusBar.getStatusBarWindow().removeView(QSCustomizer.this);
+ setVisibility(View.GONE);
}
+ mNotifQsContainer.setCustomizerAnimating(false);
}
};
}
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 aa85f78..d95d3ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -28,8 +28,6 @@
import android.os.Handler;
import android.os.Looper;
import android.service.quicksettings.TileService;
-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.qs.QSTile.DrawableIcon;
@@ -57,10 +55,8 @@
}
private void addSystemTiles(QSTileHost host) {
- boolean hasColorMod = Prefs.getBoolean(host.getContext(), Key.QS_NIGHT_ADDED, false)
- && TunerService.isTunerEnabled(host.getContext());
String possible = mContext.getString(R.string.quick_settings_tiles_default)
- + ",hotspot,inversion,saver,work,cast" + (hasColorMod ? ",night" : "");
+ + ",hotspot,inversion,saver,work,cast,night";
String[] possibleTiles = possible.split(",");
final Handler qsHandler = new Handler(host.getLooper());
final Handler mainHandler = new Handler(Looper.getMainLooper());
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 0709992..db686a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -58,6 +58,7 @@
private final IBinder mToken = new Binder();
private final IQSTileService mService;
private final TileServiceManager mServiceManager;
+ private final int mUser;
private boolean mListening;
private boolean mBound;
@@ -71,6 +72,7 @@
mServiceManager = host.getTileServices().getTileWrapper(this);
mService = mServiceManager.getTileService();
mTile = new Tile(mComponent);
+ mUser = ActivityManager.getCurrentUser();
try {
PackageManager pm = mContext.getPackageManager();
ServiceInfo info = pm.getServiceInfo(mComponent, 0);
@@ -86,6 +88,10 @@
}
}
+ public int getUser() {
+ return mUser;
+ }
+
public ComponentName getComponent() {
return mComponent;
}
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 c4436f4..2aad161 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -85,6 +85,7 @@
mHandler = handler;
mIntent = intent;
mUser = user;
+ if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser);
}
public ComponentName getComponent() {
@@ -116,13 +117,13 @@
if (!checkComponentState()) {
return;
}
- if (DEBUG) Log.d(TAG, "Binding service " + mIntent);
+ if (DEBUG) Log.d(TAG, "Binding service " + mIntent + " " + mUser);
mBindTryCount++;
mContext.bindServiceAsUser(mIntent, this,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
mUser);
} else {
- if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent);
+ if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent + " " + mUser);
// Give it another chance next time it needs to be bound, out of kindness.
mBindTryCount = 0;
mWrapper = null;
@@ -350,7 +351,7 @@
@Override
public void onClick(IBinder iBinder) {
- if (DEBUG) Log.d(TAG, "onClick " + iBinder);
+ if (DEBUG) Log.d(TAG, "onClick " + iBinder + " " + mUser);
if (mWrapper == null || !mWrapper.onClick(iBinder)) {
mClickBinder = iBinder;
queueMessage(MSG_ON_CLICK);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index fa235d3..74b3fdc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -73,6 +73,15 @@
}
@Override
+ protected String composeChangeAnnouncement() {
+ if (mState.value) {
+ return mContext.getString(R.string.accessibility_quick_settings_data_saver_changed_on);
+ } else {
+ return mContext.getString(R.string.accessibility_quick_settings_data_saver_changed_off);
+ }
+ }
+
+ @Override
public void onDataSaverChanged(boolean isDataSaving) {
refreshState(isDataSaving);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
index cb8f0e7..9a00d95 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -16,6 +16,8 @@
package com.android.systemui.recents;
+import android.graphics.Rect;
+
/**
* Due to the fact that RecentsActivity is per-user, we need to establish an
* interface (this) for the non-system user to register itself for callbacks and to
@@ -27,6 +29,6 @@
void updateRecentsVisibility(boolean visible);
void startScreenPinning();
void sendRecentsDrawnEvent();
- void sendDockingTopTaskEvent(int dragMode);
+ void sendDockingTopTaskEvent(int dragMode, in Rect initialRect);
void sendLaunchRecentsEvent();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index f5ae351..2b6ed44 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -23,7 +23,9 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -35,13 +37,16 @@
import android.util.Log;
import android.view.Display;
import android.view.View;
+import android.widget.Toast;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
+import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockingTopTaskEvent;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
+import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -387,34 +392,48 @@
return false;
}
+ Point realSize = new Point();
+ if (initialBounds == null) {
+ mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
+ .getRealSize(realSize);
+ initialBounds = new Rect(0, 0, realSize.x, realSize.y);
+ }
+
int currentUser = sSystemServicesProxy.getCurrentUser();
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
boolean screenPinningActive = ssp.isScreenPinningActive();
boolean isTopTaskHome = topTask != null && SystemServicesProxy.isHomeStack(topTask.stackId);
if (topTask != null && !isTopTaskHome && !screenPinningActive) {
- if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds);
- } else {
- if (mSystemToUserCallbacks != null) {
- IRecentsNonSystemUserCallbacks callbacks =
- mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
- if (callbacks != null) {
- try {
- callbacks.dockTopTask(topTask.id, dragMode, stackCreateMode,
- initialBounds);
- } catch (RemoteException e) {
- Log.e(TAG, "Callback failed", e);
+ if (topTask.isDockable) {
+ if (sSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds);
+ } else {
+ if (mSystemToUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.dockTopTask(topTask.id, dragMode, stackCreateMode,
+ initialBounds);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
}
- } else {
- Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
}
}
+ mDraggingInRecentsCurrentUser = currentUser;
+ return true;
+ } else {
+ Toast.makeText(mContext, R.string.recents_drag_non_dockable_task_message,
+ Toast.LENGTH_SHORT).show();
+ return false;
}
- mDraggingInRecentsCurrentUser = currentUser;
- return true;
+ } else {
+ return false;
}
- return false;
}
@Override
@@ -513,8 +532,9 @@
* Handle Recents activity visibility changed.
*/
public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
- int processUser = event.systemServicesProxy.getProcessUser();
- if (event.systemServicesProxy.isSystemUser(processUser)) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ int processUser = ssp.getProcessUser();
+ if (ssp.isSystemUser(processUser)) {
mImpl.onVisibilityChanged(event.applicationContext, event.visible);
} else {
postToSystemUser(new Runnable() {
@@ -567,14 +587,15 @@
}
}
- public final void onBusEvent(final DockingTopTaskEvent event) {
+ public final void onBusEvent(final DockedTopTaskEvent event) {
int processUser = sSystemServicesProxy.getProcessUser();
if (!sSystemServicesProxy.isSystemUser(processUser)) {
postToSystemUser(new Runnable() {
@Override
public void run() {
try {
- mUserToSystemCallbacks.sendDockingTopTaskEvent(event.dragMode);
+ mUserToSystemCallbacks.sendDockingTopTaskEvent(event.dragMode,
+ event.initialRect);
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
@@ -599,6 +620,12 @@
}
}
+ public final void onBusEvent(ConfigurationChangedEvent event) {
+ // Update the configuration for the Recents component when the activity configuration
+ // changes as well
+ mImpl.onConfigurationChanged();
+ }
+
/**
* Attempts to register with the system user.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d7c12ba..473956f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -43,6 +43,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
@@ -54,7 +55,7 @@
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
-import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -96,6 +97,7 @@
private long mLastTabKeyEventTime;
private boolean mFinishedOnStartup;
private boolean mIgnoreAltTabRelease;
+ private boolean mIsVisible;
// Top level views
private RecentsView mRecentsView;
@@ -107,7 +109,7 @@
private RecentsAppWidgetHostView mSearchWidgetHostView;
// Runnables to finish the Recents activity
- private FinishRecentsRunnable mFinishLaunchHomeRunnable;
+ private Intent mHomeIntent;
// The trigger to automatically launch the current task
private int mFocusTimerDuration;
@@ -119,7 +121,7 @@
* last activity launch state. Generally we always launch home when we exit Recents rather than
* just finishing the activity since we don't know what is behind Recents in the task stack.
*/
- class FinishRecentsRunnable implements Runnable {
+ class LaunchHomeRunnable implements Runnable {
Intent mLaunchIntent;
ActivityOptions mOpts;
@@ -127,7 +129,7 @@
/**
* Creates a finish runnable that starts the specified intent.
*/
- public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
+ public LaunchHomeRunnable(Intent launchIntent, ActivityOptions opts) {
mLaunchIntent = launchIntent;
mOpts = opts;
}
@@ -173,59 +175,6 @@
}
};
- /** Updates the set of recent tasks */
- void updateRecentsTasks() {
- // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
- // reconstructing the task stack
- RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
- if (plan == null) {
- plan = loader.createLoadPlan(this);
- }
-
- // Start loading tasks according to the load plan
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- if (!plan.hasTasks()) {
- loader.preloadTasks(plan, -1, launchState.launchedFromHome);
- }
- RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
- loadOpts.runningTaskId = launchState.launchedToTaskId;
- loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
- loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
- loader.loadTasks(this, plan, loadOpts);
-
- TaskStack stack = plan.getTaskStack();
- mRecentsView.setTaskStack(stack);
-
- // Animate the SystemUI scrims into view
- Task launchTarget = stack.getLaunchTarget();
- int taskCount = stack.getTaskCount();
- int launchTaskIndexInStack = launchTarget != null
- ? stack.indexOfStackTask(launchTarget)
- : 0;
- boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
- boolean animateNavBarScrim = !launchState.launchedWhileDocking;
- mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
-
- // Keep track of whether we launched from the nav bar button or via alt-tab
- if (launchState.launchedWithAltTab) {
- MetricsLogger.count(this, "overview_trigger_alttab", 1);
- } else {
- MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
- }
- // Keep track of whether we launched from an app or from home
- if (launchState.launchedFromAppWithThumbnail) {
- MetricsLogger.count(this, "overview_source_app", 1);
- // If from an app, track the stack index of the app in the stack (for affiliated tasks)
- MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
- } else {
- MetricsLogger.count(this, "overview_source_home", 1);
- }
- // Keep track of the total stack task count
- MetricsLogger.histogram(this, "overview_task_count", taskCount);
- }
-
/**
* Dismisses the history view back into the stack view.
*/
@@ -294,12 +243,8 @@
void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) {
DismissRecentsToHomeAnimationStarted dismissEvent =
new DismissRecentsToHomeAnimationStarted(animateTaskViews);
- if (overrideAnimation != null) {
- dismissEvent.addPostAnimationCallback(new FinishRecentsRunnable(
- mFinishLaunchHomeRunnable.mLaunchIntent, overrideAnimation));
- } else {
- dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
- }
+ dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent,
+ overrideAnimation));
dismissEvent.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
@@ -348,6 +293,7 @@
// Set the Recents layout
setContentView(R.layout.recents);
+ takeKeyEvents(true);
mRecentsView = (RecentsView) findViewById(R.id.recents_view);
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
@@ -365,11 +311,10 @@
});
// Create the home intent runnable
- Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
+ mHomeIntent.addCategory(Intent.CATEGORY_HOME);
+ mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, null);
// Bind the search app widget when we first start up
if (RecentsDebugFlags.Static.EnableSearchBar) {
@@ -386,45 +331,12 @@
}
@Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- setIntent(intent);
- }
-
- @Override
protected void onStart() {
super.onStart();
- // Update the recent tasks
- updateRecentsTasks();
-
- // If this is a new instance from a configuration change, then we have to manually trigger
- // the enter animation state, or if recents was relaunched by AM, without going through
- // the normal mechanisms
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- boolean wasLaunchedByAm = !launchState.launchedFromHome &&
- !launchState.launchedFromAppWithThumbnail;
- if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
- EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
- }
-
// Notify that recents is now visible
- SystemServicesProxy ssp = Recents.getSystemServices();
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
-
+ EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
-
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(
- new ViewTreeObserver.OnPreDrawListener() {
-
- @Override
- public boolean onPreDraw() {
- mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
- EventBus.getDefault().post(new RecentsDrawnEvent());
- return true;
- }
- });
}
@Override
@@ -434,10 +346,90 @@
}
@Override
+ protected void onResume() {
+ super.onResume();
+
+ // If the Recents component has preloaded a load plan, then use that to prevent
+ // reconstructing the task stack
+ RecentsTaskLoader loader = Recents.getTaskLoader();
+ RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
+ if (loadPlan == null) {
+ loadPlan = loader.createLoadPlan(this);
+ }
+
+ // Start loading tasks according to the load plan
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
+ if (!loadPlan.hasTasks()) {
+ loader.preloadTasks(loadPlan, -1, launchState.launchedFromHome);
+ }
+
+ RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+ loadOpts.runningTaskId = launchState.launchedToTaskId;
+ loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+ loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
+ loader.loadTasks(this, loadPlan, loadOpts);
+ TaskStack stack = loadPlan.getTaskStack();
+ mRecentsView.onResume(mIsVisible, stack);
+
+ // Animate the SystemUI scrims into view
+ Task launchTarget = stack.getLaunchTarget();
+ int taskCount = stack.getTaskCount();
+ int launchTaskIndexInStack = launchTarget != null
+ ? stack.indexOfStackTask(launchTarget)
+ : 0;
+ boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
+ boolean animateNavBarScrim = !launchState.launchedWhileDocking;
+ mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
+
+ // If this is a new instance from a configuration change, then we have to manually trigger
+ // the enter animation state, or if recents was relaunched by AM, without going through
+ // the normal mechanisms
+ boolean wasLaunchedByAm = !launchState.launchedFromHome &&
+ !launchState.launchedFromApp;
+ if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
+ EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+ }
+
+ mRecentsView.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
+ EventBus.getDefault().post(new RecentsDrawnEvent());
+ return true;
+ }
+ });
+
+ // Keep track of whether we launched from the nav bar button or via alt-tab
+ if (launchState.launchedWithAltTab) {
+ MetricsLogger.count(this, "overview_trigger_alttab", 1);
+ } else {
+ MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
+ }
+
+ // Keep track of whether we launched from an app or from home
+ if (launchState.launchedFromApp) {
+ MetricsLogger.count(this, "overview_source_app", 1);
+ // If from an app, track the stack index of the app in the stack (for affiliated tasks)
+ MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
+ } else {
+ MetricsLogger.count(this, "overview_source_home", 1);
+ }
+
+ // Keep track of the total stack task count
+ MetricsLogger.histogram(this, "overview_task_count", taskCount);
+
+ // After we have resumed, set the visible state until the next onStop() call
+ mIsVisible = true;
+ }
+
+ @Override
protected void onPause() {
super.onPause();
- // Stop the fast-toggle dozer
+ mIgnoreAltTabRelease = false;
mIterateTrigger.stopDozing();
}
@@ -445,15 +437,15 @@
protected void onStop() {
super.onStop();
- // Reset some states
- mIgnoreAltTabRelease = false;
+ // Only hide the history if Recents is completely hidden
if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
}
// Notify that recents is now hidden
- SystemServicesProxy ssp = Recents.getSystemServices();
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
+ mIsVisible = false;
+ EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
+ MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not
@@ -461,8 +453,6 @@
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
launchState.reset();
-
- MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
}
@Override
@@ -528,16 +518,23 @@
@Override
public void onMultiWindowChanged(boolean inMultiWindow) {
super.onMultiWindowChanged(inMultiWindow);
+ EventBus.getDefault().send(new ConfigurationChangedEvent());
+
+ // Reload the task stack completely
+ RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
RecentsTaskLoader loader = Recents.getTaskLoader();
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.loadIcons = false;
- launchOpts.loadThumbnails = false;
- launchOpts.onlyLoadForCache = true;
RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
- loader.preloadTasks(loadPlan, -1, false);
- loader.loadTasks(this, loadPlan, launchOpts);
- EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack(),
- inMultiWindow));
+ loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
+
+ RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
+ loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
+ loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
+ loader.loadTasks(this, loadPlan, loadOpts);
+
+ mRecentsView.onResume(mIsVisible, loadPlan.getTaskStack());
+
+ EventBus.getDefault().send(new MultiWindowStateChangedEvent(inMultiWindow));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index aa1437b..ec4820a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -28,7 +28,8 @@
public class RecentsActivityLaunchState {
public boolean launchedWithAltTab;
- public boolean launchedFromAppWithThumbnail;
+ public boolean launchedFromApp;
+ public boolean launchedFromAppDocked;
public boolean launchedFromHome;
public boolean launchedFromSearchHome;
public boolean launchedReuseTaskStackViews;
@@ -42,7 +43,8 @@
public void reset() {
launchedFromHome = false;
launchedFromSearchHome = false;
- launchedFromAppWithThumbnail = false;
+ launchedFromApp = false;
+ launchedFromAppDocked = false;
launchedToTaskId = -1;
launchedWithAltTab = false;
launchedHasConfigurationChanged = false;
@@ -67,7 +69,7 @@
public int getInitialFocusTaskIndex(int numTasks) {
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (launchedFromAppWithThumbnail) {
+ 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
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 9e43bb4..eec0411 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -69,7 +69,6 @@
public final int smallestWidth;
/** Misc **/
- public boolean useHardwareLayers;
public boolean fakeShadows;
public int svelteLevel;
public int searchBarSpaceHeightPx;
@@ -80,7 +79,6 @@
SystemServicesProxy ssp = Recents.getSystemServices();
Context appContext = context.getApplicationContext();
Resources res = appContext.getResources();
- useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers);
fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
svelteLevel = res.getInteger(R.integer.recents_svelte_level);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index cd64323..6feda81 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -39,8 +39,8 @@
public static final boolean EnableAffiliatedTaskGroups = true;
// Enables the history
public static final boolean EnableHistory = false;
- // Overrides the Tuner flags and enables the fast toggle and timeout
- public static final boolean EnableFastToggleTimeoutOverride = true;
+ // Overrides the Tuner flags and enables the timeout
+ private static final boolean EnableFastToggleTimeout = false;
// Enables us to create mock recents tasks
public static final boolean EnableMockTasks = false;
@@ -54,9 +54,9 @@
public static final int MockTaskGroupsTaskCount = 12;
}
- private static final String KEY_DISABLE_FAST_TOGGLE = "overview_disable_fast_toggle_via_button";
+ private static final String KEY_ENABLE_PAGING = "overview_enable_paging";
- private boolean mDisableFastToggleRecents;
+ private boolean mEnablePaging;
/**
* We read the prefs once when we start the activity, then update them as the tuner changes
@@ -65,31 +65,32 @@
public RecentsDebugFlags(Context context) {
// Register all our flags, this will also call onTuningChanged() for each key, which will
// initialize the current state of each flag
- TunerService.get(context).addTunable(this, KEY_DISABLE_FAST_TOGGLE);
+ TunerService.get(context).addTunable(this, KEY_ENABLE_PAGING);
}
/**
* @return whether we are enabling fast toggling.
*/
public boolean isFastToggleRecentsEnabled() {
- // These checks EnableFastToggleTimeoutOverride
SystemServicesProxy ssp = Recents.getSystemServices();
- if (mDisableFastToggleRecents || ssp.hasFreeformWorkspaceSupport() || ssp.hasDockedTask()
- || ssp.isTouchExplorationEnabled()) {
+ if (ssp.hasFreeformWorkspaceSupport() || ssp.isTouchExplorationEnabled()) {
return false;
}
- if (Static.EnableFastToggleTimeoutOverride) {
- return true;
- }
- return true;
+ return Static.EnableFastToggleTimeout;
+ }
+
+ /**
+ * @return whether we are enabling paging.
+ */
+ public boolean isPagingEnabled() {
+ return mEnablePaging;
}
@Override
public void onTuningChanged(String key, String newValue) {
switch (key) {
- case KEY_DISABLE_FAST_TOGGLE:
- mDisableFastToggleRecents = (newValue != null) &&
- (Integer.parseInt(newValue) != 0);
+ case KEY_ENABLE_PAGING:
+ mEnablePaging = (newValue != null) && (Integer.parseInt(newValue) != 0);
break;
}
EventBus.getDefault().send(new DebugFlagsChangedEvent());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 86b03c8..d864df8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ITaskStackListener;
@@ -45,7 +47,7 @@
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockingTopTaskEvent;
+import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
@@ -66,6 +68,7 @@
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskStackViewScroller;
import com.android.systemui.recents.views.TaskViewHeader;
import com.android.systemui.recents.views.TaskViewTransform;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -74,8 +77,6 @@
import java.util.ArrayList;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-
/**
* An implementation of the Recents component for the current user. For secondary users, this can
* be called remotely from the system user.
@@ -98,6 +99,7 @@
//Used to store tv or non-tv activty for use in creating intents.
private final String mRecentsIntentActivityName;
+
/**
* An implementation of ITaskStackListener, that allows us to listen for changes to the system
* task stacks and update recents accordingly.
@@ -276,28 +278,24 @@
mTriggeredFromAltTab = triggeredFromAltTab;
mDraggingInRecents = draggingInRecents;
mLaunchedWhileDocking = launchedWhileDockingTask;
- if (mFastAltTabTrigger.hasTriggered()) {
- // We are calling this from the doze trigger, so just fall through to show Recents
- mFastAltTabTrigger.resetTrigger();
+ if (mFastAltTabTrigger.isAsleep()) {
+ // Fast alt-tab duration has elapsed, fall through to showing Recents and reset
+ mFastAltTabTrigger.stopDozing();
} else if (mFastAltTabTrigger.isDozing()) {
- // We are dozing but haven't yet triggered, ignore this if this is not another alt-tab,
- // otherwise, this is an additional tab (alt-tab*), which means that we should trigger
- // immediately (fall through and disable the pending trigger)
- // TODO: This is tricky, we need to handle the tab key, but Recents has not yet started
- // so we may actually additional signal to handle multiple quick tab cases. The
- // severity of this is inversely proportional to the FAST_ALT_TAB_DELAY_MS
- // duration though
+ // Fast alt-tab duration has not elapsed. If this is triggered by a different
+ // showRecents() call, then ignore that call for now.
+ // TODO: We can not handle quick tabs that happen between the initial showRecents() call
+ // that started the activity and the activity starting up. The severity of this
+ // is inversely proportional to the FAST_ALT_TAB_DELAY_MS duration though.
if (!triggeredFromAltTab) {
return;
}
mFastAltTabTrigger.stopDozing();
- } else {
- // Otherwise, the doze trigger is not running, and if this is an alt tab, we should
- // start the trigger and then wait for the hide (or for it to elapse)
- if (triggeredFromAltTab) {
- mFastAltTabTrigger.startDozing();
- return;
- }
+ } else if (triggeredFromAltTab) {
+ // The fast alt-tab detector is not yet running, so start the trigger and wait for the
+ // hideRecents() call, or for the fast alt-tab duration to elapse
+ mFastAltTabTrigger.startDozing();
+ return;
}
try {
@@ -321,7 +319,6 @@
// Cancel the fast alt-tab trigger
mFastAltTabTrigger.stopDozing();
- mFastAltTabTrigger.resetTrigger();
return;
}
@@ -348,12 +345,14 @@
long elapsedTime = SystemClock.elapsedRealtime() - mLastToggleTime;
if (topTask != null && ssp.isRecentsTopMost(topTask, isTopTaskHome)) {
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!launchState.launchedWithAltTab) {
// If the user taps quickly
- if (ViewConfiguration.getDoubleTapMinTime() < elapsedTime &&
- elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
+ if (!debugFlags.isPagingEnabled() ||
+ (ViewConfiguration.getDoubleTapMinTime() < elapsedTime &&
+ elapsedTime < ViewConfiguration.getDoubleTapTimeout())) {
// Launch the next focused task
EventBus.getDefault().post(new LaunchNextTaskRequestEvent());
} else {
@@ -569,12 +568,12 @@
// Make sure we inform DividerView before we actually start the activity so we can change
// the resize mode already.
if (ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds)) {
- EventBus.getDefault().send(new DockingTopTaskEvent(dragMode));
+ EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
showRecents(
false /* triggeredFromAltTab */,
dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
false /* animate */,
- true /* reloadTasks*/);
+ true /* launchedWhileDockingTask*/);
}
}
@@ -602,7 +601,7 @@
com.android.internal.R.dimen.navigation_bar_width);
mTaskBarHeight = res.getDimensionPixelSize(
R.dimen.recents_task_bar_height);
- mDummyStackView = new TaskStackView(mContext, new TaskStack());
+ mDummyStackView = new TaskStackView(mContext);
mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
null, false);
}
@@ -615,8 +614,7 @@
* is not already bound (can be expensive)
* @param stack the stack to initialize the stack layout with
*/
- private void updateHeaderBarLayout(boolean tryAndBindSearchWidget,
- TaskStack stack) {
+ private void updateHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) {
RecentsConfiguration config = Recents.getConfiguration();
SystemServicesProxy ssp = Recents.getSystemServices();
Rect systemInsets = new Rect();
@@ -640,14 +638,16 @@
mSearchBarBounds, mTaskStackBounds);
// Rebind the header bar and draw it for the transition
- TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
+ TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
Rect taskStackBounds = new Rect(mTaskStackBounds);
- algo.setSystemInsets(systemInsets);
+ stackLayout.setSystemInsets(systemInsets);
if (stack != null) {
- algo.initialize(taskStackBounds,
+ stackLayout.initialize(taskStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
+ mDummyStackView.setTasks(stack, false /* notifyStackChanges */,
+ false /* relayoutTaskStack */);
}
- Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
+ Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
if (!taskViewBounds.equals(mLastTaskViewBounds)) {
mLastTaskViewBounds.set(taskViewBounds);
@@ -705,10 +705,8 @@
updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
// Update the destination rect
- mDummyStackView.updateLayoutForStack(stack);
final Task toTask = new Task();
- final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
- toTask);
+ final TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
@@ -754,17 +752,21 @@
* Creates the activity options for an app->recents transition.
*/
private ActivityOptions getThumbnailTransitionActivityOptions(
- ActivityManager.RunningTaskInfo topTask, TaskStack stack, TaskStackView stackView) {
+ ActivityManager.RunningTaskInfo topTask, TaskStackView stackView) {
if (topTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
- stackView.getScroller().setStackScrollToInitialState();
- ArrayList<Task> tasks = stack.getStackTasks();
+ ArrayList<Task> tasks = stackView.getStack().getStackTasks();
+ TaskStackLayoutAlgorithm stackLayout = stackView.getStackAlgorithm();
+ TaskStackViewScroller stackScroller = stackView.getScroller();
+
+ stackView.updateLayoutAlgorithm(true /* boundScroll */);
+ stackView.updateToInitialState();
+
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
if (task.isFreeformTask()) {
- mTmpTransform = stackView.getStackAlgorithm()
- .getStackTransformScreenCoordinates(task,
- stackView.getScroller().getStackScroll(), mTmpTransform, null);
+ mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
+ stackScroller.getStackScroll(), mTmpTransform, null);
Rect toTaskRect = new Rect();
mTmpTransform.rect.round(toTaskRect);
Bitmap thumbnail = getThumbnailBitmap(topTask, task, mTmpTransform);
@@ -778,8 +780,7 @@
} else {
// Update the destination rect
Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
- toTask);
+ TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
RectF toTaskRect = toTransform.rect;
Bitmap thumbnail = getThumbnailBitmap(topTask, toTask, toTransform);
if (thumbnail != null) {
@@ -811,9 +812,10 @@
/**
* Returns the transition rect for the given task id.
*/
- private TaskViewTransform getThumbnailTransitionTransform(TaskStack stack,
- TaskStackView stackView, Task runningTaskOut) {
+ private TaskViewTransform getThumbnailTransitionTransform(TaskStackView stackView,
+ Task runningTaskOut) {
// Find the running task in the TaskStack
+ TaskStack stack = stackView.getStack();
Task launchTask = stack.getLaunchTarget();
if (launchTask != null) {
runningTaskOut.copyFrom(launchTask);
@@ -824,7 +826,8 @@
}
// Get the transform for the running task
- stackView.getScroller().setStackScrollToInitialState();
+ stackView.updateLayoutAlgorithm(true /* boundScroll */);
+ stackView.updateToInitialState();
mTmpTransform = stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask,
stackView.getScroller().getStackScroll(), mTmpTransform, null);
return mTmpTransform;
@@ -852,6 +855,7 @@
c.scale(toTransform.scale, toTransform.scale);
mHeaderBar.rebindToTask(toTask, false /* touchExplorationEnabled */,
disabledInSafeMode);
+ mHeaderBar.setDimAlpha(toTransform.dimAlpha);
mHeaderBar.draw(c);
c.setBitmap(null);
}
@@ -884,7 +888,6 @@
updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
// Prepare the dummy stack for the transition
- mDummyStackView.updateLayoutForStack(stack);
TaskStackLayoutAlgorithm.VisibilityReport stackVr =
mDummyStackView.computeStackVisibilityReport();
@@ -900,8 +903,7 @@
if (useThumbnailTransition) {
// Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
- mDummyStackView);
+ ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView);
if (opts != null) {
startRecentsActivity(topTask, opts, false /* fromHome */,
false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
@@ -948,14 +950,15 @@
* Starts the recents activity.
*/
private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
- TaskStackLayoutAlgorithm.VisibilityReport vr) {
+ ActivityOptions opts, boolean fromHome, boolean fromSearchHome,
+ boolean fromThumbnail, TaskStackLayoutAlgorithm.VisibilityReport vr) {
// Update the configuration based on the launch options
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
launchState.launchedFromHome = fromSearchHome || fromHome;
launchState.launchedFromSearchHome = fromSearchHome;
- launchState.launchedFromAppWithThumbnail = fromThumbnail;
+ launchState.launchedFromApp = fromThumbnail || mLaunchedWhileDocking;
+ launchState.launchedFromAppDocked = mLaunchedWhileDocking;
launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
launchState.launchedWithAltTab = mTriggeredFromAltTab;
launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
index f8000b8..ffeb4a1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents;
import android.content.Context;
+import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.EventLog;
@@ -26,7 +27,7 @@
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockingTopTaskEvent;
+import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
@@ -91,8 +92,8 @@
}
@Override
- public void sendDockingTopTaskEvent(int dragMode) throws RemoteException {
- EventBus.getDefault().post(new DockingTopTaskEvent(dragMode));
+ public void sendDockingTopTaskEvent(int dragMode, Rect initialRect) throws RemoteException {
+ EventBus.getDefault().post(new DockedTopTaskEvent(dragMode, initialRect));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
index 264c2c4..0ad4681 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ConfigurationChangedEvent.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.recents.events.activity;
@@ -19,13 +19,8 @@
import com.android.systemui.recents.events.EventBus;
/**
- * Fires when the user invoked the gesture to dock the top/left task.
+ * This is sent when the Recents activity configuration has changed.
*/
-public class DockingTopTaskEvent extends EventBus.Event {
-
- public int dragMode;
-
- public DockingTopTaskEvent(int dragMode) {
- this.dragMode = dragMode;
- }
+public class ConfigurationChangedEvent extends EventBus.AnimatedEvent {
+ // Simple event
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
similarity index 74%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
index 264c2c4..f1bc214 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockedTopTaskEvent.java
@@ -16,16 +16,21 @@
package com.android.systemui.recents.events.activity;
+import android.graphics.Rect;
+
import com.android.systemui.recents.events.EventBus;
/**
- * Fires when the user invoked the gesture to dock the top/left task.
+ * Fires when the user invoked the gesture to dock the top/left task after we called into window
+ * manager and before we start recents.
*/
-public class DockingTopTaskEvent extends EventBus.Event {
+public class DockedTopTaskEvent extends EventBus.Event {
public int dragMode;
+ public Rect initialRect;
- public DockingTopTaskEvent(int dragMode) {
+ public DockedTopTaskEvent(int dragMode, Rect initialRect) {
this.dragMode = dragMode;
+ this.initialRect = initialRect;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.java
new file mode 100644
index 0000000..04ca68f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskEvent.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.systemui.recents.events.activity;
+
+
+import android.graphics.Rect;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+public class LaunchTvTaskEvent extends EventBus.Event {
+
+ public final TaskCardView taskView;
+ public final Task task;
+ public final Rect targetTaskBounds;
+ public final int targetTaskStack;
+
+ public LaunchTvTaskEvent(TaskCardView taskView, Task task, Rect targetTaskBounds,
+ int targetTaskStack) {
+ this.taskView = taskView;
+ this.task = task;
+ this.targetTaskBounds = targetTaskBounds;
+ this.targetTaskStack = targetTaskStack;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java
index 264c2c4..75d3efa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTvTaskStartedEvent.java
@@ -11,21 +11,24 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.tv.views.TaskCardView;
/**
- * Fires when the user invoked the gesture to dock the top/left task.
+ * This event is sent following {@link LaunchTvTaskEvent} after the call to the system is made to
+ * start the task, only used on TV.
*/
-public class DockingTopTaskEvent extends EventBus.Event {
+public class LaunchTvTaskStartedEvent extends EventBus.AnimatedEvent {
- public int dragMode;
+ public final TaskCardView taskView;
- public DockingTopTaskEvent(int dragMode) {
- this.dragMode = dragMode;
+ public LaunchTvTaskStartedEvent(TaskCardView taskView) {
+ this.taskView = taskView;
}
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
copy to packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
index 264c2c4..19245d9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/MultiWindowStateChangedEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT 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.systemui.recents.events.activity;
@@ -19,13 +19,13 @@
import com.android.systemui.recents.events.EventBus;
/**
- * Fires when the user invoked the gesture to dock the top/left task.
+ * This is sent by the activity whenever the multi-window state has changed.
*/
-public class DockingTopTaskEvent extends EventBus.Event {
+public class MultiWindowStateChangedEvent extends EventBus.Event {
- public int dragMode;
+ public final boolean inMultiWindow;
- public DockingTopTaskEvent(int dragMode) {
- this.dragMode = dragMode;
+ public MultiWindowStateChangedEvent(boolean inMultiWindow) {
+ this.inMultiWindow = inMultiWindow;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
index 4140bcd..8843eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
@@ -19,21 +19,18 @@
import android.content.Context;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.misc.SystemServicesProxy;
/**
- * This is sent when the visibility of the RecentsActivity for the current user changes.
+ * This is sent when the visibility of the RecentsActivity for the current user changes. Handlers
+ * of this event should not alter the UI, as the activity may still be visible.
*/
public class RecentsVisibilityChangedEvent extends EventBus.Event {
public final Context applicationContext;
- public final SystemServicesProxy systemServicesProxy;
public final boolean visible;
- public RecentsVisibilityChangedEvent(Context context, SystemServicesProxy systemServicesProxy,
- boolean visible) {
+ public RecentsVisibilityChangedEvent(Context context, boolean visible) {
this.applicationContext = context.getApplicationContext();
- this.systemServicesProxy = systemServicesProxy;
this.visible = visible;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index 95aa10f..574ea03 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -30,7 +30,7 @@
@ViewDebug.ExportedProperty(category="recents")
boolean mIsDozing;
@ViewDebug.ExportedProperty(category="recents")
- boolean mHasTriggered;
+ boolean mIsAsleep;
@ViewDebug.ExportedProperty(category="recents")
int mDozeDurationMilliseconds;
Runnable mOnSleepRunnable;
@@ -40,7 +40,7 @@
@Override
public void run() {
mIsDozing = false;
- mHasTriggered = true;
+ mIsAsleep = true;
mOnSleepRunnable.run();
}
};
@@ -56,7 +56,7 @@
*/
public void startDozing() {
forcePoke();
- mHasTriggered = false;
+ mIsAsleep = false;
}
/**
@@ -65,6 +65,7 @@
public void stopDozing() {
mHandler.removeCallbacks(mDozeRunnable);
mIsDozing = false;
+ mIsAsleep = false;
}
/**
@@ -99,12 +100,7 @@
}
/** Returns whether the trigger has fired at least once. */
- public boolean hasTriggered() {
- return mHasTriggered;
- }
-
- /** Resets the doze trigger state. */
- public void resetTrigger() {
- mHasTriggered = false;
+ public boolean isAsleep() {
+ return mIsAsleep;
}
}
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 7c5a931..532e796 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -695,6 +695,37 @@
}
/**
+ * Returns a banner used on TV for the specified Activity.
+ */
+ public Drawable getActivityBanner(ActivityInfo info) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock banner
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ Drawable banner = info.loadBanner(mPm);
+ return banner;
+ }
+
+ /**
+ * Returns a logo used on TV for the specified Activity.
+ */
+ public Drawable getActivityLogo(ActivityInfo info) {
+ if (mPm == null) return null;
+
+ // If we are mocking, then return a mock logo
+ if (RecentsDebugFlags.Static.EnableMockTasks) {
+ return new ColorDrawable(0xFF666666);
+ }
+
+ Drawable logo = info.loadLogo(mPm);
+ return logo;
+ }
+
+
+ /**
* Returns the given label for a user, badging if necessary.
*/
private String getBadgedLabel(String label, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index abcb563..6ae07fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -212,8 +212,7 @@
// Initialize the stacks
mStack = new TaskStack();
- mStack.setTasks(allTasks, false /* notifyStackChanges */);
- mStack.createAffiliatedGroupings(mContext);
+ mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index e5d4f1b..b5a5949 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -100,11 +100,7 @@
@Override
public String toString() {
- return "Task.Key: " + id + ", "
- + "s: " + stackId + ", "
- + "u: " + userId + ", "
- + "lat: " + lastActiveTime + ", "
- + getComponent().getPackageName();
+ return "t" + id + ", s" + stackId + ", u" + userId;
}
private void updateHashCode() {
@@ -204,7 +200,9 @@
this.isDockable = isDockable;
}
- /** Copies the other task. */
+ /**
+ * Copies the metadata from another task, but retains the current callbacks.
+ */
public void copyFrom(Task o) {
this.key = o.key;
this.group = o.group;
@@ -300,11 +298,6 @@
@Override
public String toString() {
- String groupAffiliation = "no group";
- if (group != null) {
- groupAffiliation = Integer.toString(group.affiliation);
- }
- return "Task (" + groupAffiliation + "): " + key +
- " [" + super.toString() + "]";
+ return "[" + key.toString() + "] " + title;
}
}
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 1f91dce..4d1c552 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -92,15 +92,6 @@
}
}
- /**
- * Resets the task list, but does not remove the filter.
- */
- void reset() {
- mTasks.clear();
- mFilteredTasks.clear();
- mTaskIndices.clear();
- }
-
/** Removes the task filter and returns the previous touch state */
void removeFilter() {
mFilter = null;
@@ -481,15 +472,6 @@
mCb = cb;
}
- /** Resets this TaskStack. */
- public void reset() {
- mCb = null;
- mStackTaskList.reset();
- mHistoryTaskList.reset();
- mGroups.clear();
- mAffinitiesGroups.clear();
- }
-
/**
* Moves the given task to either the front of the freeform workspace or the stack.
*/
@@ -556,12 +538,12 @@
* @param tasks the new set of tasks to replace the current set.
* @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
*/
- public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
+ public void setTasks(Context context, List<Task> tasks, boolean notifyStackChanges) {
// Compute a has set for each of the tasks
ArrayMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
ArrayMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
-
- ArrayList<Task> newTasks = new ArrayList<>();
+ ArrayList<Task> addedTasks = new ArrayList<>();
+ ArrayList<Task> allTasks = new ArrayList<>();
// Disable notifications if there are no callbacks
if (mCb == null) {
@@ -570,10 +552,13 @@
// Remove any tasks that no longer exist
int taskCount = mRawTaskList.size();
- for (int i = 0; i < taskCount; i++) {
+ for (int i = taskCount - 1; i >= 0; i--) {
Task task = mRawTaskList.get(i);
if (!newTasksMap.containsKey(task.key)) {
if (notifyStackChanges) {
+ // If we are notifying, then remove the task now, otherwise the raw task list
+ // will be reset at the end of this method
+ removeTask(task, AnimationProps.IMMEDIATE);
mCb.onStackTaskRemoved(this, task, i == (taskCount - 1), null,
AnimationProps.IMMEDIATE);
}
@@ -584,26 +569,28 @@
// Add any new tasks
taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (!currentTasksMap.containsKey(task.key)) {
- if (notifyStackChanges) {
- mCb.onStackTaskAdded(this, task);
- }
- newTasks.add(task);
- } else {
- newTasks.add(currentTasksMap.get(task.key));
+ Task newTask = tasks.get(i);
+ Task currentTask = currentTasksMap.get(newTask.key);
+ if (currentTask == null && notifyStackChanges) {
+ addedTasks.add(newTask);
+ } else if (currentTask != null) {
+ // The current task has bound callbacks, so just copy the data from the new task
+ // state and add it back into the list
+ currentTask.copyFrom(newTask);
+ newTask = currentTask;
}
+ allTasks.add(newTask);
}
// Sort all the tasks to ensure they are ordered correctly
- Collections.sort(newTasks, FREEFORM_LAST_ACTIVE_TIME_COMPARATOR);
+ Collections.sort(allTasks, FREEFORM_LAST_ACTIVE_TIME_COMPARATOR);
// Filter out the historical tasks from this new list
ArrayList<Task> stackTasks = new ArrayList<>();
ArrayList<Task> historyTasks = new ArrayList<>();
- int newTaskCount = newTasks.size();
+ int newTaskCount = allTasks.size();
for (int i = 0; i < newTaskCount; i++) {
- Task task = newTasks.get(i);
+ Task task = allTasks.get(i);
if (task.isHistorical) {
historyTasks.add(task);
} else {
@@ -613,10 +600,16 @@
mStackTaskList.set(stackTasks);
mHistoryTaskList.set(historyTasks);
- mRawTaskList.clear();
- mRawTaskList.addAll(newTasks);
- mGroups.clear();
- mAffinitiesGroups.clear();
+ mRawTaskList = allTasks;
+
+ // Only callback for the newly added tasks after this stack has been updated
+ int addedTaskCount = addedTasks.size();
+ for (int i = 0; i < addedTaskCount; i++) {
+ mCb.onStackTaskAdded(this, addedTasks.get(i));
+ }
+
+ // Update the affiliated groupings
+ createAffiliatedGroupings(context);
}
/**
@@ -779,9 +772,12 @@
}
/**
- * Temporary: This method will simulate affiliation groups by
+ * Temporary: This method will simulate affiliation groups
*/
- public void createAffiliatedGroupings(Context context) {
+ void createAffiliatedGroupings(Context context) {
+ mGroups.clear();
+ mAffinitiesGroups.clear();
+
if (RecentsDebugFlags.Static.EnableMockTaskGroups) {
ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
// Sort all tasks by increasing firstActiveTime of the task
@@ -926,13 +922,13 @@
@Override
public String toString() {
- String str = "Stack Tasks:\n";
+ String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
for (Task t : mStackTaskList.getTasks()) {
- str += " " + t.toString() + "\n";
+ str += " " + t.toString() + "\n";
}
- str += "Historical Tasks:\n";
+ str += "Historical Tasks(" + mHistoryTaskList.size() + "):\n";
for (Task t : mHistoryTaskList.getTasks()) {
- str += " " + t.toString() + "\n";
+ str += " " + t.toString() + "\n";
}
return str;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 0c48cf7..dae522f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -40,7 +40,6 @@
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
-import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
@@ -60,6 +59,8 @@
import com.android.systemui.tv.pip.PipManager;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* The main TV recents activity started by the RecentsImpl.
@@ -157,11 +158,13 @@
mRecentsView.setTaskStack(stack);
+ List stackTasks = stack.getStackTasks();
+ Collections.reverse(stackTasks);
if (mTaskStackViewAdapter == null) {
- mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stack.getStackTasks());
+ mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stackTasks);
mRecentsView.setTaskStackViewAdapter(mTaskStackViewAdapter);
} else {
- mTaskStackViewAdapter.setNewStackTasks(stack.getStackTasks());
+ mTaskStackViewAdapter.setNewStackTasks(stackTasks);
}
if (launchState.launchedToTaskId != -1) {
@@ -284,14 +287,14 @@
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
- !launchState.launchedFromAppWithThumbnail;
+ !launchState.launchedFromApp;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
// Notify that recents is now visible
SystemServicesProxy ssp = Recents.getSystemServices();
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
+ EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
if (mPipManager.isPipShown()) {
// Place mPipView at the PIP bounds for fine tuned focus handling.
@@ -340,8 +343,7 @@
mPipManager.removeListener(mPipListener);
mIgnoreAltTabRelease = false;
// Notify that recents is now hidden
- SystemServicesProxy ssp = Recents.getSystemServices();
- EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
+ EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
new file mode 100644
index 0000000..ef8d48e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
@@ -0,0 +1,132 @@
+/*
+ * 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.recents.tv.views;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.*;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+
+public class RecentsTvTransitionHelper {
+ private static final String TAG = "RecentsTvTransitionHelper";
+
+ private Context mContext;
+ private Handler mHandler;
+
+ public RecentsTvTransitionHelper(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ }
+
+ public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task,
+ final TaskStackHorizontalGridView stackView, final TaskCardView taskView,
+ final Rect bounds, int destinationStack) {
+ final ActivityOptions opts = ActivityOptions.makeBasic();
+ if (bounds != null) {
+ opts.setLaunchBounds(bounds.isEmpty() ? null : bounds);
+ }
+
+ final ActivityOptions.OnAnimationStartedListener animStartedListener;
+ if (task.thumbnail != null && task.thumbnail.getWidth() > 0 &&
+ task.thumbnail.getHeight() > 0) {
+ animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+ }
+ };
+ } else {
+ // This is only the case if the task is not on screen (scrolled offscreen for example)
+ animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+ }
+ };
+ }
+
+ if (taskView == null) {
+ // If there is no task view, then we do not need to worry about animating out occluding
+ // task views, and we can launch immediately
+ startTaskActivity(stack, task, taskView, opts, animStartedListener);
+ } else {
+ LaunchTvTaskStartedEvent launchStartedEvent = new LaunchTvTaskStartedEvent(taskView);
+ EventBus.getDefault().send(launchStartedEvent);
+ startTaskActivity(stack, task, taskView, opts, animStartedListener);
+ }
+ }
+
+ private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskCardView taskView,
+ ActivityOptions opts,final ActivityOptions.OnAnimationStartedListener animStartedListener) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) {
+ // Keep track of the index of the task launch
+ int taskIndexFromFront = 0;
+ int taskIndex = stack.indexOfStackTask(task);
+ if (taskIndex > -1) {
+ taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
+ }
+ EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
+ } else {
+ // Keep track of failed launches
+ EventBus.getDefault().send(new LaunchTaskFailedEvent());
+ }
+
+ IRemoteCallback.Stub callback = null;
+ if (animStartedListener != null) {
+ callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (animStartedListener != null) {
+ animStartedListener.onAnimationStarted();
+ }
+ }
+ });
+ }
+ };
+ }
+ try {
+ Rect taskRect = taskView.getGlobalRect();
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionThumb(task.thumbnail, taskRect.left,
+ taskRect.top, callback, true);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to override transition: " + e);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
index 8e768a2..bf6229c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.graphics.Rect;
+import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -31,7 +32,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
+import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
@@ -53,7 +54,8 @@
private View mEmptyView;
private boolean mAwaitingFirstLayout = true;
private Rect mSystemInsets = new Rect();
-
+ private RecentsTvTransitionHelper mTransitionHelper;
+ private Handler mHandler;
public RecentsTvView(Context context) {
this(context, null);
@@ -75,6 +77,8 @@
LayoutInflater inflater = LayoutInflater.from(context);
mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
addView(mEmptyView);
+ mHandler = new Handler();
+ mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler);
}
public void setTaskStack(TaskStack stack) {
@@ -209,6 +213,11 @@
/**** EventBus Events ****/
+ public final void onBusEvent(LaunchTvTaskEvent event) {
+ mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackHorizontalView,
+ event.taskView, event.targetTaskBounds, event.targetTaskStack);
+ }
+
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
// If we are going home, cancel the previous task's window transition
EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index e275f22..7d8a3ce 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -16,20 +16,20 @@
package com.android.systemui.recents.tv.views;
import android.content.Context;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.ImageView;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.systemui.R;
-import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
+import com.android.systemui.recents.model.Task;
-public class TaskCardView extends RelativeLayout {
+public class TaskCardView extends LinearLayout {
private ImageView mThumbnailView;
private TextView mTitleTextView;
- private TextView mContentTextView;
private ImageView mBadgeView;
private Task mTask;
@@ -52,7 +52,6 @@
protected void onFinishInflate() {
mThumbnailView = (ImageView) findViewById(R.id.card_view_thumbnail);
mTitleTextView = (TextView) findViewById(R.id.card_title_text);
- mContentTextView = (TextView) findViewById(R.id.card_content_text);
mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
}
@@ -60,11 +59,27 @@
mTask = task;
mThumbnailView.setImageBitmap(task.thumbnail);
mTitleTextView.setText(task.title);
- mContentTextView.setText(task.contentDescription);
mBadgeView.setImageDrawable(task.icon);
}
public Task getTask() {
return mTask;
}
+
+ @Override
+ public void getFocusedRect(Rect r) {
+ mThumbnailView.getFocusedRect(r);
+ }
+
+ public Rect getFocusedRect() {
+ Rect r = new Rect();
+ getFocusedRect(r);
+ return r;
+ }
+
+ public Rect getGlobalRect() {
+ Rect r = new Rect();
+ getGlobalVisibleRect(r);
+ return r;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 58ec852..4458639 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -15,7 +15,6 @@
*/
package com.android.systemui.recents.tv.views;
-
import android.content.Context;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.util.AttributeSet;
@@ -36,13 +35,17 @@
/**
* Horizontal Grid View Implementation to show the Task Stack for TV.
*/
-public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks{
+public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks {
private TaskStack mStack;
private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
private Task mFocusedTask;
+ public TaskStackHorizontalGridView(Context context) {
+ this(context, null);
+ }
+
public TaskStackHorizontalGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -64,8 +67,6 @@
* Resets this view for reuse.
*/
public void reset() {
- // Reset the focused task
- resetFocusedTask(getFocusedTask());
requestLayout();
}
@@ -73,12 +74,6 @@
* @param task - Task to reset
*/
private void resetFocusedTask(Task task) {
- if (task != null) {
- TaskCardView tv = getChildViewForTask(task);
- if (tv != null) {
- tv.requestFocus();
- }
- }
mFocusedTask = null;
}
@@ -107,6 +102,9 @@
* @return - The focused task.
*/
public Task getFocusedTask() {
+ if (findFocus() != null) {
+ mFocusedTask = ((TaskCardView)findFocus()).getTask();
+ }
return mFocusedTask;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index f154331..fba424e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -16,7 +16,6 @@
package com.android.systemui.recents.tv.views;
import android.app.Activity;
-import android.app.ActivityManagerNative;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
@@ -24,15 +23,20 @@
import android.view.ViewGroup;
import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
import com.android.systemui.recents.model.Task;
import java.util.ArrayList;
import java.util.List;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
public class TaskStackHorizontalViewAdapter extends
RecyclerView.Adapter<TaskStackHorizontalViewAdapter.ViewHolder> {
- private static final String TAG = "TaskStackHorizontalViewAdapter";
+ //Full class name is 30 characters
+ private static final String TAG = "TaskStackViewAdapter";
private List<Task> mTaskList;
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
@@ -54,7 +58,8 @@
@Override
public void onClick(View v) {
try {
- ActivityManagerNative.getDefault().startActivityFromRecents(mTask.key.id, null);
+ EventBus.getDefault().send(new LaunchTvTaskEvent(mTaskCardView, mTask,
+ null, INVALID_STACK_ID));
((Activity)(v.getContext())).finish();
} catch (Exception e) {
Log.e(TAG, v.getContext()
@@ -73,11 +78,12 @@
mTaskList.addAll(tasks);
notifyDataSetChanged();
}
+
@Override
public TaskStackHorizontalViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.recents_task_card_view, parent, false);
+ .inflate(R.layout.recents_tv_task_card_view, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
index 4359101..72b914c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -37,6 +37,13 @@
private int mTaskPadding;
public FreeformWorkspaceLayoutAlgorithm(Context context) {
+ reloadOnConfigurationChange(context);
+ }
+
+ /**
+ * Reloads the layout for the current configuration.
+ */
+ public void reloadOnConfigurationChange(Context context) {
// This is applied to the edges of each task
mTaskPadding = context.getResources().getDimensionPixelSize(
R.dimen.recents_freeform_workspace_task_padding) / 2;
@@ -72,8 +79,7 @@
}
// Bound the task width to the workspace width so that at the worst case, it will
// fit its own row
- normalizedTaskWidths[i] = Math.min(rowTaskWidth,
- normalizedWorkspaceWidth);
+ normalizedTaskWidths[i] = Math.min(rowTaskWidth, normalizedWorkspaceWidth);
}
// Determine the scale to best fit each of the tasks in the workspace
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 37b2859..a91bbd4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -122,6 +122,9 @@
animStartedListener = new ActivityOptions.OnAnimationStartedListener() {
@Override
public void onAnimationStarted() {
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
}
};
@@ -176,6 +179,7 @@
// Keep track of failed launches
EventBus.getDefault().send(new LaunchTaskFailedEvent());
}
+
if (transitionFuture != null) {
IRemoteCallback.Stub callback = null;
if (animStartedListener != null) {
@@ -276,7 +280,8 @@
} else {
layoutAlgorithm.getStackTransformScreenCoordinates(task, stackScroll, mTmpTransform,
null);
- specs.add(composeAnimationSpec(taskView, mTmpTransform, true /* addHeaderBitmap */));
+ specs.add(composeAnimationSpec(stackView, taskView, mTmpTransform,
+ true /* addHeaderBitmap */));
}
return specs;
}
@@ -297,7 +302,8 @@
} else {
layoutAlgorithm.getStackTransformScreenCoordinates(t, stackScroll,
mTmpTransform, null);
- specs.add(composeAnimationSpec(tv, mTmpTransform, true /* addHeaderBitmap */));
+ specs.add(composeAnimationSpec(stackView, tv, mTmpTransform,
+ true /* addHeaderBitmap */));
}
}
}
@@ -316,8 +322,8 @@
/**
* Composes a single animation spec for the given {@link TaskView}
*/
- private static AppTransitionAnimationSpec composeAnimationSpec(TaskView taskView,
- TaskViewTransform transform, boolean addHeaderBitmap) {
+ private static AppTransitionAnimationSpec composeAnimationSpec(TaskStackView stackView,
+ TaskView taskView, TaskViewTransform transform, boolean addHeaderBitmap) {
Bitmap b = null;
if (addHeaderBitmap) {
float scale = transform.scale;
@@ -339,6 +345,10 @@
Rect taskRect = new Rect();
transform.rect.round(taskRect);
+ if (stackView.getStack().getStackFrontMostTask(false /* includeFreeformTasks */) !=
+ taskView.getTask()) {
+ taskRect.bottom = 2 * Recents.getSystemServices().getDisplayRect().height();
+ }
return new AppTransitionAnimationSpec(taskView.getTask().key.id, b, taskRect);
}
}
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 5dde926..8342de5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -60,9 +60,7 @@
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
-import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.activity.ToggleHistoryEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
@@ -168,35 +166,40 @@
}
/** Set/get the bsp root node */
- public void setTaskStack(TaskStack stack) {
+ public void onResume(boolean isResumingFromVisible, TaskStack stack) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- mStack = stack;
- if (launchState.launchedReuseTaskStackViews) {
- if (mTaskStackView != null) {
- // If onRecentsHidden is not triggered, we need to the stack view again here
- mTaskStackView.reset();
- mTaskStackView.setStack(stack);
- } else {
- mTaskStackView = new TaskStackView(getContext(), stack);
- addView(mTaskStackView);
- }
- } else {
- if (mTaskStackView != null) {
- removeView(mTaskStackView);
- }
- mTaskStackView = new TaskStackView(getContext(), stack);
+
+ if (mTaskStackView == null || !launchState.launchedReuseTaskStackViews) {
+ isResumingFromVisible = false;
+ removeView(mTaskStackView);
+ mTaskStackView = new TaskStackView(getContext());
+ mStack = mTaskStackView.getStack();
addView(mTaskStackView);
}
- // If we are already occluded by the app, then just set the default background scrim now.
- // Otherwise, defer until the enter animation completes to animate the scrim with the
- // tasks for the home animation.
- if (launchState.launchedWhileDocking || launchState.launchedFromAppWithThumbnail
- || mStack.getTaskCount() == 0) {
- mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
+ // Reset the state
+ mAwaitingFirstLayout = !isResumingFromVisible;
+ mLastTaskLaunchedWasFreeform = false;
+
+ // Update the stack
+ mTaskStackView.onResume(isResumingFromVisible);
+ mTaskStackView.setTasks(stack, isResumingFromVisible /* notifyStackChanges */,
+ true /* relayoutTaskStack */);
+
+ if (isResumingFromVisible) {
+ // If we are already visible, then restore the background scrim
+ animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
} else {
- mBackgroundScrim.setAlpha(0);
+ // If we are already occluded by the app, then set the final background scrim alpha now.
+ // Otherwise, defer until the enter animation completes to animate the scrim alpha with
+ // the tasks for the home animation.
+ if (launchState.launchedWhileDocking || launchState.launchedFromApp
+ || mStack.getTaskCount() == 0) {
+ mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
+ } else {
+ mBackgroundScrim.setAlpha(0);
+ }
}
// Update the top level view's visibilities
@@ -205,9 +208,6 @@
} else {
showEmptyView();
}
-
- // Trigger a new layout
- requestLayout();
}
/**
@@ -662,16 +662,9 @@
animator.start();
}
- public final void onBusEvent(TaskStackUpdatedEvent event) {
- if (!event.inMultiWindow) {
- mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
- mStack.createAffiliatedGroupings(getContext());
- }
- }
-
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (!launchState.launchedWhileDocking && !launchState.launchedFromAppWithThumbnail
+ if (!launchState.launchedWhileDocking && !launchState.launchedFromApp
&& mStack.getTaskCount() > 0) {
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA,
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
@@ -686,17 +679,6 @@
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
}
- public final void onBusEvent(RecentsVisibilityChangedEvent event) {
- if (!event.visible) {
- // Reset the view state
- mAwaitingFirstLayout = true;
- mLastTaskLaunchedWasFreeform = false;
- if (RecentsDebugFlags.Static.EnableHistory) {
- hideHistoryButton(0, false /* translate */);
- }
- }
- }
-
public final void onBusEvent(ToggleHistoryEvent event) {
if (!RecentsDebugFlags.Static.EnableHistory) {
return;
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 079d7b9..84590f2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.views;
+import android.app.ActivityManager;
import android.content.res.Configuration;
import android.graphics.Point;
import android.view.MotionEvent;
@@ -149,7 +150,8 @@
mTaskView.setTranslationY(y);
mVisibleDockStates.clear();
- if (!ssp.hasDockedTask() && mRv.getTaskStack().getTaskCount() > 1) {
+ if (ActivityManager.supportsMultiWindow() &&
+ !ssp.hasDockedTask() && mRv.getTaskStack().getTaskCount() > 1) {
if (!event.task.isDockable) {
Toast.makeText(mRv.getContext(), R.string.recents_drag_non_dockable_task_message,
Toast.LENGTH_SHORT).show();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 1cd0850..19b219a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -16,9 +16,11 @@
package com.android.systemui.recents.views;
+import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.view.View;
+import android.view.ViewPropertyAnimator;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -56,25 +58,41 @@
View.VISIBLE : View.INVISIBLE);
}
+ /**
+ * Animates the nav bar scrim visibility.
+ */
+ public void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
+ int toY = 0;
+ if (visible) {
+ mNavBarScrimView.setVisibility(View.VISIBLE);
+ mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
+ } else {
+ toY = mNavBarScrimView.getMeasuredHeight();
+ }
+ if (animation != AnimationProps.IMMEDIATE) {
+ mNavBarScrimView.animate()
+ .translationY(toY)
+ .setDuration(animation.getDuration(AnimationProps.BOUNDS))
+ .setInterpolator(animation.getInterpolator(AnimationProps.BOUNDS))
+ .start();
+ } else {
+ mNavBarScrimView.setTranslationY(toY);
+ }
+ }
+
/**** EventBus events ****/
/**
* Starts animating the scrim views when entering Recents.
*/
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
- if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
- mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
- mNavBarScrimView.animate()
- .translationY(0)
- .setDuration(mNavBarScrimEnterDuration)
- .setInterpolator(Interpolators.DECELERATE_QUINT)
- .withStartAction(new Runnable() {
- @Override
- public void run() {
- mNavBarScrimView.setVisibility(View.VISIBLE);
- }
- })
- .start();
+ if (mHasNavBarScrim) {
+ AnimationProps animation = mShouldAnimateNavBarScrim
+ ? new AnimationProps()
+ .setDuration(AnimationProps.BOUNDS, mNavBarScrimEnterDuration)
+ .setInterpolator(AnimationProps.BOUNDS, Interpolators.DECELERATE_QUINT)
+ : AnimationProps.IMMEDIATE;
+ animateNavBarScrimVisibility(true, animation);
}
}
@@ -83,13 +101,12 @@
* going home).
*/
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
- if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
- mNavBarScrimView.animate()
- .translationY(mNavBarScrimView.getMeasuredHeight())
- .setStartDelay(0)
- .setDuration(TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .start();
+ if (mHasNavBarScrim) {
+ AnimationProps animation = new AnimationProps()
+ .setDuration(AnimationProps.BOUNDS,
+ TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
+ .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
+ animateNavBarScrimVisibility(false, animation);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index d152010..b36d5d1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -56,8 +56,8 @@
/**
* Callback to start the animation for the launch target {@link TaskView}.
*/
- void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled,
- ReferenceCountedTrigger postAnimationTrigger);
+ void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
+ boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger);
/**
* Callback to start the animation for the launch target {@link TaskView} when it is
@@ -71,6 +71,8 @@
public static final int ENTER_FROM_HOME_ALPHA_DURATION = 100;
public static final int ENTER_FROM_HOME_TRANSLATION_DURATION = 333;
+ public static final int ENTER_WHILE_DOCKING_DURATION = 150;
+
private static final PathInterpolator ENTER_FROM_HOME_TRANSLATION_INTERPOLATOR =
new PathInterpolator(0, 0, 0, 1f);
private static final PathInterpolator ENTER_FROM_HOME_ALPHA_INTERPOLATOR =
@@ -90,6 +92,9 @@
private static final PathInterpolator FOCUS_BEHIND_NEXT_TASK_INTERPOLATOR =
new PathInterpolator(0.4f, 0, 0.2f, 1f);
+ private static final PathInterpolator ENTER_WHILE_DOCKING_INTERPOLATOR =
+ new PathInterpolator(0, 0, 0.2f, 1f);
+
private TaskStackView mStackView;
private TaskViewTransform mTmpTransform = new TaskViewTransform();
@@ -122,6 +127,8 @@
int offscreenYOffset = stackLayout.mStackRect.height();
int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize(
R.dimen.recents_task_view_affiliate_group_enter_offset);
+ int launchedWhileDockingOffset = res.getDimensionPixelSize(
+ R.dimen.recents_task_view_launched_while_docking_offset);
// Prepare each of the task views for their enter animation from front to back
List<TaskView> taskViews = mStackView.getTaskViews();
@@ -141,7 +148,7 @@
tv.setVisibility(View.INVISIBLE);
} else if (launchState.launchedHasConfigurationChanged) {
// Just load the views as-is
- } else if (launchState.launchedFromAppWithThumbnail) {
+ } else if (launchState.launchedFromApp && !launchState.launchedWhileDocking) {
if (task.isLaunchTarget) {
tv.onPrepareLaunchTargetForEnterAnimation();
} else if (currentTaskOccludesLaunchTarget) {
@@ -159,6 +166,11 @@
bounds.offset(0, offscreenYOffset);
tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
(int) bounds.bottom);
+ } else if (launchState.launchedWhileDocking) {
+ RectF bounds = new RectF(mTmpTransform.rect);
+ bounds.offset(0, launchedWhileDockingOffset);
+ tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
+ (int) bounds.bottom);
}
}
}
@@ -192,6 +204,7 @@
int taskViewCount = taskViews.size();
for (int i = taskViewCount - 1; i >= 0; i--) {
int taskIndexFromFront = taskViewCount - i - 1;
+ int taskIndexFromBack = i;
final TaskView tv = taskViews.get(i);
Task task = tv.getTask();
boolean currentTaskOccludesLaunchTarget = false;
@@ -205,10 +218,11 @@
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null);
- if (launchState.launchedFromAppWithThumbnail) {
+ if (launchState.launchedFromApp && !launchState.launchedWhileDocking) {
if (task.isLaunchTarget) {
- tv.onStartLaunchTargetEnterAnimation(taskViewEnterFromAppDuration,
- mStackView.mScreenPinningEnabled, postAnimationTrigger);
+ tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
+ taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
+ postAnimationTrigger);
} else {
// Animate the task up if it was occluding the launch target
if (currentTaskOccludesLaunchTarget) {
@@ -240,6 +254,16 @@
.setListener(postAnimationTrigger.decrementOnAnimationEnd());
postAnimationTrigger.increment();
mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
+ } else if (launchState.launchedWhileDocking) {
+ // Animate the tasks up
+ AnimationProps taskAnimation = new AnimationProps()
+ .setDuration(AnimationProps.BOUNDS, (int) (ENTER_WHILE_DOCKING_DURATION +
+ (taskIndexFromBack * 2f * FRAME_OFFSET_MS)))
+ .setInterpolator(AnimationProps.BOUNDS,
+ ENTER_WHILE_DOCKING_INTERPOLATOR)
+ .setListener(postAnimationTrigger.decrementOnAnimationEnd());
+ postAnimationTrigger.increment();
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform, taskAnimation);
}
}
}
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 261b6f6..6df5884 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -29,6 +29,7 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.misc.FreePathInterpolator;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
@@ -36,6 +37,7 @@
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
+import java.util.List;
/**
* Used to describe a visible range that can be normalized to [0, 1].
@@ -220,6 +222,12 @@
private Range mUnfocusedRange;
private Range mFocusedRange;
+ // The initial offset from the top and bottom of the stack
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mInitialTopPeekHeight;
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mInitialBottomPeekHeight;
+
// The offset from the top when scrolled to the top of the stack
@ViewDebug.ExportedProperty(category="recents")
private int mFocusedTopPeekHeight;
@@ -231,6 +239,10 @@
@ViewDebug.ExportedProperty(category="recents")
private int mStackTopOffset;
+ // The height of the header bar
+ @ViewDebug.ExportedProperty(category="recents")
+ private int mHeaderBarHeight;
+
// The offset from the bottom of the stack to the bottom of the bounds when the stack is
// scrolled to the front
@ViewDebug.ExportedProperty(category="recents")
@@ -249,6 +261,9 @@
private FreePathInterpolator mUnfocusedDimCurveInterpolator;
private FreePathInterpolator mFocusedDimCurveInterpolator;
+ // Indexed from the front of the stack, the normalized x in the unfocused range for each task
+ private float[] mInitialNormX;
+
// The state of the stack focus (0..1), which controls the transition of the stack from the
// focused to non-focused state
@ViewDebug.ExportedProperty(category="recents")
@@ -292,23 +307,34 @@
TaskViewTransform mFrontOfStackTransform = new TaskViewTransform();
public TaskStackLayoutAlgorithm(Context context, TaskStackLayoutAlgorithmCallbacks cb) {
- Resources res = context.getResources();
mContext = context;
mCb = cb;
+ mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
+ reloadOnConfigurationChange(context);
+ }
+ /**
+ * Reloads the layout for the current configuration.
+ */
+ public void reloadOnConfigurationChange(Context context) {
+ Resources res = context.getResources();
mFocusedRange = new Range(res.getFloat(R.integer.recents_layout_focused_range_min),
res.getFloat(R.integer.recents_layout_focused_range_max));
mUnfocusedRange = new Range(res.getFloat(R.integer.recents_layout_unfocused_range_min),
res.getFloat(R.integer.recents_layout_unfocused_range_max));
- mFocusState = getDefaultFocusState();
+ mFocusState = getInitialFocusState();
+ mInitialTopPeekHeight = res.getDimensionPixelSize(R.dimen.recents_initial_top_peek_size);
+ mInitialBottomPeekHeight =
+ res.getDimensionPixelSize(R.dimen.recents_initial_bottom_peek_size);
mFocusedTopPeekHeight =
res.getDimensionPixelSize(R.dimen.recents_layout_focused_top_peek_size);
mFocusedBottomTaskPeekHeight =
res.getDimensionPixelSize(R.dimen.recents_layout_focused_bottom_task_peek_size);
+ mHeaderBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
mMinTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min);
mMaxTranslationZ = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max);
- mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
+ mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context);
}
/**
@@ -316,7 +342,7 @@
*/
public void reset() {
mTaskIndexOverrideMap.clear();
- setFocusState(getDefaultFocusState());
+ setFocusState(getInitialFocusState());
}
/**
@@ -439,46 +465,89 @@
mTaskIndexMap.put(task.key.id, i);
}
- // Calculate the min/max scroll
- if (getDefaultFocusState() > 0f) {
- mMinScrollP = 0;
- mMaxScrollP = Math.max(mMinScrollP, mNumStackTasks - 1);
- } else {
- if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
- mMinScrollP = mMaxScrollP = 0;
- } else {
- float bottomOffsetPct = (float) (mStackBottomOffset + mTaskRect.height()) /
- mStackRect.height();
- float normX = mUnfocusedCurveInterpolator.getX(bottomOffsetPct);
- mMinScrollP = 0;
- mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
- Math.max(0, mUnfocusedRange.getAbsoluteX(normX)));
- }
- }
-
+ // Update the freeform tasks
if (!freeformTasks.isEmpty()) {
mFreeformLayoutAlgorithm.update(freeformTasks, this);
- mInitialScrollP = mMaxScrollP;
- } else {
- Task launchTask = stack.getLaunchTarget();
- int launchTaskIndex = launchTask != null
- ? stack.indexOfStackTask(launchTask)
- : mNumStackTasks - 1;
- if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
- mInitialScrollP = mMinScrollP;
- } else if (getDefaultFocusState() > 0f) {
- if (launchState.launchedFromHome) {
- mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
- } else {
- mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP,
- mMaxScrollP);
- }
+ }
+
+ // Calculate the min/max/initial scroll
+ Task launchTask = stack.getLaunchTarget();
+ int launchTaskIndex = launchTask != null
+ ? stack.indexOfStackTask(launchTask)
+ : mNumStackTasks - 1;
+ if (getInitialFocusState() == STATE_FOCUSED) {
+ mMinScrollP = 0;
+ mMaxScrollP = Math.max(mMinScrollP, mNumStackTasks - 1);
+ if (launchState.launchedFromHome) {
+ mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
} else {
- float offsetPct = (float) (mTaskRect.height() / 3) / mStackRect.height();
- float normX = mUnfocusedCurveInterpolator.getX(offsetPct);
- mInitialScrollP = Utilities.clamp(launchTaskIndex -
- mUnfocusedRange.getAbsoluteX(normX), mMinScrollP, mMaxScrollP);
+ mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
}
+ } else if (!ssp.hasFreeformWorkspaceSupport() && mNumStackTasks == 1) {
+ // If there is one stack task, ignore the min/max/initial scroll positions
+ mMinScrollP = 0;
+ mMaxScrollP = 0;
+ mInitialScrollP = 0;
+ } else {
+ // Set the max scroll to be the point where the front most task is visible with the
+ // stack bottom offset
+ int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
+ float maxBottomOffsetPct = (float) maxBottomOffset / mStackRect.height();
+ float maxBottomNormX = mUnfocusedCurveInterpolator.getX(maxBottomOffsetPct);
+ mUnfocusedRange.offset(0f);
+ mMinScrollP = 0;
+ mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
+ Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
+ boolean scrollToFront = launchState.launchedFromHome ||
+ launchState.launchedFromAppDocked;
+ if (scrollToFront) {
+ mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
+ } else {
+ mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP);
+ }
+
+ // Set the initial scroll to the predefined state (which differs from the stack)
+ int initialPeekOffset = mStackRect.height() - mInitialTopPeekHeight;
+ float initialPeekOffsetPct = (float) initialPeekOffset / mStackRect.height();
+ float initialPeekOffsetNormX = mUnfocusedCurveInterpolator.getX(initialPeekOffsetPct);
+ float initialFocusedOffset = mStackRect.height() - mInitialTopPeekHeight -
+ (mHeaderBarHeight * 1f) + 1;
+ float initialFocusedOffsetPct = initialFocusedOffset / mStackRect.height();
+ float initialFocusedNormX = mUnfocusedCurveInterpolator.getX(initialFocusedOffsetPct);
+ float initialBottomOffset = mStackBottomOffset +
+ (ssp.hasDockedTask()
+ ? mHeaderBarHeight
+ : mInitialBottomPeekHeight);
+ float initialBottomOffsetPct = initialBottomOffset / mStackRect.height();
+ float initialBottomNormX = mUnfocusedCurveInterpolator.getX(initialBottomOffsetPct);
+ /*
+ // If we want to offset the top card slightly
+ mInitialNormX = scrollToFront
+ ? new float[] { initialFocusedNormX, initialPeekOffsetNormX, 0f }
+ : new float[] { initialBottomNormX, initialFocusedNormX,
+ initialPeekOffsetNormX, 0f };
+ */
+ mInitialNormX = scrollToFront
+ ? new float[] { initialFocusedNormX, initialPeekOffsetNormX, 0f }
+ : new float[] { initialBottomNormX, 0.5f, 0f };
+ }
+ }
+
+ public void updateToInitialState(List<Task> tasks) {
+ if (mInitialNormX == null) {
+ return;
+ }
+
+ mUnfocusedRange.offset(0f);
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ int indexFromFront = taskCount - i - 1;
+ if (indexFromFront >= mInitialNormX.length) {
+ break;
+ }
+ float newTaskProgress = mInitialScrollP +
+ mUnfocusedRange.getAbsoluteX(mInitialNormX[indexFromFront]);
+ mTaskIndexOverrideMap.put(tasks.get(i).key.id, newTaskProgress);
}
}
@@ -500,10 +569,18 @@
}
}
+ public void clearUnfocusedTaskOverrides() {
+ mTaskIndexOverrideMap.clear();
+ }
+
/**
* Updates this stack when a scroll happens.
*/
public void updateFocusStateOnScroll(float stackScroll, float deltaScroll) {
+ if (deltaScroll == 0f) {
+ return;
+ }
+
for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
int taskId = mTaskIndexOverrideMap.keyAt(i);
float x = mTaskIndexMap.get(taskId);
@@ -523,7 +600,7 @@
} else {
// Scrolling override x away from x, we should still move the scroll towards x
float deltaX = overrideX - x;
- newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - deltaScroll);
+ newOverrideX = Math.signum(deltaX) * (Math.abs(deltaX) - Math.abs(deltaScroll));
mTaskIndexOverrideMap.put(taskId, x + newOverrideX);
}
}
@@ -532,8 +609,13 @@
/**
* Returns the default focus state.
*/
- public int getDefaultFocusState() {
- return STATE_FOCUSED;
+ public int getInitialFocusState() {
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ if (debugFlags.isPagingEnabled()) {
+ return STATE_FOCUSED;
+ } else {
+ return STATE_UNFOCUSED;
+ }
}
/**
@@ -559,6 +641,13 @@
}
/**
+ * Returns whether this stack layout has been initialized.
+ */
+ public boolean isInitialized() {
+ return !mStackRect.isEmpty();
+ }
+
+ /**
* Computes the maximum number of visible tasks and thumbnails when the scroll is at the initial
* stack scroll. Requires that update() is called first.
*/
@@ -577,7 +666,7 @@
// Otherwise, walk backwards in the stack and count the number of tasks and visible
// thumbnails and add that to the total freeform task count
TaskViewTransform tmpTransform = new TaskViewTransform();
- Range currentRange = getDefaultFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
+ Range currentRange = getInitialFocusState() > 0f ? mFocusedRange : mUnfocusedRange;
currentRange.offset(mInitialScrollP);
int taskBarHeight = mContext.getResources().getDimensionPixelSize(
R.dimen.recents_task_bar_height);
@@ -600,8 +689,9 @@
boolean isFrontMostTaskInGroup = task.group == null || task.group.isFrontMostTask(task);
if (isFrontMostTaskInGroup) {
- getStackTransform(taskProgress, mInitialScrollP, mFocusState, tmpTransform, null,
- false /* ignoreSingleTaskCase */, false /* forceUpdate */);
+ getStackTransform(taskProgress, taskProgress, mInitialScrollP, mFocusState,
+ tmpTransform, null, false /* ignoreSingleTaskCase */,
+ false /* forceUpdate */);
float screenY = tmpTransform.rect.top;
boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight;
if (hasVisibleThumbnail) {
@@ -635,23 +725,34 @@
public TaskViewTransform getStackTransform(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform) {
return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
- false /* forceUpdate */);
+ false /* forceUpdate */, false /* ignoreTaskOverrides */);
+ }
+
+ public TaskViewTransform getStackTransform(Task task, float stackScroll,
+ TaskViewTransform transformOut, TaskViewTransform frontTransform,
+ boolean ignoreTaskOverrides) {
+ return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
+ false /* forceUpdate */, ignoreTaskOverrides);
}
public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
- TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate) {
+ TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
+ boolean ignoreTaskOverrides) {
if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
return transformOut;
} else {
// Return early if we have an invalid index
- if (task == null || mTaskIndexMap.get(task.key.id, -1) == -1) {
+ int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1);
+ if (task == null || nonOverrideTaskProgress == -1) {
transformOut.reset();
return transformOut;
}
- float taskProgress = getStackScrollForTask(task);
- getStackTransform(taskProgress, stackScroll, focusState, transformOut,
- frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
+ float taskProgress = ignoreTaskOverrides
+ ? nonOverrideTaskProgress
+ : getStackScrollForTask(task);
+ getStackTransform(taskProgress, nonOverrideTaskProgress, stackScroll, focusState,
+ transformOut, frontTransform, false /* ignoreSingleTaskCase */, forceUpdate);
return transformOut;
}
}
@@ -676,9 +777,9 @@
* internally to ensure that we can calculate the transform for any
* position in the stack.
*/
- public void getStackTransform(float taskProgress, float stackScroll, int focusState,
- TaskViewTransform transformOut, TaskViewTransform frontTransform,
- boolean ignoreSingleTaskCase, boolean forceUpdate) {
+ public void getStackTransform(float taskProgress, float nonOverrideTaskProgress,
+ float stackScroll, int focusState, TaskViewTransform transformOut,
+ TaskViewTransform frontTransform, boolean ignoreSingleTaskCase, boolean forceUpdate) {
SystemServicesProxy ssp = Recents.getSystemServices();
// Compute the focused and unfocused offset
@@ -687,6 +788,8 @@
mFocusedRange.offset(boundedStackScroll);
float boundedScrollUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
float boundedScrollFocusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
+ float boundedScrollUnfocusedNonOverrideRangeX =
+ mUnfocusedRange.getNormalizedX(nonOverrideTaskProgress);
mUnfocusedRange.offset(stackScroll);
mFocusedRange.offset(stackScroll);
boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
@@ -730,7 +833,7 @@
y = (mStackRect.top - mTaskRect.top) +
(int) Utilities.mapRange(focusState, unfocusedY, focusedY);
- z = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedRangeX),
+ z = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedNonOverrideRangeX),
mMinTranslationZ, mMaxTranslationZ);
dimAlpha = Utilities.mapRange(focusState, unfocusedDim, focusedDim);
viewOutlineAlpha = Utilities.mapRange(Utilities.clamp01(boundedScrollUnfocusedRangeX),
@@ -820,11 +923,11 @@
float peekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
float slope = ((1f - peekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
float b = 1f - slope * cpoint1X;
- float cpoint2X = 0.75f;
+ float cpoint2X = 0.65f;
float cpoint2Y = slope * cpoint2X + b;
Path p = new Path();
p.moveTo(0f, 1f);
- p.cubicTo(0f, 1f, cpoint1X, 1f, 0.5f, 1f - peekHeightPct);
+ p.cubicTo(0f, 1f, cpoint1X, cpoint1Y, 0.5f, 1f - peekHeightPct);
p.cubicTo(0.5f, 1f - peekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
return p;
}
@@ -851,8 +954,8 @@
// The unfocused dim interpolator starts at max dim, reduces to zero at 0.5 (the focused
// task), then goes back to max dim towards the front of the stack
p.moveTo(0f, MAX_DIM);
- p.cubicTo(0f, 0.1f, 0.4f, 0f, 0.5f, 0f);
- p.cubicTo(0.6f, 0f, 0.9f, MAX_DIM - 0.1f, 1f, MAX_DIM / 2f);
+ p.cubicTo(0.1f, MAX_DIM, 0.4f, 0.0f, 0.5f, 0f);
+ p.cubicTo(0.6f, 0f, 0.9f, MAX_DIM / 2f, 1f, MAX_DIM / 2f);
return p;
}
@@ -870,9 +973,9 @@
mFocusedRange.relativeMin);
float max = Utilities.mapRange(mFocusState, mUnfocusedRange.relativeMax,
mFocusedRange.relativeMax);
- getStackTransform(min, 0f, mFocusState, mBackOfStackTransform, null,
+ getStackTransform(min, min, 0f, mFocusState, mBackOfStackTransform, null,
true /* ignoreSingleTaskCase */, true /* forceUpdate */);
- getStackTransform(max, 0f, mFocusState, mFrontOfStackTransform, null,
+ getStackTransform(max, max, 0f, mFocusState, mFrontOfStackTransform, null,
true /* ignoreSingleTaskCase */, true /* forceUpdate */);
mBackOfStackTransform.visible = true;
mFrontOfStackTransform.visible = true;
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 e1a81c8..6c410c3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -35,7 +35,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.MutableBoolean;
-import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -56,6 +55,7 @@
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
+import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
@@ -65,15 +65,13 @@
import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
+import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
-import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
-import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
-import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
import com.android.systemui.recents.events.ui.UserInteractionEvent;
@@ -118,7 +116,7 @@
private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();
LayoutInflater mInflater;
- TaskStack mStack;
+ TaskStack mStack = new TaskStack();
@ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
TaskStackLayoutAlgorithm mLayoutAlgorithm;
@ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
@@ -207,13 +205,13 @@
}
};
- public TaskStackView(Context context, TaskStack stack) {
+ public TaskStackView(Context context) {
super(context);
SystemServicesProxy ssp = Recents.getSystemServices();
Resources res = context.getResources();
// Set the stack first
- setStack(stack);
+ mStack.setCallbacks(this);
mViewPool = new ViewPool<>(context, this);
mInflater = LayoutInflater.from(context);
mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
@@ -249,6 +247,41 @@
}
}
+ /**
+ * Called only if we are resuming Recents.
+ */
+ void onResume(boolean isResumingFromVisible) {
+ if (!isResumingFromVisible) {
+ // Reset the focused task
+ resetFocusedTask(getFocusedTask());
+ }
+
+ // Reset the state of each of the task views
+ List<TaskView> taskViews = new ArrayList<>();
+ taskViews.addAll(getTaskViews());
+ taskViews.addAll(mViewPool.getViews());
+ for (int i = taskViews.size() - 1; i >= 0; i--) {
+ taskViews.get(i).onResume(isResumingFromVisible);
+ }
+
+ // Reset the stack state
+ readSystemFlags();
+ mTaskViewsClipDirty = true;
+ mEnterAnimationComplete = false;
+ mUIDozeTrigger.stopDozing();
+ if (isResumingFromVisible) {
+ // Animate in the freeform workspace
+ int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
+ animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
+ Interpolators.FAST_OUT_SLOW_IN));
+ } else {
+ mStackScroller.reset();
+ mLayoutAlgorithm.reset();
+ mAwaitingFirstLayout = true;
+ requestLayout();
+ }
+ }
+
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
@@ -262,22 +295,47 @@
EventBus.getDefault().unregister(this);
}
- /** Sets the task stack */
- void setStack(TaskStack stack) {
- // Set the new stack
- mStack = stack;
- if (mStack != null) {
- mStack.setCallbacks(this);
+ /**
+ * Sets the stack tasks of this TaskStackView from the given TaskStack.
+ */
+ public void setTasks(TaskStack stack, boolean notifyStackChanges, boolean relayoutTaskStack) {
+ boolean isInitialized = mLayoutAlgorithm.isInitialized();
+ mStack.setTasks(getContext(), stack.computeAllTasksList(),
+ notifyStackChanges && isInitialized);
+ if (isInitialized) {
+ // Only update the layout if we are notifying, otherwise, we will update it in the next
+ // measure/layout pass
+ updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
+ updateToInitialState();
+
+ if (relayoutTaskStack) {
+ relayoutTaskViews(AnimationProps.IMMEDIATE);
+
+ // Rebind all the task views. This will not trigger new resources to be loaded
+ // unless they have actually changed
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ bindTaskView(tv, tv.getTask());
+ }
+ }
}
- // Layout again with the new stack
- requestLayout();
}
/** Returns the task stack. */
- TaskStack getStack() {
+ public TaskStack getStack() {
return mStack;
}
+ /**
+ * Updates this TaskStackView to the initial state.
+ */
+ public void updateToInitialState() {
+ mStackScroller.setStackScrollToInitialState();
+ mLayoutAlgorithm.updateToInitialState(mStack.getStackTasks());
+ }
+
/** Updates the list of task views */
void updateTaskViewsList() {
mTaskViews.clear();
@@ -331,37 +389,6 @@
return null;
}
- /** Resets this TaskStackView for reuse. */
- void reset() {
- // Reset the focused task
- resetFocusedTask(getFocusedTask());
-
- // Return all the views to the pool
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = taskViewCount - 1; i >= 0; i--) {
- mViewPool.returnViewToPool(taskViews.get(i));
- }
-
- // Mark each task view for relayout
- List<TaskView> poolViews = mViewPool.getViews();
- for (TaskView tv : poolViews) {
- tv.reset();
- }
-
- // Reset the stack state
- mStack.reset();
- mTaskViewsClipDirty = true;
- mAwaitingFirstLayout = true;
- mEnterAnimationComplete = false;
- mUIDozeTrigger.stopDozing();
- mUIDozeTrigger.resetTrigger();
- mStackScroller.reset();
- mLayoutAlgorithm.reset();
- readSystemFlags();
- requestLayout();
- }
-
/** Returns the stack algorithm for this task stack. */
public TaskStackLayoutAlgorithm getStackAlgorithm() {
return mLayoutAlgorithm;
@@ -410,7 +437,7 @@
*/
int[] computeVisibleTaskTransforms(ArrayList<TaskViewTransform> taskTransforms,
ArrayList<Task> tasks, float curStackScroll, float targetStackScroll,
- ArraySet<Task.TaskKey> ignoreTasksSet) {
+ ArraySet<Task.TaskKey> ignoreTasksSet, boolean ignoreTaskOverrides) {
int taskCount = tasks.size();
int[] visibleTaskRange = mTmpIntPair;
visibleTaskRange[0] = -1;
@@ -430,7 +457,7 @@
// Calculate the current and (if necessary) the target transform for the task
transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
- taskTransforms.get(i), frontTransform);
+ taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
if (useTargetStackScroll && !transform.visible) {
// If we have a target stack scroll and the task is not currently visible, then we
// just update the transform at the new scroll
@@ -468,11 +495,13 @@
/**
* Binds the visible {@link TaskView}s at the given target scroll.
- *
- * @see #bindVisibleTaskViews(float, ArraySet<Task.TaskKey>)
*/
void bindVisibleTaskViews(float targetStackScroll) {
- bindVisibleTaskViews(targetStackScroll, mIgnoreTasks);
+ bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, false /* ignoreTaskOverrides */);
+ }
+
+ void bindVisibleTaskViews(float targetStackScroll, boolean ignoreTaskOverrides) {
+ bindVisibleTaskViews(targetStackScroll, mIgnoreTasks, ignoreTaskOverrides);
}
/**
@@ -487,12 +516,16 @@
* target stack scroll.
* @param ignoreTasksSet The set of tasks to ignore in this rebinding of the visible
* {@link TaskView}s
+ * @param ignoreTaskOverrides If set, the visible task computation will get the transforms for
+ * tasks at their non-overridden task progress
*/
- void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet) {
+ void bindVisibleTaskViews(float targetStackScroll, ArraySet<Task.TaskKey> ignoreTasksSet,
+ boolean ignoreTaskOverrides) {
// Get all the task transforms
ArrayList<Task> tasks = mStack.getStackTasks();
int[] visibleTaskRange = computeVisibleTaskTransforms(mCurrentTaskTransforms, tasks,
- mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet);
+ mStackScroller.getStackScroll(), targetStackScroll, ignoreTasksSet,
+ ignoreTaskOverrides);
// Return all the invisible children to the pool
mTmpTaskViewMap.clear();
@@ -502,15 +535,22 @@
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
Task task = tv.getTask();
- int taskIndex = mStack.indexOfStackTask(task);
- TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
// Skip ignored tasks
if (ignoreTasksSet.contains(task.key)) {
continue;
}
- if (task.isFreeformTask() || transform.visible) {
+ // It is possible for the set of lingering TaskViews to differ from the stack if the
+ // stack was updated before the relayout. If the task view is no longer in the stack,
+ // then just return it back to the view pool.
+ int taskIndex = mStack.indexOfStackTask(task);
+ TaskViewTransform transform = null;
+ if (taskIndex != -1) {
+ transform = mCurrentTaskTransforms.get(taskIndex);
+ }
+
+ if (task.isFreeformTask() || (transform != null && transform.visible)) {
mTmpTaskViewMap.put(task.key, tv);
} else {
if (mTouchExplorationEnabled) {
@@ -601,19 +641,17 @@
// If we had a deferred animation, cancel that
mDeferredTaskViewLayoutAnimation = null;
- // Cancel all task view animations
- cancelAllTaskViewAnimations();
-
// Synchronize the current set of TaskViews
- bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet);
+ bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTasksSet,
+ false /* ignoreTaskOverrides */);
// Animate them to their final transforms with the given animation
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = 0; i < taskViewCount; i++) {
- final TaskView tv = taskViews.get(i);
- final int taskIndex = mStack.indexOfStackTask(tv.getTask());
- final TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
+ TaskView tv = taskViews.get(i);
+ int taskIndex = mStack.indexOfStackTask(tv.getTask());
+ TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
if (ignoreTasksSet.contains(tv.getTask().key)) {
continue;
@@ -637,6 +675,10 @@
*/
public void updateTaskViewToTransform(TaskView taskView, TaskViewTransform transform,
AnimationProps animation) {
+ if (taskView.isAnimatingTo(transform)) {
+ return;
+ }
+ taskView.cancelTransformAnimation();
taskView.updateViewPropertiesToTaskTransform(transform, animation,
mRequestUpdateClippingListener);
}
@@ -657,7 +699,8 @@
transform.fillIn(tv);
} else {
mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
- focusState, transform, null, true /* forceUpdate */);
+ focusState, transform, null, true /* forceUpdate */,
+ false /* ignoreTaskOverrides */);
}
transform.visible = true;
}
@@ -674,7 +717,7 @@
Task task = tasks.get(i);
TaskViewTransform transform = transformsOut.get(i);
mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
- true /* forceUpdate */);
+ true /* forceUpdate */, true /* ignoreTaskOverrides */);
transform.visible = true;
}
}
@@ -759,9 +802,7 @@
}
}
tv.getViewBounds().setClipBottom(clipBottom);
- if (!config.useHardwareLayers) {
- tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
- }
+ tv.mThumbnailView.updateThumbnailVisibility(clipBottom - tv.getPaddingBottom());
prevVisibleTv = tv;
}
mTaskViewsClipDirty = false;
@@ -772,7 +813,7 @@
*
* @see #updateLayoutAlgorithm(boolean, ArraySet<Task.TaskKey>)
*/
- void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
+ public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax) {
updateLayoutAlgorithm(boundScrollToNewMinMax, mIgnoreTasks);
}
@@ -781,7 +822,7 @@
*
* @param ignoreTasksSet the set of tasks to ignore in the relayout
*/
- void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
+ public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax,
ArraySet<Task.TaskKey> ignoreTasksSet) {
// Compute the min and max scroll values
mLayoutAlgorithm.update(mStack, ignoreTasksSet);
@@ -860,6 +901,7 @@
cancelAllTaskViewAnimations();
}
+ mLayoutAlgorithm.clearUnfocusedTaskOverrides();
willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
requestViewFocus);
} else {
@@ -1107,15 +1149,6 @@
}
/**
- * This is ONLY used from the Recents component to update the dummy stack view for purposes
- * of getting the task rect to animate to.
- */
- public void updateLayoutForStack(TaskStack stack) {
- mStack = stack;
- updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
- }
-
- /**
* Computes the maximum number of visible tasks and thumbnails. Requires that
* updateLayoutForStack() is called first.
*/
@@ -1162,11 +1195,16 @@
// If this is the first layout, then scroll to the front of the stack, then update the
// TaskViews with the stack so that we can lay them out
- if (mAwaitingFirstLayout) {
- mStackScroller.setStackScrollToInitialState();
+ // TODO: The second check is a workaround for wacky layouts that we get while docking via
+ // long pressing the recents button
+ if (mAwaitingFirstLayout ||
+ (mStackScroller.getStackScroll() == mLayoutAlgorithm.mInitialScrollP)) {
+ updateToInitialState();
}
+
// Rebind all the views, including the ignore ones
- bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET);
+ bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET,
+ false /* ignoreTaskOverrides */);
// Measure each of the TaskViews
mTmpTaskViews.clear();
@@ -1405,8 +1443,8 @@
public void onReturnViewToPool(TaskView tv) {
final Task task = tv.getTask();
- // Report that this tasks's data is no longer being used
- Recents.getTaskLoader().unloadTaskData(task);
+ // Unbind the task from the task view
+ unbindTaskView(tv, task);
// Reset the view properties and view state
tv.resetViewProperties();
@@ -1451,14 +1489,11 @@
// Update the task views list after adding the new task view
updateTaskViewsList();
- // Rebind the task and request that this task's data be filled into the TaskView
- tv.onTaskBound(task);
-
- // Load the task data
- Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
+ // Bind the task view to the new task
+ bindTaskView(tv, task);
// If the doze trigger has already fired, then update the state for this task view
- if (mUIDozeTrigger.hasTriggered()) {
+ if (mUIDozeTrigger.isAsleep()) {
tv.setNoUserInteractionState();
}
@@ -1487,6 +1522,19 @@
return (tv.getTask() == preferredData);
}
+ private void bindTaskView(TaskView tv, Task task) {
+ // Rebind the task and request that this task's data be filled into the TaskView
+ tv.onTaskBound(task);
+
+ // Load the task data
+ Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
+ }
+
+ private void unbindTaskView(TaskView tv, Task task) {
+ // Report that this task's data is no longer being used
+ Recents.getTaskLoader().unloadTaskData(task);
+ }
+
/**** TaskViewCallbacks Implementation ****/
@Override
@@ -1652,12 +1700,6 @@
}
}
- public final void onBusEvent(RecentsVisibilityChangedEvent event) {
- if (!event.visible) {
- reset();
- }
- }
-
public final void onBusEvent(DragStartEvent event) {
// Ensure that the drag task is not animated
addIgnoreTask(event.task);
@@ -1754,12 +1796,14 @@
event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
taskViewRect.right, taskViewRect.bottom);
- // Animate all the TaskViews back into position
+ // Animate the non-drag TaskViews back into position
mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
mTmpTransform, null);
event.getAnimationTrigger().increment();
relayoutTaskViews(new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
Interpolators.FAST_OUT_SLOW_IN));
+
+ // Animate the drag TaskView back into position
updateTaskViewToTransform(event.taskView, mTmpTransform,
new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
event.getAnimationTrigger().decrementOnAnimationEnd()));
@@ -1832,31 +1876,28 @@
mAnimationHelper.startHideHistoryAnimation();
}
- public final void onBusEvent(TaskStackUpdatedEvent event) {
+ public final void onBusEvent(MultiWindowStateChangedEvent event) {
if (!event.inMultiWindow) {
- // Scroll the stack to the front after it has been updated
- event.addPostAnimationCallback(new Runnable() {
+ // Scroll the stack to the front to see the undocked task
+ mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() {
@Override
public void run() {
- mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP,
- null /* postScrollRunnable */);
+ List<TaskView> taskViews = getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int i = 0; i < taskViewCount; i++) {
+ TaskView tv = taskViews.get(i);
+ tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
+ tv.mIsDisabledInSafeMode);
+ }
}
});
}
- // When the multi-window state changes, rebind all task view headers again to update their
- // dockable state
- event.addPostAnimationCallback(new Runnable() {
- @Override
- public void run() {
- List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
- TaskView tv = taskViews.get(i);
- tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
- tv.mIsDisabledInSafeMode);
- }
- }
- });
+ }
+
+ public final void onBusEvent(ConfigurationChangedEvent event) {
+ mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
+ mLayoutAlgorithm.initialize(mStackBounds,
+ TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 333df9d..ad46abd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -115,13 +115,8 @@
* @return whether the stack progress changed.
*/
public boolean setStackScrollToInitialState() {
- SystemServicesProxy ssp = Recents.getSystemServices();
float prevStackScrollP = mStackScrollP;
- if (ssp.hasDockedTask()) {
- setStackScroll(mLayoutAlgorithm.mMaxScrollP);
- } else {
- setStackScroll(mLayoutAlgorithm.mInitialScrollP);
- }
+ setStackScroll(mLayoutAlgorithm.mInitialScrollP);
return Float.compare(prevStackScrollP, mStackScrollP) != 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 20933ee..8635911 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -20,6 +20,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Path;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.MutableBoolean;
@@ -44,6 +45,7 @@
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
+import com.android.systemui.recents.misc.FreePathInterpolator;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
@@ -60,6 +62,16 @@
private static final int INACTIVE_POINTER_ID = -1;
private static final Interpolator STACK_TRANSFORM_INTERPOLATOR =
new PathInterpolator(0.73f, 0.33f, 0.42f, 0.85f);
+ // The min overscroll is the amount of task progress overscroll we want / the max overscroll
+ // curve value below
+ private static final float MAX_OVERSCROLL = 0.7f / 0.3f;
+ private static final Interpolator OVERSCROLL_INTERP;
+ static {
+ Path OVERSCROLL_PATH = new Path();
+ OVERSCROLL_PATH.moveTo(0, 0);
+ OVERSCROLL_PATH.cubicTo(0.2f, 0.175f, 0.25f, 0.3f, 1f, 0.3f);
+ OVERSCROLL_INTERP = new FreePathInterpolator(OVERSCROLL_PATH);
+ }
Context mContext;
TaskStackView mSv;
@@ -245,6 +257,18 @@
// of the curve, so just move the scroll proportional to that
float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
float curScrollP = mDownScrollP + deltaP;
+
+ // Modulate the overscroll to prevent users from pulling the stack too far
+ float minScrollP = layoutAlgorithm.mMinScrollP;
+ float maxScrollP = layoutAlgorithm.mMaxScrollP;
+ if (curScrollP < minScrollP || curScrollP > maxScrollP) {
+ float clampedScrollP = Utilities.clamp(curScrollP, minScrollP, maxScrollP);
+ float overscrollP = (curScrollP - clampedScrollP);
+ float overscrollX = Math.abs(overscrollP) / MAX_OVERSCROLL;
+ curScrollP = clampedScrollP + (Math.signum(overscrollP) *
+ (OVERSCROLL_INTERP.getInterpolation(overscrollX) * MAX_OVERSCROLL));
+ }
+
mScroller.setStackScroll(curScrollP);
mStackViewScrolledEvent.updateY(y - mLastY);
EventBus.getDefault().send(mStackViewScrolledEvent);
@@ -446,7 +470,7 @@
}
// Pick up the newly visible views, not including the deleting tasks
- mSv.bindVisibleTaskViews(newStackScroll);
+ mSv.bindVisibleTaskViews(newStackScroll, true /* ignoreTaskOverrides */);
// Get the final set of task transforms (with task removed)
mSv.getLayoutTaskTransforms(newStackScroll, TaskStackLayoutAlgorithm.STATE_UNFOCUSED,
@@ -486,6 +510,7 @@
mSv.getScroller().setStackScroll(mTargetStackScroll, null);
// Update the focus state to the final focus state
mSv.getStackAlgorithm().setFocusState(TaskStackLayoutAlgorithm.STATE_UNFOCUSED);
+ mSv.getStackAlgorithm().clearUnfocusedTaskOverrides();
// Remove the task view from the stack
EventBus.getDefault().send(new TaskViewDismissedEvent(tv.getTask(), tv));
// Stop tracking this deletion animation
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 2e7c7f2..7584a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -77,6 +77,24 @@
/**
* The dim overlay is generally calculated from the task progress, but occasionally (like when
+ * launching) needs to be animated independently of the task progress. This call is only used
+ * when animating the task into Recents, when the header dim is already applied
+ */
+ public static final Property<TaskView, Float> DIM_ALPHA_WITHOUT_HEADER =
+ new FloatProperty<TaskView>("dimAlphaWithoutHeader") {
+ @Override
+ public void setValue(TaskView tv, float dimAlpha) {
+ tv.setDimAlphaWithoutHeader(dimAlpha);
+ }
+
+ @Override
+ public Float get(TaskView tv) {
+ return tv.getDimAlpha();
+ }
+ };
+
+ /**
+ * The dim overlay is generally calculated from the task progress, but occasionally (like when
* launching) needs to be animated independently of the task progress.
*/
public static final Property<TaskView, Float> DIM_ALPHA =
@@ -129,6 +147,7 @@
AnimateableViewBounds mViewBounds;
private AnimatorSet mTransformAnimation;
+ private final TaskViewTransform mTargetAnimationTransform = new TaskViewTransform();
private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
View mContent;
@@ -175,11 +194,13 @@
}
/** Resets this TaskView for reuse. */
- void reset() {
- resetViewProperties();
+ void onResume(boolean isResumingFromVisible) {
resetNoUserInteractionState();
readSystemFlags();
- setClipViewInStack(false);
+ if (!isResumingFromVisible) {
+ resetViewProperties();
+ setClipViewInStack(false);
+ }
setCallbacks(null);
}
@@ -299,6 +320,7 @@
// Create the animator
mTransformAnimation = toAnimation.createAnimator(mTmpAnimators);
mTransformAnimation.start();
+ mTargetAnimationTransform.copyFrom(toTransform);
}
}
@@ -318,6 +340,14 @@
}
/**
+ * @return whether we are animating towards {@param transform}
+ */
+ boolean isAnimatingTo(TaskViewTransform transform) {
+ return mTransformAnimation != null && mTransformAnimation.isStarted()
+ && mTargetAnimationTransform.isSame(transform);
+ }
+
+ /**
* Cancels any current transform animations.
*/
public void cancelTransformAnimation() {
@@ -388,21 +418,17 @@
* Sets the current dim.
*/
public void setDimAlpha(float dimAlpha) {
- RecentsConfiguration config = Recents.getConfiguration();
-
- int dimAlphaInt = (int) (dimAlpha * 255);
mDimAlpha = dimAlpha;
- if (config.useHardwareLayers) {
- // Defer setting hardware layers if we have not yet measured, or there is no dim to draw
- if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0) {
- mDimColorFilter.setColor(Color.argb(dimAlphaInt, 0, 0, 0));
- mDimLayerPaint.setColorFilter(mDimColorFilter);
- mContent.setLayerType(LAYER_TYPE_HARDWARE, mDimLayerPaint);
- }
- } else {
- mThumbnailView.setDimAlpha(dimAlpha);
- mHeaderView.setDimAlpha(dimAlpha);
- }
+ mThumbnailView.setDimAlpha(dimAlpha);
+ mHeaderView.setDimAlpha(dimAlpha);
+ }
+
+ /**
+ * Sets the current dim without updating the header's dim.
+ */
+ public void setDimAlphaWithoutHeader(float dimAlpha) {
+ mDimAlpha = dimAlpha;
+ mThumbnailView.setDimAlpha(dimAlpha);
}
/**
@@ -413,25 +439,6 @@
}
/**
- * Animates the dim to the given value.
- */
- void animateDimAlpha(float toDimAlpha, AnimationProps animation) {
- // Animate the dim into view as well
- if (Float.compare(toDimAlpha, getDimAlpha()) != 0) {
- Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
- DIM_ALPHA, getDimAlpha(), toDimAlpha));
- if (animation.getListener() != null) {
- anim.addListener(animation.getListener());
- }
- anim.start();
- } else {
- if (animation.getListener() != null) {
- animation.getListener().onAnimationEnd(null);
- }
- }
- }
-
- /**
* Explicitly sets the focused state of this task.
*/
public void setFocusedState(boolean isFocused, boolean requestViewFocus) {
@@ -517,18 +524,20 @@
@Override
public void onPrepareLaunchTargetForEnterAnimation() {
// These values will be animated in when onStartLaunchTargetEnterAnimation() is called
- setDimAlpha(0);
+ setDimAlphaWithoutHeader(0);
mActionButtonView.setAlpha(0f);
}
@Override
- public void onStartLaunchTargetEnterAnimation(int duration, boolean screenPinningEnabled,
- ReferenceCountedTrigger postAnimationTrigger) {
- // Un-dim the view before/while launching the target
- AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT)
- .setListener(postAnimationTrigger.decrementOnAnimationEnd());
+ public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
+ boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) {
+ // Dim the view after the app window transitions down into recents
postAnimationTrigger.increment();
- animateDimAlpha(0, animation);
+ AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
+ Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
+ DIM_ALPHA_WITHOUT_HEADER, getDimAlpha(), transform.dimAlpha));
+ anim.addListener(postAnimationTrigger.decrementOnAnimationEnd());
+ anim.start();
if (screenPinningEnabled) {
showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
@@ -540,7 +549,9 @@
ReferenceCountedTrigger postAnimationTrigger) {
// Un-dim the view before/while launching the target
AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
- animateDimAlpha(0, animation);
+ Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
+ DIM_ALPHA, getDimAlpha(), 0));
+ anim.start();
postAnimationTrigger.increment();
hideActionButton(true /* fadeOut */, duration,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 05a8527..b2a7d90 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -347,9 +347,11 @@
* Sets the dim alpha, only used when we are not using hardware layers.
* (see RecentsConfiguration.useHardwareLayers)
*/
- void setDimAlpha(float dimAlpha) {
- mDimAlpha = dimAlpha;
- updateBackgroundColor(mBackground.getColor(), dimAlpha);
+ public void setDimAlpha(float dimAlpha) {
+ if (Float.compare(mDimAlpha, dimAlpha) != 0) {
+ mDimAlpha = dimAlpha;
+ updateBackgroundColor(mBackground.getColor(), dimAlpha);
+ }
}
/**
@@ -377,7 +379,9 @@
int primaryColor = disabledInSafeMode
? mDisabledTaskBarBackgroundColor
: t.colorPrimary;
- updateBackgroundColor(primaryColor, mDimAlpha);
+ if (mBackground.getColor() != primaryColor) {
+ updateBackgroundColor(primaryColor, mDimAlpha);
+ }
if (t.icon != null) {
mIconView.setImageDrawable(t.icon);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index 0d16a79..dc76e61 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -121,6 +121,18 @@
}
/**
+ * @return whether {@param other} is the same transform as this
+ */
+ public boolean isSame(TaskViewTransform other) {
+ return translationZ == other.translationZ
+ && scale == other.scale
+ && other.alpha == alpha
+ && dimAlpha == other.dimAlpha
+ && visible == other.visible
+ && rect.equals(other.rect);
+ }
+
+ /**
* Resets the current transform.
*/
public void reset() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index a06700d..eb08947 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -39,6 +39,7 @@
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.media.MediaActionSound;
import android.net.Uri;
import android.os.AsyncTask;
@@ -92,13 +93,13 @@
/**
* An AsyncTask that saves an image to the media store in the background.
*/
-class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void,
- SaveImageInBackgroundData> {
+class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
private static final String SCREENSHOTS_DIR_NAME = "Screenshots";
private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
+ private final SaveImageInBackgroundData mParams;
private final NotificationManager mNotificationManager;
private final Notification.Builder mNotificationBuilder, mPublicNotificationBuilder;
private final File mScreenshotDir;
@@ -121,6 +122,7 @@
Resources r = context.getResources();
// Prepare all the output metadata
+ mParams = data;
mImageTime = System.currentTimeMillis();
String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
@@ -209,17 +211,17 @@
}
@Override
- protected SaveImageInBackgroundData doInBackground(SaveImageInBackgroundData... params) {
+ protected Void doInBackground(Void... params) {
if (isCancelled()) {
- return params[0];
+ return null;
}
// By default, AsyncTask sets the worker thread to have background thread priority, so bump
// it back up so that we save a little quicker.
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
- Context context = params[0].context;
- Bitmap image = params[0].image;
+ Context context = mParams.context;
+ Bitmap image = mParams.image;
Resources r = context.getResources();
try {
@@ -283,14 +285,14 @@
r.getString(com.android.internal.R.string.delete), deleteAction);
mNotificationBuilder.addAction(deleteActionBuilder.build());
- params[0].imageUri = uri;
- params[0].image = null;
- params[0].errorMsgResId = 0;
+ mParams.imageUri = uri;
+ mParams.image = null;
+ mParams.errorMsgResId = 0;
} catch (Exception e) {
// IOException/UnsupportedOperationException may be thrown if external storage is not
// mounted
- params[0].clearImage();
- params[0].errorMsgResId = R.string.screenshot_failed_to_save_text;
+ mParams.clearImage();
+ mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
}
// Recycle the bitmap data
@@ -298,23 +300,23 @@
image.recycle();
}
- return params[0];
+ return null;
}
@Override
- protected void onPostExecute(SaveImageInBackgroundData params) {
- if (params.errorMsgResId != 0) {
+ protected void onPostExecute(Void params) {
+ if (mParams.errorMsgResId != 0) {
// Show a message that we've failed to save the image to disk
- GlobalScreenshot.notifyScreenshotError(params.context, mNotificationManager,
- params.errorMsgResId);
+ GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,
+ mParams.errorMsgResId);
} else {
// Show the final notification to indicate screenshot saved
- Context context = params.context;
+ Context context = mParams.context;
Resources r = context.getResources();
// Create the intent to show the screenshot in gallery
Intent launchIntent = new Intent(Intent.ACTION_VIEW);
- launchIntent.setDataAndType(params.imageUri, "image/png");
+ launchIntent.setDataAndType(mParams.imageUri, "image/png");
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
final long now = System.currentTimeMillis();
@@ -323,7 +325,7 @@
mPublicNotificationBuilder
.setContentTitle(r.getString(R.string.screenshot_saved_title))
.setContentText(r.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0))
+ .setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
.setWhen(now)
.setAutoCancel(true)
.setColor(context.getColor(
@@ -331,7 +333,7 @@
mNotificationBuilder
.setContentTitle(r.getString(R.string.screenshot_saved_title))
.setContentText(r.getString(R.string.screenshot_saved_text))
- .setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0))
+ .setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
.setWhen(now)
.setAutoCancel(true)
.setColor(context.getColor(
@@ -341,15 +343,18 @@
mNotificationManager.notify(R.id.notification_screenshot, mNotificationBuilder.build());
}
- params.finisher.run();
- params.clearContext();
+ mParams.finisher.run();
+ mParams.clearContext();
}
@Override
- protected void onCancelled(SaveImageInBackgroundData params) {
- params.finisher.run();
- params.clearImage();
- params.clearContext();
+ protected void onCancelled(Void params) {
+ // If we are cancelled while the task is running in the background, we may get null params.
+ // The finisher is expected to always be called back, so just use the baked-in params from
+ // the ctor in any case.
+ mParams.finisher.run();
+ mParams.clearImage();
+ mParams.clearContext();
// Cancel the posted notification
mNotificationManager.cancel(R.id.notification_screenshot);
@@ -407,6 +412,7 @@
private Bitmap mScreenBitmap;
private View mScreenshotLayout;
+ private ScreenshotSelectorView mScreenshotSelectorView;
private ImageView mBackgroundView;
private ImageView mScreenshotView;
private ImageView mScreenshotFlash;
@@ -417,7 +423,7 @@
private float mBgPadding;
private float mBgPaddingScale;
- private AsyncTask<SaveImageInBackgroundData, Void, SaveImageInBackgroundData> mSaveInBgTask;
+ private AsyncTask<Void, Void, Void> mSaveInBgTask;
private MediaActionSound mCameraSound;
@@ -437,7 +443,11 @@
mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);
mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);
mScreenshotFlash = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
+ mScreenshotSelectorView = (ScreenshotSelectorView) mScreenshotLayout.findViewById(
+ R.id.global_screenshot_selector);
mScreenshotLayout.setFocusable(true);
+ mScreenshotSelectorView.setFocusable(true);
+ mScreenshotSelectorView.setFocusableInTouchMode(true);
mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
@@ -449,7 +459,7 @@
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
- WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.TYPE_SCREENSHOT,
WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
@@ -504,7 +514,7 @@
mSaveInBgTask.cancel(false);
}
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager)
- .execute(data);
+ .execute();
}
/**
@@ -525,7 +535,8 @@
/**
* Takes a screenshot of the current display and shows an animation.
*/
- void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
+ void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
+ int x, int y, int width, int height) {
// We need to orient the screenshot correctly (and the Surface api seems to take screenshots
// only in the natural orientation of the device :!)
mDisplay.getRealMetrics(mDisplayMetrics);
@@ -565,6 +576,13 @@
mScreenBitmap = ss;
}
+ if (width != mDisplayMetrics.widthPixels || height != mDisplayMetrics.heightPixels) {
+ // Crop the screenshot to selected region
+ Bitmap cropped = Bitmap.createBitmap(mScreenBitmap, x, y, width, height);
+ mScreenBitmap.recycle();
+ mScreenBitmap = cropped;
+ }
+
// Optimizations
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
@@ -574,6 +592,71 @@
statusBarVisible, navBarVisible);
}
+ void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
+ mDisplay.getRealMetrics(mDisplayMetrics);
+ takeScreenshot(finisher, statusBarVisible, navBarVisible, 0, 0, mDisplayMetrics.widthPixels,
+ mDisplayMetrics.heightPixels);
+ }
+
+ /**
+ * Displays a screenshot selector
+ */
+ void takeScreenshotPartial(final Runnable finisher, final boolean statusBarVisible,
+ final boolean navBarVisible) {
+ mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ ScreenshotSelectorView view = (ScreenshotSelectorView) v;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ view.startSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ view.updateSelection((int) event.getX(), (int) event.getY());
+ return true;
+ case MotionEvent.ACTION_UP:
+ view.setVisibility(View.GONE);
+ mWindowManager.removeView(mScreenshotLayout);
+ final Rect rect = view.getSelectionRect();
+ if (rect != null) {
+ if (rect.width() != 0 && rect.height() != 0) {
+ // Need mScreenshotLayout to handle it after the view disappears
+ mScreenshotLayout.post(new Runnable() {
+ public void run() {
+ takeScreenshot(finisher, statusBarVisible, navBarVisible,
+ rect.left, rect.top, rect.width(), rect.height());
+ }
+ });
+ }
+ }
+
+ view.stopSelection();
+ return true;
+ }
+
+ return false;
+ }
+ });
+ mScreenshotLayout.post(new Runnable() {
+ @Override
+ public void run() {
+ mScreenshotSelectorView.setVisibility(View.VISIBLE);
+ mScreenshotSelectorView.requestFocus();
+ }
+ });
+ }
+
+ /**
+ * Cancels screenshot request
+ */
+ void stopScreenshot() {
+ // If the selector layer still presents on screen, we remove it and resets its state.
+ if (mScreenshotSelectorView.getSelectionRect() != null) {
+ mWindowManager.removeView(mScreenshotLayout);
+ mScreenshotSelectorView.stopSelection();
+ }
+ }
/**
* Starts the animation after taking the screenshot
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
new file mode 100644
index 0000000..07a9246
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSelectorView.java
@@ -0,0 +1,82 @@
+/*
+ * 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.screenshot;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Draws a selection rectangle while taking screenshot
+ */
+public class ScreenshotSelectorView extends View {
+ private Point mStartPoint;
+ private Rect mSelectionRect;
+ private final Paint mPaintSelection, mPaintBackground;
+
+ public ScreenshotSelectorView(Context context) {
+ this(context, null);
+ }
+
+ public ScreenshotSelectorView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mPaintBackground = new Paint(Color.BLACK);
+ mPaintBackground.setAlpha(160);
+ mPaintSelection = new Paint(Color.TRANSPARENT);
+ mPaintSelection.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ }
+
+ public void startSelection(int x, int y) {
+ mStartPoint = new Point(x, y);
+ mSelectionRect = new Rect(x, y, x, y);
+ }
+
+ public void updateSelection(int x, int y) {
+ if (mSelectionRect != null) {
+ mSelectionRect.left = Math.min(mStartPoint.x, x);
+ mSelectionRect.right = Math.max(mStartPoint.x, x);
+ mSelectionRect.top = Math.min(mStartPoint.y, y);
+ mSelectionRect.bottom = Math.max(mStartPoint.y, y);
+ invalidate();
+ }
+ }
+
+ public Rect getSelectionRect() {
+ return mSelectionRect;
+ }
+
+ public void stopSelection() {
+ mStartPoint = null;
+ mSelectionRect = null;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawRect(mLeft, mTop, mRight, mBottom, mPaintBackground);
+ if (mSelectionRect != null) {
+ canvas.drawRect(mSelectionRect, mPaintSelection);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
new file mode 100644
index 0000000..fc2a1e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceErrorReceiver.java
@@ -0,0 +1,40 @@
+/*
+ * 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.screenshot;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.systemui.R;
+
+/**
+ * Performs a number of miscellaneous, non-system-critical actions
+ * after the system has finished booting.
+ */
+public class ScreenshotServiceErrorReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ // Show a message that we've failed to save the image to disk
+ NotificationManager nm = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ GlobalScreenshot.notifyScreenshotError(context, nm,
+ R.string.screenshot_failed_to_save_unknown_text);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 456b5fa..4badc42 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -23,6 +23,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.view.WindowManager;
public class TakeScreenshotService extends Service {
private static final String TAG = "TakeScreenshotService";
@@ -32,21 +33,28 @@
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- switch (msg.what) {
- case 1:
- final Messenger callback = msg.replyTo;
- if (mScreenshot == null) {
- mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
+ final Messenger callback = msg.replyTo;
+ Runnable finisher = new Runnable() {
+ @Override
+ public void run() {
+ Message reply = Message.obtain(null, 1);
+ try {
+ callback.send(reply);
+ } catch (RemoteException e) {
}
- mScreenshot.takeScreenshot(new Runnable() {
- @Override public void run() {
- Message reply = Message.obtain(null, 1);
- try {
- callback.send(reply);
- } catch (RemoteException e) {
- }
- }
- }, msg.arg1 > 0, msg.arg2 > 0);
+ }
+ };
+ if (mScreenshot == null) {
+ mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
+ }
+
+ switch (msg.what) {
+ case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
+ mScreenshot.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0);
+ break;
+ case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
+ mScreenshot.takeScreenshotPartial(finisher, msg.arg1 > 0, msg.arg2 > 0);
+ break;
}
}
};
@@ -55,4 +63,10 @@
public IBinder onBind(Intent intent) {
return new Messenger(mHandler).getBinder();
}
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ if (mScreenshot != null) mScreenshot.stopScreenshot();
+ return true;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 8f8683b..69dcabe 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -17,18 +17,31 @@
package com.android.systemui.shortcut;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
import android.os.RemoteException;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.IWindowManager;
import android.view.KeyEvent;
+import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.settingslib.accessibility.AccessibilityUtils;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.DividerView;
+import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
import java.util.List;
import java.util.Set;
@@ -42,28 +55,70 @@
private static final String TAG = "ShortcutKeyDispatcher";
private ShortcutKeyServiceProxy mShortcutKeyServiceProxy = new ShortcutKeyServiceProxy(this);
- private IWindowManager windowManagerService = WindowManagerGlobal.getWindowManagerService();
+ private IWindowManager mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+ private IActivityManager mActivityManager = ActivityManagerNative.getDefault();
protected final long META_MASK = ((long) KeyEvent.META_META_ON) << Integer.SIZE;
protected final long ALT_MASK = ((long) KeyEvent.META_ALT_ON) << Integer.SIZE;
protected final long CTRL_MASK = ((long) KeyEvent.META_CTRL_ON) << Integer.SIZE;
protected final long SHIFT_MASK = ((long) KeyEvent.META_SHIFT_ON) << Integer.SIZE;
+ protected final long SC_DOCK_LEFT = META_MASK | KeyEvent.KEYCODE_LEFT_BRACKET;
+ protected final long SC_DOCK_RIGHT = META_MASK | KeyEvent.KEYCODE_RIGHT_BRACKET;
+
/**
* Registers a shortcut key to window manager.
* @param shortcutCode packed representation of shortcut key code and meta information
*/
public void registerShortcutKey(long shortcutCode) {
try {
- windowManagerService.registerShortcutKey(shortcutCode, mShortcutKeyServiceProxy);
+ mWindowManagerService.registerShortcutKey(shortcutCode, mShortcutKeyServiceProxy);
} catch (RemoteException e) {
// Do nothing
}
}
@Override
- public void onShortcutKeyPressed(long shortcutCode) {}
+ public void onShortcutKeyPressed(long shortcutCode) {
+ int orientation = mContext.getResources().getConfiguration().orientation;
+ if ((shortcutCode == SC_DOCK_LEFT || shortcutCode == SC_DOCK_RIGHT)
+ && orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ handleDockKey(shortcutCode);
+ }
+ }
@Override
- public void start() {}
+ public void start() {
+ registerShortcutKey(SC_DOCK_LEFT);
+ registerShortcutKey(SC_DOCK_RIGHT);
+ }
+
+ private void handleDockKey(long shortcutCode) {
+ try {
+ int dockSide = mWindowManagerService.getDockedStackSide();
+ if (dockSide == WindowManager.DOCKED_INVALID) {
+ // If there is no window docked, we dock the top-most window.
+ Recents recents = getComponent(Recents.class);
+ int dockMode = (shortcutCode == SC_DOCK_LEFT)
+ ? ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+ : ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ recents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE, dockMode, null);
+ MetricsLogger.action(mContext, MetricsEvent.WINDOW_DOCK_SHORTCUTS);
+ } else {
+ // If there is already a docked window, we respond by resizing the docking pane.
+ DividerView dividerView = getComponent(Divider.class).getView();
+ DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
+ int dividerPosition = dividerView.getCurrentPosition();
+ DividerSnapAlgorithm.SnapTarget currentTarget =
+ snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
+ int increment = (shortcutCode == SC_DOCK_LEFT) ? -1 : 1;
+ DividerSnapAlgorithm.SnapTarget target = snapAlgorithm.cycleNonDismissTarget(
+ currentTarget, increment);
+ dividerView.startDragging(true /* animate */, false /* touching */);
+ dividerView.stopDragging(target.position, 0f, true /* avoidDismissStart */);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "handleDockKey() failed.");
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index da5cbe7..e015666 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -16,6 +16,9 @@
package com.android.systemui.stackdivider;
+import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
+import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -31,6 +34,9 @@
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnDoubleTapListener;
+import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.VelocityTracker;
@@ -39,6 +45,7 @@
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -52,17 +59,16 @@
import com.android.internal.policy.DockedDividerUtils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.DockingTopTaskEvent;
+import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.activity.UndockingTaskEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
-import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
-import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
-
/**
* Docked stack divider.
*/
@@ -123,6 +129,7 @@
private final Rect mDockedInsetRect = new Rect();
private final Rect mOtherInsetRect = new Rect();
private final Rect mLastResizeRect = new Rect();
+ private final Rect mDisplayRect = new Rect();
private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance();
private DividerWindowManager mWindowManager;
private VelocityTracker mVelocityTracker;
@@ -133,7 +140,9 @@
private boolean mAnimateAfterRecentsDrawn;
private boolean mGrowAfterRecentsDrawn;
private boolean mGrowRecents;
- private Animator mCurrentAnimator;
+ private ValueAnimator mCurrentAnimator;
+ private boolean mEntranceAnimationRunning;
+ private GestureDetector mGestureDetector;
private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
@Override
@@ -211,12 +220,35 @@
landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW));
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
mHandle.setAccessibilityDelegate(mHandleDelegate);
+ mGestureDetector = new GestureDetector(mContext, new SimpleOnGestureListener() {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ updateDockSide();
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (mDockSide != WindowManager.DOCKED_INVALID
+ && !ssp.isRecentsTopMost(ssp.getTopMostTask(), null /* isTopHome */)) {
+ mWindowManagerProxy.swapTasks();
+ return true;
+ }
+ return false;
+ }
+ });
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
+ getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+
+ @Override
+ public void onGlobalLayout() {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(),
+ mHandle.getLeft() + mHandle.getWidth(),
+ mHandle.getTop() + mHandle.getHeight()));
+ }
+ });
}
@Override
@@ -318,6 +350,7 @@
@Override
public boolean onTouch(View v, MotionEvent event) {
convertToScreenCoordinates(event);
+ mGestureDetector.onTouchEvent(event);
final int action = event.getAction() & MotionEvent.ACTION_MASK;
switch (action) {
case MotionEvent.ACTION_DOWN:
@@ -411,6 +444,7 @@
mWindowManagerProxy.setResizing(false);
mDockSide = WindowManager.DOCKED_INVALID;
mCurrentAnimator = null;
+ mEntranceAnimationRunning = false;
}
});
mCurrentAnimator = anim;
@@ -594,7 +628,18 @@
}
mLastResizeRect.set(mDockedRect);
- if (taskPosition != TASK_POSITION_SAME) {
+ if (mEntranceAnimationRunning && taskPosition != TASK_POSITION_SAME) {
+ if (mCurrentAnimator != null) {
+ calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
+ } else {
+ calculateBoundsForPosition(isHorizontalDivision() ? mDisplayHeight : mDisplayWidth,
+ mDockSide, mDockedTaskRect);
+ }
+ calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide),
+ mOtherTaskRect);
+ mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
+ mOtherTaskRect, null);
+ } else if (taskPosition != TASK_POSITION_SAME) {
calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
mOtherRect);
int dockSideInverted = DockedDividerUtils.invertDockSide(mDockSide);
@@ -610,16 +655,17 @@
calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
+ mDisplayRect.set(0, 0, mDisplayWidth, mDisplayHeight);
alignTopLeft(mDockedRect, mDockedTaskRect);
alignTopLeft(mOtherRect, mOtherTaskRect);
mDockedInsetRect.set(mDockedTaskRect);
mOtherInsetRect.set(mOtherTaskRect);
if (dockSideTopLeft(mDockSide)) {
- alignTopLeft(mDockedRect, mDockedInsetRect);
- alignBottomRight(mOtherRect, mOtherInsetRect);
+ alignTopLeft(mDisplayRect, mDockedInsetRect);
+ alignBottomRight(mDisplayRect, mOtherInsetRect);
} else {
- alignBottomRight(mDockedRect, mDockedInsetRect);
- alignTopLeft(mOtherRect, mOtherInsetRect);
+ alignBottomRight(mDisplayRect, mDockedInsetRect);
+ alignTopLeft(mDisplayRect, mOtherInsetRect);
}
applyDismissingParallax(mDockedTaskRect, mDockSide, taskSnapTarget, position,
taskPositionDocked);
@@ -638,6 +684,9 @@
}
private float getDimFraction(int position, SnapTarget dismissTarget) {
+ if (mEntranceAnimationRunning) {
+ return 0f;
+ }
float fraction = mSnapAlgorithm.calculateDismissingFraction(position);
fraction = Math.max(0, Math.min(fraction, 1f));
fraction = DIM_INTERPOLATOR.getInterpolation(fraction);
@@ -839,12 +888,18 @@
}
}
- public final void onBusEvent(DockingTopTaskEvent dockingEvent) {
- if (dockingEvent.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) {
+ public final void onBusEvent(DockedTopTaskEvent event) {
+ if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) {
mGrowAfterRecentsDrawn = false;
mAnimateAfterRecentsDrawn = true;
startDragging(false /* animate */, false /* touching */);
}
+ updateDockSide();
+ int position = DockedDividerUtils.calculatePositionForBounds(event.initialRect,
+ mDockSide, mDividerSize);
+ mEntranceAnimationRunning = true;
+ resizeStack(position, mSnapAlgorithm.getMiddleTarget().position,
+ mSnapAlgorithm.getMiddleTarget());
}
public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 15bcaf8..e312fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -16,6 +16,9 @@
package com.android.systemui.stackdivider;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.view.WindowManager.DOCKED_INVALID;
+
import android.app.ActivityManagerNative;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -27,9 +30,6 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.view.WindowManager.DOCKED_INVALID;
-
/**
* Proxy to simplify calls into window manager/activity manager
*/
@@ -39,7 +39,7 @@
private static final WindowManagerProxy sInstance = new WindowManagerProxy();
- @GuardedBy("mResizeRect")
+ @GuardedBy("mDockedRect")
private final Rect mDockedRect = new Rect();
private final Rect mTempDockedTaskRect = new Rect();
private final Rect mTempDockedInsetRect = new Rect();
@@ -52,6 +52,9 @@
private final Rect mTmpRect4 = new Rect();
private final Rect mTmpRect5 = new Rect();
+ @GuardedBy("mDockedRect")
+ private final Rect mTouchableRegion = new Rect();
+
private boolean mDimLayerVisible;
private int mDimLayerTargetStack;
private float mDimLayerAlpha;
@@ -117,6 +120,32 @@
}
};
+ private final Runnable mSwapRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ ActivityManagerNative.getDefault().swapDockedAndFullscreenStack();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to resize stack: " + e);
+ }
+ }
+ };
+
+ private final Runnable mSetTouchableRegionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ synchronized (mDockedRect) {
+ mTmpRect1.set(mTouchableRegion);
+ }
+ WindowManagerGlobal.getWindowManagerService().setDockedStackDividerTouchRegion(
+ mTmpRect1);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to set touchable region: " + e);
+ }
+ }
+ };
+
private WindowManagerProxy() {
}
@@ -188,4 +217,15 @@
mDimLayerAlpha = alpha;
mExecutor.execute(mDimLayerRunnable);
}
+
+ public void swapTasks() {
+ mExecutor.execute(mSwapRunnable);
+ }
+
+ public void setTouchRegion(Rect region) {
+ synchronized (mDockedRect) {
+ mTouchableRegion.set(region);
+ }
+ mExecutor.execute(mSetTouchableRegionRunnable);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index effe581..71f74cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -36,7 +36,9 @@
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
/**
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -121,6 +123,7 @@
private float mAnimationTranslationY;
private boolean mDrawingAppearAnimation;
private ValueAnimator mAppearAnimator;
+ private ValueAnimator mBackgroundColorAnimator;
private float mAppearAnimationFraction = -1.0f;
private float mAppearAnimationTranslation;
private boolean mShowingLegacyBackground;
@@ -145,7 +148,7 @@
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mFadeInFromDarkAnimator = null;
- updateOutlineAlpha();
+ updateBackground();
}
};
private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener
@@ -157,6 +160,9 @@
};
private float mShadowAlpha = 1.0f;
private FakeShadowView mFakeShadow;
+ private int mCurrentBackgroundTint;
+ private int mTargetTint;
+ private int mStartTint;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -334,9 +340,7 @@
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- if (mDimmed) {
- mBackgroundNormal.setVisibility(View.INVISIBLE);
- }
+ updateBackground();
}
});
animator.start();
@@ -365,14 +369,14 @@
*/
public void makeInactive(boolean animate) {
if (mActivated) {
+ mActivated = false;
if (mDimmed) {
if (animate) {
startActivateAnimation(true /* reverse */);
} else {
- mBackgroundNormal.setVisibility(View.INVISIBLE);
+ updateBackground();
}
}
- mActivated = false;
}
if (mOnActivatedListener != null) {
mOnActivatedListener.onActivationReset(this);
@@ -402,20 +406,9 @@
return;
}
mDark = dark;
+ updateBackground();
if (!dark && fade && !shouldHideBackground()) {
- if (mActivated) {
- mBackgroundDimmed.setVisibility(View.VISIBLE);
- mBackgroundNormal.setVisibility(View.VISIBLE);
- } else if (mDimmed) {
- mBackgroundDimmed.setVisibility(View.VISIBLE);
- mBackgroundNormal.setVisibility(View.INVISIBLE);
- } else {
- mBackgroundDimmed.setVisibility(View.INVISIBLE);
- mBackgroundNormal.setVisibility(View.VISIBLE);
- }
fadeInFromDark(delay);
- } else {
- updateBackground();
}
updateOutlineAlpha();
}
@@ -457,21 +450,63 @@
* Sets the tint color of the background
*/
public void setTintColor(int color) {
+ setTintColor(color, false);
+ }
+
+ /**
+ * Sets the tint color of the background
+ */
+ public void setTintColor(int color, boolean animated) {
mBgTint = color;
- updateBackgroundTint();
+ updateBackgroundTint(animated);
}
protected void updateBackgroundTint() {
- int color = getBgColor();
+ updateBackgroundTint(false /* animated */);
+ }
+
+ private void updateBackgroundTint(boolean animated) {
+ if (mBackgroundColorAnimator != null) {
+ mBackgroundColorAnimator.cancel();
+ }
int rippleColor = getRippleColor();
+ mBackgroundDimmed.setRippleColor(rippleColor);
+ mBackgroundNormal.setRippleColor(rippleColor);
+ int color = calculateBgColor();
+ if (!animated) {
+ setBackgroundTintColor(color);
+ } else if (color != mCurrentBackgroundTint) {
+ mStartTint = mCurrentBackgroundTint;
+ mTargetTint = color;
+ mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
+ animation.getAnimatedFraction());
+ setBackgroundTintColor(newColor);
+ }
+ });
+ mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
+ mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBackgroundColorAnimator = null;
+ }
+ });
+ mBackgroundColorAnimator.start();
+ }
+ }
+
+ private void setBackgroundTintColor(int color) {
+ mCurrentBackgroundTint = color;
if (color == mNormalColor) {
// We don't need to tint a normal notification
color = 0;
}
mBackgroundDimmed.setTint(color);
mBackgroundNormal.setTint(color);
- mBackgroundDimmed.setRippleColor(rippleColor);
- mBackgroundNormal.setRippleColor(rippleColor);
}
/**
@@ -480,6 +515,7 @@
private void fadeInFromDark(long delay) {
final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal;
background.setAlpha(0f);
+ mBackgroundVisibilityUpdater.onAnimationUpdate(null);
background.setPivotX(mBackgroundDimmed.getWidth() / 2f);
background.setPivotY(getActualHeight() / 2f);
background.setScaleX(DARK_EXIT_SCALE_START);
@@ -517,6 +553,10 @@
private void fadeDimmedBackground() {
mBackgroundDimmed.animate().cancel();
mBackgroundNormal.animate().cancel();
+ if (mActivated) {
+ updateBackground();
+ return;
+ }
if (!shouldHideBackground()) {
if (mDimmed) {
mBackgroundDimmed.setVisibility(View.VISIBLE);
@@ -546,11 +586,7 @@
mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- if (mDimmed) {
- mBackgroundNormal.setVisibility(View.INVISIBLE);
- } else {
- mBackgroundDimmed.setVisibility(View.INVISIBLE);
- }
+ updateBackground();
mBackgroundAnimator = null;
}
});
@@ -565,12 +601,14 @@
mBackgroundNormal.setVisibility(View.INVISIBLE);
} else if (mDimmed) {
mBackgroundDimmed.setVisibility(View.VISIBLE);
- mBackgroundNormal.setVisibility(View.INVISIBLE);
+ mBackgroundNormal.setVisibility(mActivated ? View.VISIBLE : View.INVISIBLE);
} else {
mBackgroundDimmed.setVisibility(View.INVISIBLE);
mBackgroundNormal.setVisibility(View.VISIBLE);
mBackgroundNormal.setAlpha(1f);
removeCallbacks(mTapTimeoutRunnable);
+ // make in inactive to avoid it sticking around active
+ makeInactive(false /* animate */);
}
setNormalBackgroundVisibilityAmount(
mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
@@ -773,8 +811,12 @@
protected abstract View getContentView();
- public int getBgColor() {
- if (mBgTint != 0) {
+ public int calculateBgColor() {
+ return calculateBgColor(true /* withTint */);
+ }
+
+ private int calculateBgColor(boolean withTint) {
+ if (withTint && mBgTint != 0) {
return mBgTint;
} else if (mShowingLegacyBackground) {
return mLegacyColor;
@@ -839,7 +881,7 @@
}
public boolean hasSameBgColor(ActivatableNotificationView otherView) {
- return getBgColor() == otherView.getBgColor();
+ return calculateBgColor() == otherView.calculateBgColor();
}
@Override
@@ -863,6 +905,10 @@
outlineTranslation);
}
+ public int getBackgroundColorWithoutTint() {
+ return calculateBgColor(false /* withTint */);
+ }
+
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 469a1fc..143f160 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1891,7 +1891,7 @@
}
}
- protected abstract boolean isPanelFullyCollapsed();
+ public abstract boolean isPanelFullyCollapsed();
/**
* Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 84b84e2..246f15e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -105,7 +105,6 @@
private boolean mClearable;
private ExpansionLogger mLogger;
private String mLoggingKey;
- private boolean mWasReset;
private NotificationSettingsIconRow mSettingsIconRow;
private NotificationGuts mGuts;
private NotificationData.Entry mEntry;
@@ -242,18 +241,22 @@
}
private void updateLimits() {
- boolean customView = getPrivateLayout().getContractedChild().getId()
+ updateLimitsForView(mPrivateLayout);
+ updateLimitsForView(mPublicLayout);
+ }
+
+ private void updateLimitsForView(NotificationContentView layout) {
+ boolean customView = layout.getContractedChild().getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
mNotificationMinHeightLegacy : mNotificationMinHeight;
- boolean headsUpCustom = getPrivateLayout().getHeadsUpChild() != null &&
- getPrivateLayout().getHeadsUpChild().getId()
- != com.android.internal.R.id.status_bar_latest_event_content;
+ boolean headsUpCustom = layout.getHeadsUpChild() != null &&
+ layout.getHeadsUpChild().getId()
+ != com.android.internal.R.id.status_bar_latest_event_content;
int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
: mMaxHeadsUpHeight;
- mPrivateLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
- mPublicLayout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
+ layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
}
public StatusBarNotification getStatusBarNotification() {
@@ -412,8 +415,7 @@
}
public ExpandableNotificationRow getViewAtPosition(float y) {
- if (!mIsSummaryWithChildren || !mChildrenExpanded
- || (getNotificationChildren().size() == 1 && isClearable())) {
+ if (!mIsSummaryWithChildren || !mChildrenExpanded) {
return this;
} else {
ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
@@ -570,6 +572,13 @@
mPublicLayout.reInflateViews();
}
+ public void setContentBackground(int customBackgroundColor, boolean animate,
+ NotificationContentView notificationContentView) {
+ if (getShowingLayout() == notificationContentView) {
+ setTintColor(customBackgroundColor, animate);
+ }
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -615,20 +624,16 @@
mShowingPublicInitialized = false;
mIsSystemExpanded = false;
mOnKeyguard = false;
- mPublicLayout.reset(mIsHeadsUp);
- mPrivateLayout.reset(mIsHeadsUp);
+ mPublicLayout.reset();
+ mPrivateLayout.reset();
resetHeight();
resetTranslation();
logExpansionEvent(false, wasExpanded);
}
public void resetHeight() {
- if (mIsHeadsUp) {
- resetActualHeight();
- }
mMaxExpandHeight = 0;
mHeadsUpHeight = 0;
- mWasReset = true;
onHeightReset();
requestLayout();
}
@@ -740,6 +745,9 @@
public Animator getTranslateViewAnimator(final float leftTarget,
AnimatorUpdateListener listener) {
+ if (mTranslateAnim != null) {
+ mTranslateAnim.cancel();
+ }
if (areGutsExposed()) {
// No translation if guts are exposed.
return null;
@@ -764,19 +772,27 @@
if (listener != null) {
translateAnim.addUpdateListener(listener);
}
- }
- translateAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator anim) {
- if (mSettingsIconRow != null && leftTarget == 0) {
- mSettingsIconRow.resetState();
+ translateAnim.addListener(new AnimatorListenerAdapter() {
+ boolean cancelled = false;
+
+ @Override
+ public void onAnimationCancel(Animator anim) {
+ cancelled = true;
}
- mTranslateAnim = null;
- }
- });
+
+ @Override
+ public void onAnimationEnd(Animator anim) {
+ if (!cancelled && mSettingsIconRow != null && leftTarget == 0) {
+ mSettingsIconRow.resetState();
+ mTranslateAnim = null;
+ }
+ }
+ });
+ }
set.play(translateAnim);
}
}
+ mTranslateAnim = set;
return set;
}
@@ -964,18 +980,6 @@
return mStatusBarNotification != null && mStatusBarNotification.isClearable();
}
- /**
- * Apply an expansion state to the layout.
- */
- public void applyExpansionToLayout() {
- boolean expand = isExpanded();
- if (expand && mExpandable) {
- setActualHeight(mMaxExpandHeight);
- } else {
- setActualHeight(getMinHeight());
- }
- }
-
@Override
public int getIntrinsicHeight() {
if (isUserLocked()) {
@@ -1028,7 +1032,6 @@
}
mPrivateLayout.updateExpandButtons(isExpandable());
updateChildrenHeaderAppearance();
- updateHeaderChildCount();
updateChildrenVisibility();
}
@@ -1041,7 +1044,11 @@
* @return whether the view state is currently expanded.
*/
public boolean isExpanded() {
- return !mOnKeyguard
+ return isExpanded(false /* allowOnKeyguard */);
+ }
+
+ public boolean isExpanded(boolean allowOnKeyguard) {
+ return (!mOnKeyguard || allowOnKeyguard)
&& (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
|| isUserExpanded());
}
@@ -1057,12 +1064,7 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- boolean updateExpandHeight = mMaxExpandHeight == 0 && !mWasReset;
updateMaxHeights();
- if (updateExpandHeight) {
- applyExpansionToLayout();
- }
- mWasReset = false;
}
private void updateMaxHeights() {
@@ -1126,7 +1128,8 @@
} else {
animateShowingPublic(delay, duration);
}
-
+ NotificationContentView showingLayout = getShowingLayout();
+ showingLayout.updateBackgroundColor(animated);
mPrivateLayout.updateExpandButtons(isExpandable());
updateClearability();
mShowingPublicInitialized = true;
@@ -1184,13 +1187,6 @@
}
}
- public void updateHeaderChildCount() {
- if (mIsSummaryWithChildren) {
- mNotificationHeader.setChildCount(
- mChildrenContainer.getNotificationChildren().size());
- }
- }
-
public static void applyTint(View v, int color) {
int alpha;
if (color != 0) {
@@ -1302,15 +1298,7 @@
header.reapply(getContext(), mNotificationHeader);
mNotificationHeaderWrapper.notifyContentUpdated(mEntry.notification);
}
- updateHeaderExpandButton();
updateChildrenHeaderAppearance();
- updateHeaderChildCount();
- }
-
- private void updateHeaderExpandButton() {
- if (mIsSummaryWithChildren) {
- mNotificationHeader.setIsGroupHeader(true /* isGroupHeader*/);
- }
}
public void updateChildrenHeaderAppearance() {
@@ -1376,7 +1364,7 @@
float y = event.getY();
NotificationHeaderView header = getVisibleNotificationHeader();
if (header != null) {
- return header.isInTouchRect(x, y);
+ return header.isInTouchRect(x - getTranslation(), y);
}
return super.disallowSingleClick(event);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index b2c632b..91418ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -36,7 +36,6 @@
protected OnHeightChangedListener mOnHeightChangedListener;
private int mActualHeight;
protected int mClipTopAmount;
- private boolean mActualHeightInitialized;
private boolean mDark;
private ArrayList<View> mMatchParentViews = new ArrayList<View>();
private int mClipTopOptimization;
@@ -44,6 +43,7 @@
private boolean mWillBeGone;
private int mMinClipTopAmount = 0;
private boolean mClipToActualHeight = true;
+ private boolean mChangingPosition = false;
public ExpandableView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -99,28 +99,9 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- if (!mActualHeightInitialized && mActualHeight == 0) {
- int initialHeight = getInitialHeight();
- if (initialHeight != 0) {
- setActualHeight(initialHeight);
- }
- }
updateClipping();
}
- /**
- * Resets the height of the view on the next layout pass
- */
- protected void resetActualHeight() {
- mActualHeight = 0;
- mActualHeightInitialized = false;
- requestLayout();
- }
-
- protected int getInitialHeight() {
- return getHeight();
- }
-
@Override
public boolean pointInView(float localX, float localY, float slop) {
float top = mClipTopAmount;
@@ -137,7 +118,6 @@
* @param notifyListeners Whether the listener should be informed about the change.
*/
public void setActualHeight(int actualHeight, boolean notifyListeners) {
- mActualHeightInitialized = true;
mActualHeight = actualHeight;
updateClipping();
if (notifyListeners) {
@@ -428,6 +408,14 @@
return 0;
}
+ public void setChangingPosition(boolean changingPosition) {
+ mChangingPosition = changingPosition;
+ }
+
+ public boolean isChangingPosition() {
+ return mChangingPosition;
+ }
+
/**
* A listener notifying when {@link #getActualHeight} changes.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 61105f8..3b87577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
+import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.NotificationViewWrapper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -48,7 +49,7 @@
private static final int VISIBLE_TYPE_EXPANDED = 1;
private static final int VISIBLE_TYPE_HEADSUP = 2;
private static final int VISIBLE_TYPE_SINGLELINE = 3;
- private static final int UNDEFINED = -1;
+ public static final int UNDEFINED = -1;
private final Rect mClipBounds = new Rect();
private final int mMinContractedHeight;
@@ -120,7 +121,7 @@
R.dimen.min_notification_layout_height);
mNotificationContentMarginEnd = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_content_margin_end);
- reset(true);
+ reset();
}
public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
@@ -255,7 +256,7 @@
updateVisibility();
}
- public void reset(boolean resetActualHeight) {
+ public void reset() {
if (mContractedChild != null) {
mContractedChild.animate().cancel();
removeView(mContractedChild);
@@ -271,10 +272,6 @@
mContractedChild = null;
mExpandedChild = null;
mHeadsUpChild = null;
- mVisibleType = VISIBLE_TYPE_CONTRACTED;
- if (resetActualHeight) {
- mContentHeight = mSmallHeight;
- }
}
public View getContractedChild() {
@@ -371,6 +368,7 @@
getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
hiddenView.transformTo(shownView, 0.0f);
mVisibleType = visibleType;
+ updateBackgroundColor(true /* animate */);
}
if (mTransformationStartVisibleType != UNDEFINED
&& mVisibleType != mTransformationStartVisibleType) {
@@ -380,11 +378,29 @@
float transformationAmount = calculateTransformationAmount();
shownView.transformFrom(hiddenView, transformationAmount);
hiddenView.transformTo(shownView, transformationAmount);
+ updateBackgroundTransformation(transformationAmount);
} else {
updateViewVisibilities(visibleType);
+ updateBackgroundColor(false);
}
}
+ private void updateBackgroundTransformation(float transformationAmount) {
+ int endColor = getBackgroundColor(mVisibleType);
+ int startColor = getBackgroundColor(mTransformationStartVisibleType);
+ if (endColor != startColor) {
+ if (startColor == 0) {
+ startColor = mContainingNotification.getBackgroundColorWithoutTint();
+ }
+ if (endColor == 0) {
+ endColor = mContainingNotification.getBackgroundColorWithoutTint();
+ }
+ endColor = NotificationUtils.interpolateColors(startColor, endColor,
+ transformationAmount);
+ }
+ mContainingNotification.setContentBackground(endColor, false, this);
+ }
+
private float calculateTransformationAmount() {
int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
int endHeight = getViewForVisibleType(mVisibleType).getHeight();
@@ -461,9 +477,24 @@
updateViewVisibilities(visibleType);
}
mVisibleType = visibleType;
+ updateBackgroundColor(animate);
}
}
+ public void updateBackgroundColor(boolean animate) {
+ int customBackgroundColor = getBackgroundColor(mVisibleType);
+ mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
+ }
+
+ private int getBackgroundColor(int visibleType) {
+ NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
+ int customBackgroundColor = 0;
+ if (currentVisibleWrapper != null) {
+ customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
+ }
+ return customBackgroundColor;
+ }
+
private void updateViewVisibilities(int visibleType) {
boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
mContractedWrapper.setVisible(contractedVisible);
@@ -484,12 +515,18 @@
private void animateToVisibleType(int visibleType) {
final TransformableView shownView = getTransformableViewForVisibleType(visibleType);
final TransformableView hiddenView = getTransformableViewForVisibleType(mVisibleType);
+ if (shownView == hiddenView) {
+ shownView.setVisible(true);
+ return;
+ }
shownView.transformFrom(hiddenView);
getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
hiddenView.transformTo(shownView, new Runnable() {
@Override
public void run() {
- hiddenView.setVisible(false);
+ if (hiddenView != getTransformableViewForVisibleType(mVisibleType)) {
+ hiddenView.setVisible(false);
+ }
}
});
}
@@ -528,8 +565,8 @@
}
}
- private NotificationViewWrapper getCurrentVisibleWrapper() {
- switch (mVisibleType) {
+ private NotificationViewWrapper getVisibleWrapper(int visibleType) {
+ switch (visibleType) {
case VISIBLE_TYPE_EXPANDED:
return mExpandedWrapper;
case VISIBLE_TYPE_HEADSUP:
@@ -547,9 +584,12 @@
private int calculateVisibleType() {
if (mUserExpanding) {
int height = !mIsChildInGroup || isGroupExpanded()
- || mContainingNotification.isExpanded()
+ || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
? mContainingNotification.getMaxContentHeight()
: mContainingNotification.getShowingLayout().getMinHeight();
+ if (height == 0) {
+ height = mContentHeight;
+ }
int expandedVisualType = getVisualTypeForHeight(height);
int collapsedVisualType = getVisualTypeForHeight(
mContainingNotification.getMinExpandHeight());
@@ -557,7 +597,12 @@
? expandedVisualType
: collapsedVisualType;
}
- int viewHeight = Math.min(mContentHeight, mContainingNotification.getIntrinsicHeight());
+ int intrinsicHeight = mContainingNotification.getIntrinsicHeight();
+ int viewHeight = mContentHeight;
+ if (intrinsicHeight != 0) {
+ // the intrinsicHeight might be 0 because it was just reset.
+ viewHeight = Math.min(mContentHeight, intrinsicHeight);
+ }
return getVisualTypeForHeight(viewHeight);
}
@@ -578,7 +623,8 @@
}
} else {
if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
- && (!mIsChildInGroup || !mContainingNotification.isExpanded()))) {
+ && (!mIsChildInGroup
+ || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
return VISIBLE_TYPE_CONTRACTED;
} else {
return VISIBLE_TYPE_EXPANDED;
@@ -595,7 +641,6 @@
return;
}
mDark = dark;
- dark = dark && !mShowingLegacyBackground;
if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
mContractedWrapper.setDark(dark, fade, delay);
}
@@ -626,6 +671,19 @@
public void setShowingLegacyBackground(boolean showing) {
mShowingLegacyBackground = showing;
+ updateShowingLegacyBackground();
+ }
+
+ private void updateShowingLegacyBackground() {
+ if (mContractedChild != null) {
+ mContractedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
+ }
+ if (mExpandedChild != null) {
+ mExpandedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
+ }
+ if (mHeadsUpChild != null) {
+ mHeadsUpWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
+ }
}
public void setIsChildInGroup(boolean isChildInGroup) {
@@ -638,7 +696,6 @@
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
updateSingleLineView();
applyRemoteInput(entry);
- selectLayout(false /* animate */, true /* force */);
if (mContractedChild != null) {
mContractedWrapper.notifyContentUpdated(entry.notification);
}
@@ -648,6 +705,8 @@
if (mHeadsUpChild != null) {
mHeadsUpWrapper.notifyContentUpdated(entry.notification);
}
+ updateShowingLegacyBackground();
+ selectLayout(false /* animate */, true /* force */);
setDark(mDark, false /* animate */, 0 /* delay */);
}
@@ -768,7 +827,7 @@
}
public NotificationHeaderView getVisibleNotificationHeader() {
- NotificationViewWrapper wrapper = getCurrentVisibleWrapper();
+ NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
return wrapper == null ? null : wrapper.getNotificationHeader();
}
@@ -796,6 +855,7 @@
mTransformationStartVisibleType = UNDEFINED;
mVisibleType = calculateVisibleType();
updateViewVisibilities(mVisibleType);
+ updateBackgroundColor(false);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
index 375459f..fcc48bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
@@ -29,11 +29,18 @@
public class NotificationSettingsIconRow extends FrameLayout implements View.OnClickListener {
+ private static final int GEAR_ALPHA_ANIM_DURATION = 200;
+
public interface SettingsIconRowListener {
/**
* Called when the gear behind a notification is touched.
*/
public void onGearTouched(ExpandableNotificationRow row, int x, int y);
+
+ /**
+ * Called when a notification is slid back over the gear.
+ */
+ public void onSettingsIconRowReset(NotificationSettingsIconRow row);
}
private ExpandableNotificationRow mParent;
@@ -45,6 +52,8 @@
private boolean mSettingsFadedIn = false;
private boolean mAnimating = false;
private boolean mOnLeft = true;
+ private boolean mDismissing = false;
+ private boolean mSnapping = false;
private int[] mGearLocation = new int[2];
private int[] mParentLocation = new int[2];
@@ -78,8 +87,14 @@
public void resetState() {
setGearAlpha(0f);
+ mSettingsFadedIn = false;
mAnimating = false;
+ mSnapping = false;
+ mDismissing = false;
setIconLocation(true /* on left */);
+ if (mListener != null) {
+ mListener.onSettingsIconRowReset(this);
+ }
}
public void setGearListener(SettingsIconRowListener listener) {
@@ -94,20 +109,24 @@
return mParent;
}
- private void setGearAlpha(float alpha) {
+ public void setGearAlpha(float alpha) {
if (alpha == 0) {
mSettingsFadedIn = false; // Can fade in again once it's gone.
setVisibility(View.INVISIBLE);
} else {
- if (alpha == 1) {
- mSettingsFadedIn = true;
- }
setVisibility(View.VISIBLE);
}
mGearIcon.setAlpha(alpha);
}
/**
+ * Returns whether the icon is on the left side of the view or not.
+ */
+ public boolean isIconOnLeft() {
+ return mOnLeft;
+ }
+
+ /**
* Returns the horizontal space in pixels required to display the gear behind a notification.
*/
public float getSpaceForGear() {
@@ -119,7 +138,7 @@
* if entire view is visible.
*/
public boolean isVisible() {
- return mSettingsFadedIn;
+ return mGearIcon.getAlpha() > 0;
}
public void cancelFadeAnimator() {
@@ -129,16 +148,18 @@
}
public void updateSettingsIcons(final float transX, final float size) {
- if (mAnimating || (mGearIcon.getAlpha() == 0)) {
- // Don't adjust when animating or settings aren't visible
+ if (mAnimating || !mSettingsFadedIn) {
+ // Don't adjust when animating, or if the gear hasn't been shown yet.
return;
}
- setIconLocation(transX > 0 /* fromLeft */);
+
final float fadeThreshold = size * 0.3f;
final float absTrans = Math.abs(transX);
float desiredAlpha = 0;
- if (absTrans <= fadeThreshold) {
+ if (absTrans == 0) {
+ desiredAlpha = 0;
+ } else if (absTrans <= fadeThreshold) {
desiredAlpha = 1;
} else {
desiredAlpha = 1 - ((absTrans - fadeThreshold) / (size - fadeThreshold));
@@ -148,6 +169,12 @@
public void fadeInSettings(final boolean fromLeft, final float transX,
final float notiThreshold) {
+ if (mDismissing || mAnimating) {
+ return;
+ }
+ if (isIconLocationChange(transX)) {
+ setGearAlpha(0f);
+ }
setIconLocation(transX > 0 /* fromLeft */);
mFadeAnimator = ValueAnimator.ofFloat(mGearIcon.getAlpha(), 1);
mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -164,40 +191,53 @@
});
mFadeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationCancel(Animator animation) {
- super.onAnimationCancel(animation);
- mAnimating = false;
- mSettingsFadedIn = false;
- }
-
- @Override
public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
mAnimating = true;
}
@Override
+ public void onAnimationCancel(Animator animation) {
+ // TODO should animate back to 0f from current alpha
+ mGearIcon.setAlpha(0f);
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
mAnimating = false;
- mSettingsFadedIn = true;
+ mSettingsFadedIn = mGearIcon.getAlpha() == 1;
}
});
mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
- mFadeAnimator.setDuration(200);
+ mFadeAnimator.setDuration(GEAR_ALPHA_ANIM_DURATION);
mFadeAnimator.start();
}
- private void setIconLocation(boolean onLeft) {
- if (onLeft == mOnLeft) {
+ public void setIconLocation(boolean onLeft) {
+ if (onLeft == mOnLeft || mSnapping) {
// Same side? Do nothing.
return;
}
-
setTranslationX(onLeft ? 0 : (mParent.getWidth() - mHorizSpaceForGear));
mOnLeft = onLeft;
}
+ public boolean isIconLocationChange(float translation) {
+ boolean onLeft = translation > mGearIcon.getPaddingStart();
+ boolean onRight = translation < -mGearIcon.getPaddingStart();
+ if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) {
+ return true;
+ }
+ return false;
+ }
+
+ public void setDismissing() {
+ mDismissing = true;
+ }
+
+ public void setSnapping(boolean snapping) {
+ mSnapping = snapping;
+ }
+
@Override
public void onClick(View v) {
if (v.getId() == R.id.gear_icon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
index 7e7fc3a..c0148c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
@@ -41,4 +41,20 @@
* Status bar is locked and shows the full screen user switcher.
*/
public static final int FULLSCREEN_USER_SWITCHER = 3;
+
+
+ public static String toShortString(int x) {
+ switch (x) {
+ case SHADE:
+ return "SHD";
+ case SHADE_LOCKED:
+ return "SHD_LCK";
+ case KEYGUARD:
+ return "KGRD";
+ case FULLSCREEN_USER_SWITCHER:
+ return "FS_USRSW";
+ default:
+ return "bad_value_" + x;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index bf05d1d..f75f3574 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -76,26 +76,26 @@
});
mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR);
mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- if (endRunnable != null) {
- mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
- public boolean mCancelled;
+ mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() {
+ public boolean mCancelled;
- @Override
- public void onAnimationEnd(Animator animation) {
- endRunnable.run();
- if (!mCancelled) {
- setVisible(false);
- } else {
- abortTransformations();
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ if (endRunnable != null) {
+ endRunnable.run();
}
+ setVisible(false);
+ } else {
+ abortTransformations();
}
+ }
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
- });
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+ });
mViewTransformationAnimation.start();
}
@@ -188,6 +188,9 @@
@Override
public void setVisible(boolean visible) {
+ if (mViewTransformationAnimation != null) {
+ mViewTransformationAnimation.cancel();
+ }
for (Integer viewType : mTransformedViews.keySet()) {
TransformState ownState = getCurrentState(viewType);
if (ownState != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index 8a93c5b..bb43899 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -71,7 +71,6 @@
private List<CarNavigationButton> mNavButtons = new ArrayList<CarNavigationButton>();
private int mCurrentFacetIndex;
- private String mCurrentPackageName;
private SparseBooleanArray mFacetHasMultipleAppsCache = new SparseBooleanArray();
public CarNavigationBarController(Context context,
@@ -84,7 +83,6 @@
}
public void taskChanged(String packageName) {
- mCurrentPackageName = packageName;
// If the package name belongs to a filter, then highlight appropriate button in
// the navigation bar.
if (mFacetPackageMap.containsKey(packageName)) {
@@ -298,12 +296,6 @@
return;
}
- // Don't launch the lens picker if it's already running and the
- // user clicks the same facet
- if (packageName.equals(mCurrentPackageName) && index == mCurrentFacetIndex) {
- return;
- }
-
intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index));
intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index));
// The facet is identified by the index in which it was added to the nav bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 7f8f20f7..49e4ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -16,8 +16,19 @@
package com.android.systemui.statusbar.notification;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.Color;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+import android.support.v4.graphics.ColorUtils;
import android.view.View;
+import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
import com.android.systemui.statusbar.phone.NotificationPanelView;
@@ -27,6 +38,11 @@
public class NotificationCustomViewWrapper extends NotificationViewWrapper {
private final ViewInvertHelper mInvertHelper;
+ private final Paint mGreyPaint = new Paint();
+ private int mBackgroundColor = 0;
+ private static final int CUSTOM_BACKGROUND_TAG = R.id.custom_background_color;
+ private boolean mShouldInvertDark;
+ private boolean mShowingLegacyBackground;
protected NotificationCustomViewWrapper(View view) {
super(view);
@@ -39,10 +55,46 @@
return;
}
super.setDark(dark, fade, delay);
- if (fade) {
- mInvertHelper.fade(dark, delay);
+ if (!mShowingLegacyBackground && mShouldInvertDark) {
+ if (fade) {
+ mInvertHelper.fade(dark, delay);
+ } else {
+ mInvertHelper.update(dark);
+ }
} else {
- mInvertHelper.update(dark);
+ mView.setLayerType(dark ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, null);
+ if (fade) {
+ fadeGrayscale(dark, delay);
+ } else {
+ updateGrayscale(dark);
+ }
+ }
+ }
+
+ protected void fadeGrayscale(final boolean dark, long delay) {
+ startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateGrayscaleMatrix((float) animation.getAnimatedValue());
+ mGreyPaint.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ mView.setLayerPaint(mGreyPaint);
+ }
+ }, dark, delay, new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!dark) {
+ mView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ }
+ });
+ }
+
+ protected void updateGrayscale(boolean dark) {
+ if (dark) {
+ updateGrayscaleMatrix(1f);
+ mGreyPaint.setColorFilter(
+ new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ mView.setLayerPaint(mGreyPaint);
}
}
@@ -51,4 +103,35 @@
super.setVisible(visible);
mView.setAlpha(visible ? 1.0f : 0.0f);
}
+
+ @Override
+ public void notifyContentUpdated(StatusBarNotification notification) {
+ super.notifyContentUpdated(notification);
+ Drawable background = mView.getBackground();
+ mBackgroundColor = 0;
+ if (background instanceof ColorDrawable) {
+ mBackgroundColor = ((ColorDrawable) background).getColor();
+ mView.setBackground(null);
+ mView.setTag(CUSTOM_BACKGROUND_TAG, mBackgroundColor);
+ } else if (mView.getTag(CUSTOM_BACKGROUND_TAG) != null) {
+ mBackgroundColor = (int) mView.getTag(CUSTOM_BACKGROUND_TAG);
+ }
+ mShouldInvertDark = mBackgroundColor == 0 || isColorLight(mBackgroundColor);
+ }
+
+ private boolean isColorLight(int backgroundColor) {
+ return Color.alpha(backgroundColor) == 0
+ || ColorUtils.calculateLuminance(backgroundColor) > 0.5;
+ }
+
+ @Override
+ public int getCustomBackgroundColor() {
+ return mBackgroundColor;
+ }
+
+ @Override
+ public void setShowingLegacyBackground(boolean showing) {
+ super.setShowingLegacyBackground(showing);
+ mShowingLegacyBackground = showing;
+ }
}
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 842bd22c..b201d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -47,7 +47,6 @@
*/
public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
- private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
0, PorterDuff.Mode.SRC_ATOP);
private final int mIconDarkAlpha;
@@ -178,21 +177,6 @@
}
}
- protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
- boolean dark, long delay, Animator.AnimatorListener listener) {
- float startIntensity = dark ? 0f : 1f;
- float endIntensity = dark ? 1f : 0f;
- ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
- animator.addUpdateListener(updateListener);
- animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
- animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- animator.setStartDelay(delay);
- if (listener != null) {
- animator.addListener(listener);
- }
- animator.start();
- }
-
private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
@Override
@@ -264,10 +248,6 @@
mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
}
- private void updateGrayscaleMatrix(float intensity) {
- mGrayscaleColorMatrix.setSaturation(1 - intensity);
- }
-
private static int interpolateColor(int source, int target, float t) {
int aSource = Color.alpha(source);
int rSource = Color.red(source);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 4738657..6ef61ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification;
+import android.graphics.Color;
import android.widget.ImageView;
import com.android.internal.util.NotificationColorUtil;
@@ -38,4 +39,12 @@
public static float interpolate(float start, float end, float amount) {
return start * (1.0f - amount) + end * amount;
}
+
+ public static int interpolateColors(int startColor, int endColor, float amount) {
+ return Color.argb(
+ (int) interpolate(Color.alpha(startColor), Color.alpha(endColor), amount),
+ (int) interpolate(Color.red(startColor), Color.red(endColor), amount),
+ (int) interpolate(Color.green(startColor), Color.green(endColor), amount),
+ (int) interpolate(Color.blue(startColor), Color.blue(endColor), amount));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 0df0d26..ebff69d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -16,13 +16,19 @@
package com.android.systemui.statusbar.notification;
+import android.animation.Animator;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.ColorMatrix;
import android.service.notification.StatusBarNotification;
import android.view.NotificationHeaderView;
import android.view.View;
+import com.android.systemui.Interpolators;
import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.NotificationContentView;
import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Wraps the actual notification content view; used to implement behaviors which are different for
@@ -30,6 +36,7 @@
*/
public abstract class NotificationViewWrapper implements TransformableView {
+ protected final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
protected final View mView;
protected boolean mDark;
protected boolean mDarkInitialized = false;
@@ -75,6 +82,26 @@
mDarkInitialized = false;
};
+
+ protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+ boolean dark, long delay, Animator.AnimatorListener listener) {
+ float startIntensity = dark ? 0f : 1f;
+ float endIntensity = dark ? 1f : 0f;
+ ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+ animator.addUpdateListener(updateListener);
+ animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ animator.setStartDelay(delay);
+ if (listener != null) {
+ animator.addListener(listener);
+ }
+ animator.start();
+ }
+
+ protected void updateGrayscaleMatrix(float intensity) {
+ mGrayscaleColorMatrix.setSaturation(1 - intensity);
+ }
+
/**
* Update the appearance of the expand button.
*
@@ -122,4 +149,11 @@
mView.animate().cancel();
mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
+
+ public int getCustomBackgroundColor() {
+ return 0;
+ }
+
+ public void setShowingLegacyBackground(boolean showing) {
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TextViewTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TextViewTransformState.java
index d3393b3..20dbc4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TextViewTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TextViewTransformState.java
@@ -46,7 +46,8 @@
if(TextUtils.equals(otherTvs.mText.getText(), mText.getText())) {
int ownEllipsized = getEllipsisCount();
int otherEllipsized = otherTvs.getEllipsisCount();
- return ownEllipsized == otherEllipsized;
+ return ownEllipsized == otherEllipsized
+ && mText.getHeight() == otherTvs.mText.getHeight();
}
}
return super.sameAs(otherState);
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 5b4a3f0..65e7973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -105,7 +105,6 @@
if (mSelectedUser != null && mSelectedUser.getIdentifier() != mCurrentUserId) {
// When selected user is different from the current user, show the selected
// user's static wallpaper.
- mWallpaperManager.forgetLoadedWallpaper();
mCache = mWallpaperManager.getBitmapAsUser(mSelectedUser.getIdentifier());
} else {
// When there is no selected user, or it's same as the current user, show the
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 88b8afa..e5e3caf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -65,7 +65,7 @@
import java.util.List;
public class NotificationPanelView extends PanelView implements
- ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
+ ExpandableView.OnHeightChangedListener,
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
HeadsUpManager.OnHeadsUpChangedListener {
@@ -92,6 +92,7 @@
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private KeyguardStatusBarView mKeyguardStatusBar;
protected QSContainer mQsContainer;
+ private DensityContainer mQsDensityContainer;
private KeyguardStatusView mKeyguardStatusView;
private TextView mClockView;
private View mReserveNotificationSpace;
@@ -217,11 +218,12 @@
super.onFinishInflate();
mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view);
- DensityContainer container = (DensityContainer) findViewById(R.id.qs_density_container);
- container.addInflateListener(new InflateListener() {
+ mQsDensityContainer = (DensityContainer) findViewById(R.id.qs_density_container);
+ mQsDensityContainer.addInflateListener(new InflateListener() {
@Override
public void onInflated(View v) {
mQsContainer = (QSContainer) v.findViewById(R.id.quick_settings_container);
+ mQsContainer.setPanelView(NotificationPanelView.this);
mQsContainer.getHeader().setOnClickListener(NotificationPanelView.this);
}
});
@@ -247,7 +249,7 @@
final int height = bottom - top;
final int oldHeight = oldBottom - oldTop;
if (height != oldHeight) {
- onScrollChanged();
+ onQsHeightChanged();
}
}
});
@@ -275,11 +277,12 @@
public void updateResources() {
int panelWidth = getResources().getDimensionPixelSize(R.dimen.notification_panel_width);
int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsContainer.getLayoutParams();
+ FrameLayout.LayoutParams lp =
+ (FrameLayout.LayoutParams) mQsDensityContainer.getLayoutParams();
if (lp.width != panelWidth) {
lp.width = panelWidth;
lp.gravity = panelGravity;
- mQsContainer.setLayoutParams(lp);
+ mQsDensityContainer.setLayoutParams(lp);
mQsContainer.post(mUpdateHeader);
}
@@ -547,7 +550,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (mBlockTouches) {
+ if (mBlockTouches || mQsContainer.isCustomizing()) {
return false;
}
initDownStates(event);
@@ -707,7 +710,7 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (mBlockTouches) {
+ if (mBlockTouches || mQsContainer.isCustomizing()) {
return false;
}
initDownStates(event);
@@ -906,18 +909,6 @@
}
@Override
- public void onOverscrolled(float lastTouchX, float lastTouchY, int amount) {
- if (mIntercepting && shouldQuickSettingsIntercept(lastTouchX, lastTouchY,
- -1 /* yDiff: Not relevant here */)) {
- mQsTracking = true;
- onQsExpansionStarted(amount);
- mInitialHeightOnTouch = mQsExpansionHeight;
- mInitialTouchY = mLastTouchY;
- mInitialTouchX = mLastTouchX;
- }
- }
-
- @Override
public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
cancelQsAnimation();
if (!mQsExpansionEnabled) {
@@ -1719,9 +1710,10 @@
public void onReset(ExpandableView view) {
}
- @Override
- public void onScrollChanged() {
+ public void onQsHeightChanged() {
+ mQsMaxExpansionHeight = mQsContainer.getDesiredHeight();
if (mQsExpanded) {
+ mQsExpansionHeight = mQsMaxExpansionHeight;
requestScrollerTopPaddingUpdate(false /* animate */);
requestPanelHeightUpdate();
}
@@ -2219,7 +2211,7 @@
protected void setVerticalPanelTranslation(float translation) {
mNotificationStackScroller.setTranslationX(translation);
- mQsContainer.setTranslationX(translation);
+ mQsDensityContainer.setTranslationX(translation);
}
protected void updateStackHeight(float stackHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 6d90e5c..f0df706 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -24,21 +24,24 @@
import android.view.ViewStub;
import android.view.WindowInsets;
import android.widget.FrameLayout;
-
+import com.android.systemui.DensityContainer;
import com.android.systemui.R;
+import com.android.systemui.qs.QSContainer;
+import com.android.systemui.qs.customize.QSCustomizer;
/**
* The container with notification stack scroller and quick settings inside.
*/
public class NotificationsQuickSettingsContainer extends FrameLayout
- implements ViewStub.OnInflateListener {
+ implements ViewStub.OnInflateListener, DensityContainer.InflateListener {
- private View mQsContainer;
+ private DensityContainer mQsContainer;
private View mUserSwitcher;
private View mStackScroller;
private View mKeyguardStatusBar;
private boolean mInflated;
private boolean mQsExpanded;
+ private boolean mCustomizerAnimating;
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -47,7 +50,8 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mQsContainer = findViewById(R.id.qs_density_container);
+ mQsContainer = (DensityContainer) findViewById(R.id.qs_density_container);
+ mQsContainer.addInflateListener(this);
mStackScroller = findViewById(R.id.notification_stack_scroller);
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher);
@@ -80,8 +84,9 @@
boolean userSwitcherVisible = mInflated && mUserSwitcher.getVisibility() == View.VISIBLE;
boolean statusBarVisible = mKeyguardStatusBar.getVisibility() == View.VISIBLE;
- View stackQsTop = mQsExpanded ? mStackScroller : mQsContainer;
- View stackQsBottom = !mQsExpanded ? mStackScroller : mQsContainer;
+ final boolean qsBottom = mQsExpanded && !mCustomizerAnimating;
+ View stackQsTop = qsBottom ? mStackScroller : mQsContainer;
+ View stackQsBottom = !qsBottom ? mStackScroller : mQsContainer;
// Invert the order of the scroll view and user switcher such that the notifications receive
// touches first but the panel gets drawn above.
if (child == mQsContainer) {
@@ -117,10 +122,23 @@
}
}
+ @Override
+ public void onInflated(View v) {
+ QSCustomizer customizer = ((QSContainer) v).getCustomizer();
+ customizer.setContainer(this);
+ }
+
public void setQsExpanded(boolean expanded) {
if (mQsExpanded != expanded) {
mQsExpanded = expanded;
invalidate();
}
}
+
+ public void setCustomizerAnimating(boolean isAnimating) {
+ if (mCustomizerAnimating != isAnimating) {
+ mCustomizerAnimating = isAnimating;
+ invalidate();
+ }
+ }
}
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 ef16388..bf58611 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -112,6 +112,7 @@
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;
import com.android.systemui.doze.DozeLog;
@@ -772,10 +773,21 @@
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, mScrimSrcModeEnabled);
+ scrimBehind, scrimInFront, headsUpScrim);
+ if (mScrimSrcModeEnabled) {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
+ mScrimController.setDrawBehindAsSrc(asSrc);
+ mStackScroller.setDrawBackgroundAsSrc(asSrc);
+ }
+ };
+ mBackdrop.setOnVisibilityChangedRunnable(runnable);
+ runnable.run();
+ }
mHeadsUpManager.addListener(mScrimController);
mStackScroller.setScrimController(mScrimController);
- mScrimController.setBackDropView(mBackdrop);
mStatusBarView.setScrimController(mScrimController);
mDozeScrimController = new DozeScrimController(mScrimController, context);
@@ -1000,7 +1012,7 @@
}
}
- private void clearAllNotifications() {
+ public void clearAllNotifications() {
// animate-swipe all dismissable notifications, then animate the shade closed
int numChildren = mStackScroller.getChildCount();
@@ -1137,9 +1149,10 @@
@Override
public boolean onLongClick(View v) {
- if (mRecents == null) {
+ if (mRecents == null || !ActivityManager.supportsMultiWindow()) {
return false;
}
+
boolean initiallyDocked = WindowManagerProxy.getInstance().getDockSide()
== WindowManager.DOCKED_INVALID;
boolean dockedAtEnd = toggleSplitScreenMode();
@@ -1160,13 +1173,8 @@
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
- Point realSize = new Point();
- mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
- .getRealSize(realSize);
- Rect initialBounds= new Rect(0, 0, realSize.x, realSize.y);
return mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
- ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
- initialBounds);
+ ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null);
} else {
EventBus.getDefault().send(new UndockingTaskEvent());
return false;
@@ -1466,6 +1474,9 @@
mStackScroller.removeView(remove);
mStackScroller.setChildTransferInProgress(false);
}
+
+ removeNotificationChildren();
+
for (int i=0; i<toShow.size(); i++) {
View v = toShow.get(i);
if (v.getParent() == null) {
@@ -1473,6 +1484,8 @@
}
}
+ addNotificationChildrenAndSort();
+
// So after all this work notifications still aren't sorted correctly.
// Let's do that now by advancing through toShow and mStackScroller in
// lock-step, making sure mStackScroller matches what we see in toShow.
@@ -1494,9 +1507,6 @@
}
- // lets handle the child notifications now
- updateNotificationShadeForChildren();
-
// clear the map again for the next usage
mTmpChildOrderMap.clear();
@@ -1522,34 +1532,7 @@
&& !ONLY_CORE_APPS);
}
- private void updateNotificationShadeForChildren() {
- // First let's remove all children which don't belong in the parents
- ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
- for (int i = 0; i < mStackScroller.getChildCount(); i++) {
- View view = mStackScroller.getChildAt(i);
- if (!(view instanceof ExpandableNotificationRow)) {
- // We don't care about non-notification views.
- continue;
- }
-
- ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
- List<ExpandableNotificationRow> children = parent.getNotificationChildren();
- List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
-
- if (children != null) {
- toRemove.clear();
- for (ExpandableNotificationRow childRow : children) {
- if (orderedChildren == null || !orderedChildren.contains(childRow)) {
- toRemove.add(childRow);
- }
- }
- for (ExpandableNotificationRow remove : toRemove) {
- parent.removeChildNotification(remove);
- mStackScroller.notifyGroupChildRemoved(remove);
- }
- }
- }
-
+ private void addNotificationChildrenAndSort() {
// Let's now add all notification children which are missing
boolean orderChanged = false;
for (int i = 0; i < mStackScroller.getChildCount(); i++) {
@@ -1580,6 +1563,39 @@
}
}
+ private void removeNotificationChildren() {
+ // First let's remove all children which don't belong in the parents
+ ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
+ for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+ View view = mStackScroller.getChildAt(i);
+ if (!(view instanceof ExpandableNotificationRow)) {
+ // We don't care about non-notification views.
+ continue;
+ }
+
+ ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+ List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+ List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+
+ if (children != null) {
+ toRemove.clear();
+ for (ExpandableNotificationRow childRow : children) {
+ if (orderedChildren == null || !orderedChildren.contains(childRow)) {
+ toRemove.add(childRow);
+ }
+ }
+ for (ExpandableNotificationRow remove : toRemove) {
+ parent.removeChildNotification(remove);
+ if (mNotificationData.get(remove.getStatusBarNotification().getKey()) == null) {
+ // We only want to add an animation if the view is completely removed
+ // otherwise it's just a transfer
+ mStackScroller.notifyGroupChildRemoved(remove);
+ }
+ }
+ }
+ }
+ }
+
@Override
public void addQsTile(ComponentName tile) {
mQSPanel.getHost().addTile(tile);
@@ -1871,10 +1887,16 @@
if (mBackdrop.getVisibility() != View.VISIBLE) {
mBackdrop.setVisibility(View.VISIBLE);
if (allowEnterAnimation) {
- mBackdrop.animate().alpha(1f);
+ mBackdrop.animate().alpha(1f).withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarWindowManager.setBackdropShowing(true);
+ }
+ });
} else {
mBackdrop.animate().cancel();
mBackdrop.setAlpha(1f);
+ mStatusBarWindowManager.setBackdropShowing(true);
}
metaDataChanged = true;
if (DEBUG_MEDIA) {
@@ -1931,7 +1953,10 @@
// We are unlocking directly - no animation!
mBackdrop.setVisibility(View.GONE);
+ mBackdropBack.setImageDrawable(null);
+ mStatusBarWindowManager.setBackdropShowing(false);
} else {
+ mStatusBarWindowManager.setBackdropShowing(false);
mBackdrop.animate()
// Never let the alpha become zero - otherwise the RenderNode
// won't draw anything and uninitialized memory will show through
@@ -1946,7 +1971,7 @@
public void run() {
mBackdrop.setVisibility(View.GONE);
mBackdropFront.animate().cancel();
- mBackdropBack.animate().cancel();
+ mBackdropBack.setImageDrawable(null);
mHandler.post(mHideBackdropFront);
}
});
@@ -2244,6 +2269,10 @@
mStatusBarWindowManager.setPanelExpanded(isExpanded);
}
+ public void onScreenTurnedOff() {
+ mFalsingManager.onScreenOff();
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -2947,18 +2976,15 @@
KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args);
}
+ FalsingManager.getInstance(mContext).dump(pw);
+ FalsingLog.dump(pw);
+
pw.println("SharedPreferences:");
for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
}
}
- private String hunStateToString(Entry entry) {
- if (entry == null) return "null";
- if (entry.notification == null) return "corrupt";
- return entry.notification.getPackageName();
- }
-
private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode=");
pw.println(BarTransitions.modeToString(transitions.getMode()));
@@ -3120,7 +3146,7 @@
}
};
- private void resetUserExpandedStates() {
+ public void resetUserExpandedStates() {
ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
final int notificationCount = activeNotifications.size();
for (int i = 0; i < notificationCount; i++) {
@@ -3554,7 +3580,7 @@
}
@Override
- protected boolean isPanelFullyCollapsed() {
+ public boolean isPanelFullyCollapsed() {
return mNotificationPanel.isFullyCollapsed();
}
@@ -4167,7 +4193,6 @@
mDeviceInteractive = false;
mWakeUpComingFromTouch = false;
mWakeUpTouchLocation = null;
- mFalsingManager.onScreenOff();
mStackScroller.setAnimationsEnabled(false);
updateVisibleToUser();
if (mLaunchCameraOnFinishedGoingToSleep) {
@@ -4474,7 +4499,20 @@
}
private void handlePulseWhileDozing(@NonNull PulseCallback callback, int reason) {
- mDozeScrimController.pulse(callback, reason);
+ mDozeScrimController.pulse(new PulseCallback() {
+
+ @Override
+ public void onPulseStarted() {
+ callback.onPulseStarted();
+ mStackScroller.setPulsing(true);
+ }
+
+ @Override
+ public void onPulseFinished() {
+ callback.onPulseFinished();
+ mStackScroller.setPulsing(false);
+ }
+ }, reason);
}
private void handleStopDozing() {
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 f894a22..5dcd393 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -114,6 +114,7 @@
private final ManagedProfileController mProfileController;
private final NextAlarmController mNextAlarmController;
private View mHeader;
+ private int mCurrentUser;
public QSTileHost(Context context, PhoneStatusBar statusBar,
BluetoothController bluetooth, LocationController location,
@@ -320,7 +321,8 @@
}
if (DEBUG) Log.d(TAG, "Recreating tiles");
final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
- if (tileSpecs.equals(mTileSpecs)) return;
+ int currentUser = ActivityManager.getCurrentUser();
+ if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
if (!tileSpecs.contains(tile.getKey())) {
if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
@@ -329,15 +331,16 @@
}
final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>();
for (String tileSpec : tileSpecs) {
- if (mTiles.containsKey(tileSpec)) {
- QSTile<?> tile = mTiles.get(tileSpec);
+ QSTile<?> tile = mTiles.get(tileSpec);
+ if (tile != null && (!(tile instanceof CustomTile)
+ || ((CustomTile) tile).getUser() == currentUser)) {
if (DEBUG) Log.d(TAG, "Adding " + tile);
tile.removeCallbacks();
newTiles.put(tileSpec, tile);
} else {
if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
try {
- QSTile<?> tile = createTile(tileSpec);
+ tile = createTile(tileSpec);
if (tile != null && tile.isAvailable()) {
tile.setTileSpec(tileSpec);
newTiles.put(tileSpec, tile);
@@ -347,6 +350,7 @@
}
}
}
+ mCurrentUser = currentUser;
mTileSpecs.clear();
mTileSpecs.addAll(tileSpecs);
mTiles.clear();
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 256cc6b..8f329c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -83,7 +83,6 @@
private float mDateScaleFactor;
private float mGearTranslation;
- private TouchAnimator mAnimator;
private TouchAnimator mSecondHalfAnimator;
private TouchAnimator mFirstHalfAnimator;
private TouchAnimator mDateSizeAnimator;
@@ -154,12 +153,7 @@
mDateScaleFactor = dateExpandedSize / dateCollapsedSize;
updateDateTimePosition();
- mAnimator = new TouchAnimator.Builder()
- .addFloat(mSettingsContainer, "translationY", -mGearTranslation, 0)
- .addFloat(mMultiUserSwitch, "translationY", -mGearTranslation, 0)
- .build();
mSecondHalfAnimator = new TouchAnimator.Builder()
- .addFloat(mSettingsButton, "rotation", -180, 0)
.addFloat(mAlarmStatus, "alpha", 0, 1)
.addFloat(mEmergencyOnly, "alpha", 0, 1)
.setStartDelay(.5f)
@@ -174,6 +168,9 @@
.setStartDelay(.36f)
.build();
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(mMultiUserSwitch, "alpha", 0, 1)
.setStartDelay(QSAnimator.EXPANDED_TILE_DELAY)
@@ -211,7 +208,6 @@
@Override
public void setExpansion(float headerExpansionFraction) {
mExpansionAmount = headerExpansionFraction;
- mAnimator.setPosition(headerExpansionFraction);
mSecondHalfAnimator.setPosition(headerExpansionFraction);
mFirstHalfAnimator.setPosition(headerExpansionFraction);
mDateSizeAnimator.setPosition(headerExpansionFraction);
@@ -296,8 +292,7 @@
mHost = host;
host.setHeaderView(this);
mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
- mHeaderQsPanel.setMaxTiles(5);
- mHeaderQsPanel.setHost(host);
+ mHeaderQsPanel.setHost(host, null /* No customization in header */);
setUserInfoController(host.getUserInfoController());
setBatteryController(host.getBatteryController());
setNextAlarmController(host.getNextAlarmController());
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 fe76ae7..3eda320 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -30,7 +30,6 @@
import android.view.animation.PathInterpolator;
import com.android.systemui.R;
-import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.ScrimView;
@@ -46,7 +45,6 @@
public static final long ANIMATION_DURATION = 220;
public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR
= new PathInterpolator(0f, 0, 0.7f, 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;
@@ -61,6 +59,10 @@
private final UnlockMethodCache mUnlockMethodCache;
private final View mHeadsUpScrim;
+ private float mScrimBehindAlpha = SCRIM_BEHIND_ALPHA;
+ private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
+ private float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
+
protected boolean mKeyguardShowing;
private float mFraction;
@@ -75,8 +77,6 @@
private long mAnimationDelay;
private Runnable mOnAnimationFinished;
private final Interpolator mInterpolator = new DecelerateInterpolator();
- private BackDropView mBackDropView;
- private boolean mScrimSrcEnabled;
private boolean mDozing;
private float mDozeInFrontAlpha;
private float mDozeBehindAlpha;
@@ -89,15 +89,14 @@
private boolean mForceHideScrims;
private boolean mSkipFirstFrame;
private boolean mDontAnimateBouncerChanges;
+ private boolean mKeyguardFadingOutInProgress;
- public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
- boolean scrimSrcEnabled) {
+ public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim) {
mScrimBehind = scrimBehind;
mScrimInFront = scrimInFront;
mHeadsUpScrim = headsUpScrim;
final Context context = scrimBehind.getContext();
mUnlockMethodCache = UnlockMethodCache.getInstance(context);
- mScrimSrcEnabled = scrimSrcEnabled;
updateHeadsUpScrim(false);
}
@@ -106,6 +105,19 @@
scheduleUpdate();
}
+ public void setShowScrimBehind(boolean show) {
+ if (show) {
+ mScrimBehindAlpha = SCRIM_BEHIND_ALPHA;
+ mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
+ mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
+ } else {
+ mScrimBehindAlpha = 0;
+ mScrimBehindAlphaKeyguard = 0;
+ mScrimBehindAlphaUnlocking = 0;
+ }
+ scheduleUpdate();
+ }
+
public void onTrackingStarted() {
mExpanding = true;
mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
@@ -234,7 +246,7 @@
fraction = (float) Math.pow(fraction, 0.8f);
behindFraction = (float) Math.pow(behindFraction, 0.8f);
setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA);
- setScrimBehindColor(behindFraction * SCRIM_BEHIND_ALPHA_KEYGUARD);
+ setScrimBehindColor(behindFraction * mScrimBehindAlphaKeyguard);
} else if (mBouncerShowing) {
setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA);
setScrimBehindColor(0f);
@@ -242,8 +254,8 @@
float fraction = Math.max(0, Math.min(mFraction, 1));
setScrimInFrontColor(0f);
setScrimBehindColor(fraction
- * (SCRIM_BEHIND_ALPHA_KEYGUARD - SCRIM_BEHIND_ALPHA_UNLOCKING)
- + SCRIM_BEHIND_ALPHA_UNLOCKING);
+ * (mScrimBehindAlphaKeyguard - mScrimBehindAlphaUnlocking)
+ + mScrimBehindAlphaUnlocking);
}
}
@@ -256,7 +268,7 @@
} else {
// woo, special effects
final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
- setScrimBehindColor(k * SCRIM_BEHIND_ALPHA);
+ setScrimBehindColor(k * mScrimBehindAlpha);
}
}
@@ -331,12 +343,16 @@
if (mOnAnimationFinished != null) {
mOnAnimationFinished.run();
mOnAnimationFinished = null;
+ mKeyguardFadingOutInProgress = false;
}
scrim.setTag(TAG_KEY_ANIM, null);
scrim.setTag(TAG_KEY_ANIM_TARGET, null);
}
});
anim.start();
+ if (mAnimateKeyguardFadingOut) {
+ mKeyguardFadingOutInProgress = true;
+ }
if (mSkipFirstFrame) {
anim.setCurrentPlayTime(16);
}
@@ -371,6 +387,7 @@
&& mOnAnimationFinished != null) {
mOnAnimationFinished.run();
mOnAnimationFinished = null;
+ mKeyguardFadingOutInProgress = false;
}
}
@@ -378,19 +395,7 @@
return scrim.getTag(TAG_KEY_ANIM) != null;
}
- public void setBackDropView(BackDropView backDropView) {
- mBackDropView = backDropView;
- mBackDropView.setOnVisibilityChangedRunnable(new Runnable() {
- @Override
- public void run() {
- updateScrimBehindDrawingMode();
- }
- });
- updateScrimBehindDrawingMode();
- }
-
- private void updateScrimBehindDrawingMode() {
- boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE && mScrimSrcEnabled;
+ public void setDrawBehindAsSrc(boolean asSrc) {
mScrimBehind.setDrawAsSrc(asSrc);
}
@@ -423,6 +428,10 @@
}
private void updateScrim(boolean animate, View scrim, float alpha, float currentAlpha) {
+ if (mKeyguardFadingOutInProgress) {
+ return;
+ }
+
ValueAnimator previousAnimator = StackStateAnimator.getChildTag(scrim,
TAG_KEY_ANIM);
float animEndValue = -1;
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 0e84f733..117e2b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -212,6 +212,7 @@
public void onScreenTurnedOff() {
mScreenTurnedOn = false;
+ mPhoneStatusBar.onScreenTurnedOff();
}
public void notifyDeviceWakeUpRequested() {
@@ -226,6 +227,10 @@
mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
}
+ public boolean isUnlockWithWallpaper() {
+ return mStatusBarWindowManager.isShowingWallpaper();
+ }
+
public void setOccluded(boolean occluded) {
if (occluded && !mOccluded && mShowing) {
if (mPhoneStatusBar.isInLaunchTransition()) {
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 fcaf050..ada7450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -100,12 +100,16 @@
private void applyKeyguardFlags(State state) {
if (state.keyguardShowing) {
- mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
mLpChanged.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
} else {
- mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
}
+
+ if (state.keyguardShowing && !state.backdropShowing) {
+ mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+ } else {
+ mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+ }
}
private void adjustScreenOrientation(State state) {
@@ -173,7 +177,7 @@
private void applyInputFeatures(State state) {
if (state.isKeyguardShowingAndNotOccluded()
&& state.statusBarState == StatusBarState.KEYGUARD
- && !state.qsExpanded) {
+ && !state.qsExpanded && !state.forceUserActivity) {
mLpChanged.inputFeatures |=
WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
} else {
@@ -255,6 +259,11 @@
apply(mCurrentState);
}
+ public void setBackdropShowing(boolean showing) {
+ mCurrentState.backdropShowing = showing;
+ apply(mCurrentState);
+ }
+
public void setKeyguardFadingAway(boolean keyguardFadingAway) {
mCurrentState.keyguardFadingAway = keyguardFadingAway;
apply(mCurrentState);
@@ -265,6 +274,11 @@
apply(mCurrentState);
}
+ public void setForceUserActivity(boolean forceUserActivity) {
+ mCurrentState.forceUserActivity = forceUserActivity;
+ apply(mCurrentState);
+ }
+
public void setHeadsUpShowing(boolean showing) {
mCurrentState.headsUpShowing = showing;
apply(mCurrentState);
@@ -318,6 +332,10 @@
pw.println(mCurrentState);
}
+ public boolean isShowingWallpaper() {
+ return !mCurrentState.backdropShowing;
+ }
+
private static class State {
boolean keyguardShowing;
boolean keyguardOccluded;
@@ -332,6 +350,8 @@
boolean forceStatusBarVisible;
boolean forceCollapsed;
boolean forceDozeBrightness;
+ boolean forceUserActivity;
+ boolean backdropShowing;
/**
* The {@link BaseStatusBar} state from the status bar.
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 c6659d1..29b0f4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -155,8 +155,21 @@
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mEntry.row.isChangingPosition()) {
+ if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
+ mEditText.requestFocus();
+ }
+ }
+ }
+
+ @Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+ if (mEntry.row.isChangingPosition()) {
+ return;
+ }
mController.removeRemoteInput(mEntry);
}
@@ -229,6 +242,9 @@
}
private void defocusIfNeeded() {
+ if (mDefocusListener.mEntry.row.isChangingPosition()) {
+ return;
+ }
if (isFocusable() && isEnabled()) {
setInnerFocusable(false);
if (mDefocusListener != null) {
@@ -248,9 +264,11 @@
}
@Override
- protected void onFocusLost() {
- super.onFocusLost();
- defocusIfNeeded();
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ if (!focused) {
+ defocusIfNeeded();
+ }
}
@Override
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 cedc3c7..ab44b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -315,8 +315,8 @@
public void logoutCurrentUser() {
int currentUser = ActivityManager.getCurrentUser();
if (currentUser != UserHandle.USER_SYSTEM) {
- switchToUserId(UserHandle.USER_SYSTEM);
- stopUserId(currentUser);
+ pauseRefreshUsers();
+ ActivityManager.logoutCurrentUser();
}
}
@@ -384,14 +384,6 @@
}
}
- private void stopUserId(int id) {
- try {
- ActivityManagerNative.getDefault().stopUser(id, /* force= */ false, null);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't stop user.", e);
- }
- }
-
private void showExitGuestDialog(int id) {
if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
mExitGuestDialog.cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 7955733..676ff2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -96,7 +96,8 @@
com.android.internal.R.dimen.notification_content_margin_top);
mNotificatonTopPadding = getResources().getDimensionPixelSize(
R.dimen.notification_children_container_top_padding);
- mCollapsedBottompadding = 11.5f * getResources().getDisplayMetrics().density;
+ mCollapsedBottompadding = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_bottom);
}
@Override
@@ -511,7 +512,7 @@
break;
}
ExpandableNotificationRow child = mChildren.get(i);
- float childHeight = child.isExpanded()
+ float childHeight = child.isExpanded(true /* allowOnKeyguard */)
? child.getMaxExpandHeight()
: child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
maxContentHeight += childHeight;
@@ -532,7 +533,7 @@
int childCount = mChildren.size();
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
- float childHeight = child.isExpanded()
+ float childHeight = child.isExpanded(true /* allowOnKeyguard */)
? child.getMaxExpandHeight()
: child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
float singleLineHeight = child.getShowingLayout().getMinHeight(
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 aa444f5..d9b78a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -18,7 +18,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeAnimator;
@@ -324,6 +323,8 @@
}
}
};
+ private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
+ private boolean mPulsing;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -359,7 +360,6 @@
mDebugPaint.setStyle(Paint.Style.STROKE);
}
mFalsingManager = FalsingManager.getInstance(context);
- mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
}
@Override
@@ -372,6 +372,11 @@
}
@Override
+ public void onSettingsIconRowReset(NotificationSettingsIconRow row) {
+ mSwipeHelper.setSnappedToGear(false);
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, mCurrentBounds.top, getWidth(), mCurrentBounds.bottom, mBackgroundPaint);
if (DEBUG) {
@@ -427,6 +432,11 @@
R.dimen.min_top_overscroll_to_qs);
}
+ public void setDrawBackgroundAsSrc(boolean asSrc) {
+ mBackgroundPaint.setXfermode(asSrc ? mSrcMode : null);
+ invalidate();
+ }
+
private void notifyHeightChangeListener(ExpandableView view) {
if (mOnHeightChangedListener != null) {
mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
@@ -712,11 +722,15 @@
mDragAnimPendingChildren.remove(animView);
}
- if (targetLeft == 0 && mCurrIconRow != null) {
- mCurrIconRow.resetState();
- mCurrIconRow = null;
- if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
- mGearExposedView = null;
+ if (mCurrIconRow != null) {
+ if (targetLeft == 0) {
+ mCurrIconRow.resetState();
+ mCurrIconRow = null;
+ if (mGearExposedView != null && mGearExposedView == mTranslatingParentView) {
+ mGearExposedView = null;
+ }
+ } else {
+ mSwipeHelper.setSnappedToGear(true);
}
}
}
@@ -774,12 +788,15 @@
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
ExpandableNotificationRow parent = row.getNotificationParent();
- if (mGearExposedView != null && parent != null
- && parent.areChildrenExpanded() && mGearExposedView == parent) {
+ if (parent != null && parent.areChildrenExpanded()
+ && (mGearExposedView == parent
+ || (parent.getNotificationChildren().size() == 1
+ && parent.isClearable()))) {
// In this case the group is expanded and showing the gear for the
// group, further interaction should apply to the group, not any
- // child notifications so we use the parent of the child.
- child = row.getNotificationParent();
+ // child notifications so we use the parent of the child. We also do the same
+ // if we only have a single child.
+ child = parent;
}
}
return child;
@@ -857,7 +874,7 @@
public boolean canChildBeExpanded(View v) {
return v instanceof ExpandableNotificationRow
&& ((ExpandableNotificationRow) v).isExpandable()
- && !((ExpandableNotificationRow) v).isHeadsUp();
+ && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
}
public void setUserExpandedChild(View v, boolean userExpanded) {
@@ -2187,7 +2204,7 @@
}
private void updateNotificationAnimationStates() {
- boolean running = mAnimationsEnabled;
+ boolean running = mAnimationsEnabled || mPulsing;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
@@ -2197,7 +2214,8 @@
}
private void updateAnimationState(View child) {
- updateAnimationState(mAnimationsEnabled && (mIsExpanded || isPinnedHeadsUp(child)), child);
+ updateAnimationState((mAnimationsEnabled || mPulsing)
+ && (mIsExpanded || isPinnedHeadsUp(child)), child);
}
@@ -2243,8 +2261,10 @@
int currentIndex = indexOfChild(child);
if (child != null && child.getParent() == this && currentIndex != newIndex) {
mChangePositionInProgress = true;
+ ((ExpandableView)child).setChangingPosition(true);
removeView(child);
addView(child, newIndex);
+ ((ExpandableView)child).setChangingPosition(false);
mChangePositionInProgress = false;
if (mIsExpanded && mAnimationsEnabled && child.getVisibility() != View.GONE) {
mChildrenChangingPositions.add(child);
@@ -2645,6 +2665,7 @@
mIsExpansionChanging = false;
if (!mIsExpanded) {
mOwnScrollY = 0;
+ mPhoneStatusBar.resetUserExpandedStates();
// lets make sure nothing is in the overlay anymore
getOverlay().clear();
@@ -2718,13 +2739,13 @@
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isUserLocked() && row != getFirstChildNotGone()) {
+ if (row.isSummaryWithChildren()) {
+ return;
+ }
// We are actually expanding this view
- float endPosition;
+ float endPosition = row.getTranslationY() + row.getActualHeight();
if (row.isChildInGroup()) {
- ExpandableNotificationRow parent = row.getNotificationParent();
- endPosition = parent.getTranslationY() + parent.getActualHeight();
- } else {
- endPosition = row.getTranslationY() + row.getActualHeight();
+ endPosition += row.getNotificationParent().getTranslationY();
}
int stackEnd = mMaxLayoutHeight - mBottomStackPeekSize -
mBottomStackSlowDownHeight + (int) mStackTranslation;
@@ -3315,6 +3336,11 @@
return mIsExpanded;
}
+ public void setPulsing(boolean pulsing) {
+ mPulsing = pulsing;
+ updateNotificationAnimationStates();
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
@@ -3362,15 +3388,11 @@
}
private class NotificationSwipeHelper extends SwipeHelper {
- private static final int MOVE_STATE_LEFT = -1;
- private static final int MOVE_STATE_UNDEFINED = 0;
- private static final int MOVE_STATE_RIGHT = 1;
-
private static final long GEAR_SHOW_DELAY = 60;
-
private CheckForDrag mCheckForDrag;
private Handler mHandler;
- private int mMoveState = MOVE_STATE_UNDEFINED;
+ private boolean mGearSnappedTo;
+ private boolean mGearSnappedOnLeft;
public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
super(swipeDirection, callback, context);
@@ -3383,6 +3405,10 @@
mTranslatingParentView = currView;
// Reset check for drag gesture
+ cancelCheckForDrag();
+ if (mCurrIconRow != null) {
+ mCurrIconRow.setSnapping(false);
+ }
mCheckForDrag = null;
mCurrIconRow = null;
@@ -3394,17 +3420,32 @@
mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow();
mCurrIconRow.setGearListener(NotificationStackScrollLayout.this);
}
- mMoveState = MOVE_STATE_UNDEFINED;
}
@Override
public void onMoveUpdate(View view, float translation, float delta) {
- final int newMoveState = (delta < 0) ? MOVE_STATE_RIGHT : MOVE_STATE_LEFT;
- if (mMoveState != MOVE_STATE_UNDEFINED && mMoveState != newMoveState) {
- // Changed directions, make sure we check for drag again.
- mCheckForDrag = null;
+ if (mCurrIconRow != null) {
+ mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping.
+
+ // If the gear is visible and the movement is towards it it's not a location change.
+ boolean onLeft = mGearSnappedTo ? mGearSnappedOnLeft : mCurrIconRow.isIconOnLeft();
+ boolean locationChange = isTowardsGear(translation, onLeft)
+ ? false : mCurrIconRow.isIconLocationChange(translation);
+ if (locationChange) {
+ // Don't consider it "snapped" if location has changed.
+ setSnappedToGear(false);
+
+ // Changed directions, make sure we check to fade in icon again.
+ if (!mHandler.hasCallbacks(mCheckForDrag)) {
+ // No check scheduled, set null to schedule a new one.
+ mCheckForDrag = null;
+ } else {
+ // Check scheduled, reset alpha and update location; check will fade it in
+ mCurrIconRow.setGearAlpha(0f);
+ mCurrIconRow.setIconLocation(translation > 0 /* onLeft */);
+ }
+ }
}
- mMoveState = newMoveState;
final boolean gutsExposed = (view instanceof ExpandableNotificationRow)
&& ((ExpandableNotificationRow) view).areGutsExposed();
@@ -3417,35 +3458,101 @@
@Override
public void dismissChild(final View view, float velocity) {
- cancelCheckForDrag();
super.dismissChild(view, velocity);
+ cancelCheckForDrag();
+ setSnappedToGear(false);
}
@Override
public void snapChild(final View animView, final float targetLeft, float velocity) {
+ super.snapChild(animView, targetLeft, velocity);
+ onDragCancelled(animView);
+ if (targetLeft == 0) {
+ cancelCheckForDrag();
+ setSnappedToGear(false);
+ }
+ }
+
+
+ @Override
+ public boolean handleUpEvent(MotionEvent ev, View animView, float velocity,
+ float translation) {
+ if (mCurrIconRow == null) {
+ cancelCheckForDrag();
+ return false; // Let SwipeHelper handle it.
+ }
+
+ boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isIconOnLeft());
+ boolean gestureFastEnough = Math.abs(velocity) > getEscapeVelocity();
+
+ if (mGearSnappedTo && mCurrIconRow.isVisible()) {
+ if (mGearSnappedOnLeft == mCurrIconRow.isIconOnLeft()) {
+ boolean coveringGear =
+ Math.abs(getTranslation(animView)) <= getSpaceForGear(animView) * 0.6f;
+ if (gestureTowardsGear || coveringGear) {
+ // Gesture is towards or covering the gear
+ snapChild(animView, 0 /* leftTarget */, velocity);
+ } else if (isDismissGesture(ev)) {
+ // Gesture is a dismiss that's not towards the gear
+ dismissChild(animView, swipedFastEnough() ? velocity : 0f);
+ } else {
+ // Didn't move enough to dismiss or cover, snap to the gear
+ snapToGear(animView, velocity);
+ }
+ } else if ((!gestureFastEnough && swipedEnoughToShowGear(animView))
+ || (gestureTowardsGear && !swipedFarEnough())) {
+ // The gear has been snapped to previously, however, the gear is now on the
+ // other side. If gesture is towards gear and not too far snap to the gear.
+ snapToGear(animView, velocity);
+ } else {
+ dismissOrSnapBack(animView, velocity, ev);
+ }
+ } else if ((!gestureFastEnough && swipedEnoughToShowGear(animView))
+ || gestureTowardsGear) {
+ // Gear has not been snapped to previously and this is gear revealing gesture
+ snapToGear(animView, velocity);
+ } else {
+ dismissOrSnapBack(animView, velocity, ev);
+ }
+ return true;
+ }
+
+ private void dismissOrSnapBack(View animView, float velocity, MotionEvent ev) {
+ if (isDismissGesture(ev)) {
+ dismissChild(animView, swipedFastEnough() ? velocity : 0f);
+ } else {
+ snapChild(animView, 0 /* leftTarget */, velocity);
+ }
+ }
+
+ private void snapToGear(View animView, float velocity) {
+ final float snapBackThreshold = getSpaceForGear(animView);
+ final float target = mCurrIconRow.isIconOnLeft() ? snapBackThreshold
+ : -snapBackThreshold;
+ mGearExposedView = mTranslatingParentView;
+ if (mGearDisplayedListener != null
+ && (animView instanceof ExpandableNotificationRow)) {
+ mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView);
+ }
+ if (mCurrIconRow != null) {
+ mCurrIconRow.setSnapping(true);
+ setSnappedToGear(true);
+ }
+ onDragCancelled(animView);
+ super.snapChild(animView, target, velocity);
+ }
+
+ private boolean swipedEnoughToShowGear(View animView) {
final float snapBackThreshold = getSpaceForGear(animView);
final float translation = getTranslation(animView);
final boolean fromLeft = translation > 0;
final float absTrans = Math.abs(translation);
final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
- boolean pastGear = (fromLeft && translation >= snapBackThreshold * 0.4f
- && translation <= notiThreshold) ||
- (!fromLeft && absTrans >= snapBackThreshold * 0.4f
- && absTrans <= notiThreshold);
-
- if (pastGear && !isPinnedHeadsUp(animView)
- && (animView instanceof ExpandableNotificationRow)) {
- // bouncity
- final float target = fromLeft ? snapBackThreshold : -snapBackThreshold;
- mGearExposedView = mTranslatingParentView;
- if (mGearDisplayedListener != null) {
- mGearDisplayedListener.onGearDisplayed((ExpandableNotificationRow) animView);
- }
- super.snapChild(animView, target, velocity);
- } else {
- super.snapChild(animView, 0, velocity);
- }
+ // If the notification can't be dismissed then how far it can move is
+ // restricted -- reduce the distance it needs to move in this case.
+ final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f;
+ return absTrans >= snapBackThreshold * 0.4f && absTrans <= notiThreshold;
}
@Override
@@ -3482,6 +3589,25 @@
}
/**
+ * Returns whether the gesture is towards the gear location or not.
+ */
+ private boolean isTowardsGear(float velocity, boolean onLeft) {
+ if (mCurrIconRow == null) {
+ return false;
+ }
+ return mCurrIconRow.isVisible()
+ && ((onLeft && velocity <= 0) || (!onLeft && velocity >= 0));
+ }
+
+ /**
+ * Indicates the the gear has been snapped to.
+ */
+ private void setSnappedToGear(boolean snapped) {
+ mGearSnappedOnLeft = (mCurrIconRow != null) ? mCurrIconRow.isIconOnLeft() : false;
+ mGearSnappedTo = snapped && mCurrIconRow != null;
+ }
+
+ /**
* Returns the horizontal space in pixels required to display the gear behind a
* notification.
*/
@@ -3493,7 +3619,7 @@
}
private void checkForDrag() {
- if (mCheckForDrag == null) {
+ if (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag)) {
mCheckForDrag = new CheckForDrag();
mHandler.postDelayed(mCheckForDrag, GEAR_SHOW_DELAY);
}
@@ -3504,7 +3630,6 @@
mCurrIconRow.cancelFadeAnimator();
}
mHandler.removeCallbacks(mCheckForDrag);
- mCheckForDrag = null;
}
private final class CheckForDrag implements Runnable {
@@ -3514,14 +3639,13 @@
final float absTransX = Math.abs(translation);
final float bounceBackToGearWidth = getSpaceForGear(mTranslatingParentView);
final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
- if (mCurrIconRow != null && absTransX >= bounceBackToGearWidth * 0.4
+ if ((mCurrIconRow != null && (!mCurrIconRow.isVisible()
+ || mCurrIconRow.isIconLocationChange(translation)))
+ && absTransX >= bounceBackToGearWidth * 0.4
&& absTransX < notiThreshold) {
- // Show icon
+ // Fade in the gear
mCurrIconRow.fadeInSettings(translation > 0 /* fromLeft */, translation,
notiThreshold);
- } else {
- // Allow more to be posted if this wasn't a drag.
- mCheckForDrag = null;
}
}
}
@@ -3534,7 +3658,7 @@
final View prevGearExposedView = mGearExposedView;
mGearExposedView = null;
-
+ mGearSnappedTo = false;
Animator anim = getViewTranslationAnimator(prevGearExposedView,
0 /* leftTarget */, null /* updateListener */);
if (anim != null) {
@@ -3610,7 +3734,6 @@
// ANIMATION_TYPE_DIMMED
new AnimationFilter()
- .animateY()
.animateDimmed(),
// ANIMATION_TYPE_CHANGE_POSITION
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 0ed6ef8..2524e1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -125,7 +125,7 @@
}
@Override
- protected boolean isPanelFullyCollapsed() {
+ public boolean isPanelFullyCollapsed() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
index 61135bd..26e1d46 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java
@@ -20,6 +20,8 @@
import android.provider.Settings;
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;
@@ -46,6 +48,12 @@
}
@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);
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 123165e..ff7ea27 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -34,6 +34,7 @@
import android.os.Debug;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.Log;
import com.android.systemui.Prefs;
@@ -51,7 +52,8 @@
public class PipManager {
private static final String TAG = "PipManager";
private static final boolean DEBUG = false;
- private static final boolean DEBUG_FORCE_ONBOARDING = false;
+ private static final boolean DEBUG_FORCE_ONBOARDING =
+ SystemProperties.getBoolean("debug.tv.pip_force_onboarding", false);
private static PipManager sPipManager;
@@ -66,6 +68,9 @@
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
+
+ private static final int CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS = 3000;
+
private int mSuspendPipResizingReason;
private static final float SCALE_FACTOR = 1.1f;
@@ -170,6 +175,12 @@
resizePinnedStack(mState);
}
};
+ private final Runnable mClosePipRunnable = new Runnable() {
+ @Override
+ public void run() {
+ closePip();
+ }
+ };
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -281,6 +292,7 @@
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onPipActivityClosed();
}
+ mHandler.removeCallbacks(mClosePipRunnable);
}
/**
@@ -543,6 +555,12 @@
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onMediaControllerChanged();
}
+ if (mPipMediaController == null) {
+ mHandler.postDelayed(mClosePipRunnable,
+ CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS);
+ } else {
+ mHandler.removeCallbacks(mClosePipRunnable);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index fb425ab..285dfd1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -50,7 +50,7 @@
private TextView mPlayPauseDescriptionTextView;
private View mCloseButtonView;
private View mCloseDescriptionView;
- private boolean mMovePipToFullscreen;
+ private boolean mPipMovedToFullscreen;
private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
@Override
@@ -70,7 +70,7 @@
@Override
public void onClick(View v) {
mPipManager.movePipToFullscreen();
- mMovePipToFullscreen = true;
+ mPipMovedToFullscreen = true;
finish();
}
});
@@ -169,7 +169,7 @@
}
private void restorePipAndFinish() {
- if (!mMovePipToFullscreen) {
+ if (!mPipMovedToFullscreen) {
mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
}
finish();
@@ -225,7 +225,7 @@
@Override
public void finish() {
super.finish();
- if (mPipManager.isRecentsShown() && !mMovePipToFullscreen) {
+ if (mPipManager.isRecentsShown() && !mPipMovedToFullscreen) {
SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
for (int i = services.length - 1; i >= 0; i--) {
if (services[i] instanceof Recents) {
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 8171b49..d36a1d7 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -1798,38 +1798,56 @@
// 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.
@@ -1935,8 +1953,50 @@
// a notification.
ACTION_TOUCH_GEAR = 333;
+ // Logs that the user has edited the enabled VR listeners.
+ VR_MANAGE_LISTENERS = 334;
+
+ // Settings -> Accessibility -> Click after pointer stops moving
+ ACCESSIBILITY_TOGGLE_AUTOCLICK = 335;
+ // Settings -> Sound
+ SOUND = 336;
+ // Settings -> Notifications -> Gear
+ CONFIGURE_NOTIFICATION = 337;
+ // Settings -> Wi-Fi -> Gear
+ CONFIGURE_WIFI = 338;
+ // Settings -> Display -> Display size
+ DISPLAY_SCREEN_ZOOM = 339;
+ // Settings -> Display -> Font size
+ ACCESSIBILITY_FONT_SIZE = 340;
+ // Settings -> Data usage -> Cellular/Wi-Fi data usage
+ DATA_USAGE_LIST = 341;
+ // Settings -> Data usage -> Billing cycle or DATA_USAGE_LIST -> Gear
+ BILLING_CYCLE = 342;
+ // DATA_USAGE_LIST -> Any item or App info -> Data usage
+ APP_DATA_USAGE = 343;
+ // Settings -> Language & input -> Language
+ USER_LOCALE_LIST = 344;
+ // Settings -> Language & input -> Virtual keyboard
+ VIRTUAL_KEYBOARDS = 345;
+ // Settings -> Language & input -> Physical keyboard
+ PHYSICAL_KEYBOARDS = 346;
+ // Settings -> Language & input -> Virtual keyboard -> Add a virtual keyboard
+ ENABLE_VIRTUAL_KEYBOARDS = 347;
+ // Settings -> Data usage -> Data Saver
+ DATA_SAVER_SUMMARY = 348;
+ // Settings -> Data usage -> Data Saver -> Unrestricted data access
+ DATA_USAGE_UNRESTRICTED_ACCESS = 349;
+
+ // Used for generic logging of Settings Preference Persistence, should not be used
+ // outside SharedPreferencesLogger.
+ ACTION_GENERIC_PACKAGE = 350;
+ // Settings -> Apps -> Gear -> Special access
+ SPECIAL_ACCESS = 351;
+
+ // Logs that the user docks window via shortcut key.
+ WINDOW_DOCK_SHORTCUTS = 352;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
-
}
}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 4877a378..4e667c6 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1062,7 +1062,7 @@
type, kind, norm, size);
}
return (jlong)(uintptr_t)rsElementCreate((RsContext)con, (RsDataType)type, (RsDataKind)kind,
- norm, size, true);
+ norm, size);
}
static jlong
@@ -1101,7 +1101,7 @@
jlong id = (jlong)(uintptr_t)rsElementCreate2((RsContext)con,
(const RsElement *)ids, fieldCount,
nameArray, fieldCount * sizeof(size_t), sizeArray,
- (const uint32_t *)arraySizes, fieldCount, true);
+ (const uint32_t *)arraySizes, fieldCount);
free(ids);
free(arraySizes);
@@ -1175,7 +1175,7 @@
}
return (jlong)(uintptr_t)rsTypeCreate((RsContext)con, (RsElement)eid, dimx, dimy, dimz, mips,
- faces, yuv, true);
+ faces, yuv);
}
static void
@@ -1211,7 +1211,7 @@
}
return (jlong)(uintptr_t) rsAllocationCreateTyped((RsContext)con, (RsType)type,
(RsAllocationMipmapControl)mips,
- (uint32_t)usage, (uintptr_t)pointer, true);
+ (uint32_t)usage, (uintptr_t)pointer);
}
static void
@@ -1316,7 +1316,7 @@
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- ptr, bitmap.getSize(), usage, true);
+ ptr, bitmap.getSize(), usage);
bitmap.unlockPixels();
return id;
}
@@ -1332,7 +1332,7 @@
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCreateTyped((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- (uint32_t)usage, (uintptr_t)ptr, true);
+ (uint32_t)usage, (uintptr_t)ptr);
bitmap.unlockPixels();
return id;
}
@@ -1348,7 +1348,7 @@
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- ptr, bitmap.getSize(), usage, true);
+ ptr, bitmap.getSize(), usage);
bitmap.unlockPixels();
return id;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index ad70853..562d9506 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -37,7 +37,8 @@
/**
* This class handles gesture detection for the Touch Explorer. It collects
- * touch events, and sends events to mListener as gestures are recognized.
+ * touch events and determines when they match a gesture, as well as when they
+ * won't match a gesture. These state changes are then surfaced to mListener.
*/
class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListener {
@@ -46,12 +47,58 @@
// Tag for logging received events.
private static final String LOG_TAG = "AccessibilityGestureDetector";
+ /**
+ * Listener functions are called as a result of onMoveEvent(). The current
+ * MotionEvent in the context of these functions is the event passed into
+ * onMotionEvent.
+ */
public interface Listener {
- public void onDoubleTapAndHold(MotionEvent event, int policyFlags);
- public boolean onDoubleTap(MotionEvent event, int policyFlags);
- public boolean onGestureCompleted(int gestureId);
- public void onGestureStarted();
- public void onGestureCancelled(MotionEvent event, int policyFlags);
+ /**
+ * Called when the user has performed a double tap and then held down
+ * the second tap.
+ *
+ * @param event The most recent MotionEvent received.
+ * @param policyFlags The policy flags of the most recent event.
+ */
+ void onDoubleTapAndHold(MotionEvent event, int policyFlags);
+
+ /**
+ * Called when the user lifts their finger on the second tap of a double
+ * tap.
+ *
+ * @param event The most recent MotionEvent received.
+ * @param policyFlags The policy flags of the most recent event.
+ *
+ * @return true if the event is consumed, else false
+ */
+ boolean onDoubleTap(MotionEvent event, int policyFlags);
+
+ /**
+ * Called when the system has decided the event stream is a gesture.
+ *
+ * @return true if the event is consumed, else false
+ */
+ boolean onGestureStarted();
+
+ /**
+ * Called when an event stream is recognized as a gesture.
+ *
+ * @param gestureId ID of the gesture that was recognized.
+ *
+ * @return true if the event is consumed, else false
+ */
+ boolean onGestureCompleted(int gestureId);
+
+ /**
+ * Called when the system has decided an event stream doesn't match any
+ * known gesture.
+ *
+ * @param event The most recent MotionEvent received.
+ * @param policyFlags The policy flags of the most recent event.
+ *
+ * @return true if the event is consumed, else false
+ */
+ public boolean onGestureCancelled(MotionEvent event, int policyFlags);
}
private final Listener mListener;
@@ -121,9 +168,9 @@
// movement when gesturing, and touch exploring. Based on user testing,
// all gestures started with the initial movement taking less than 100ms.
// When touch exploring, the first movement almost always takes longer than
- // 200ms. From this data, 150ms seems the best value to decide what
+ // 200ms. From this data, 200ms seems the best value to decide what
// kind of interaction it is.
- private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
+ private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 200;
// Time threshold used to determine if a gesture should be cancelled. If
// the finger pauses for longer than this delay, the ongoing gesture is
@@ -145,6 +192,18 @@
context.getResources().getDisplayMetrics()) * GESTURE_CONFIRM_MM;
}
+ /**
+ * Handle a motion event. If an action is completed, the appropriate
+ * callback on mListener is called, and the return value of the callback is
+ * passed to the caller.
+ *
+ * @param event The raw motion event. It's important that this be the raw
+ * event, before any transformations have been applied, so that measurements
+ * can be made in physical units.
+ * @param policyFlags Policy flags for the event.
+ *
+ * @return true if the event is consumed, else false
+ */
public boolean onMotionEvent(MotionEvent event, int policyFlags) {
final float x = event.getX();
final float y = event.getY();
@@ -179,13 +238,20 @@
mBaseY = y;
mBaseTime = time;
+ // Since the pointer has moved, this is not a double
+ // tap.
+ mFirstTapDetected = false;
+ mDoubleTapDetected = false;
+
// If this hasn't been confirmed as a gesture yet, send
// the event.
if (!mGestureStarted) {
mGestureStarted = true;
- mListener.onGestureStarted();
+ return mListener.onGestureStarted();
}
- } else {
+ } else if (!mFirstTapDetected) {
+ // The finger may not move if they are double tapping.
+ // In that case, we shouldn't cancel the gesture.
final long timeDelta = time - mBaseTime;
final long threshold = mGestureStarted ?
CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS :
@@ -195,8 +261,7 @@
// timeout, cancel gesture detection.
if (timeDelta > threshold) {
cancelGesture();
- mListener.onGestureCancelled(event, policyFlags);
- return false;
+ return mListener.onGestureCancelled(event, policyFlags);
}
}
@@ -211,16 +276,13 @@
break;
case MotionEvent.ACTION_UP:
- if (maybeFinishDoubleTap(event, policyFlags)) {
- return true;
+ if (mDoubleTapDetected) {
+ return finishDoubleTap(event, policyFlags);
}
if (mGestureStarted) {
mStrokeBuffer.add(new GesturePoint(x, y, time));
- if (!recognizeGesture()) {
- mListener.onGestureCancelled(event, policyFlags);
- }
- return true;
+ return recognizeGesture(event, policyFlags);
}
break;
@@ -244,8 +306,8 @@
case MotionEvent.ACTION_POINTER_UP:
// If we're detecting taps on the second finger, see if we
// should finish the double tap.
- if (mSecondFingerDoubleTap && maybeFinishDoubleTap(event, policyFlags)) {
- return true;
+ if (mSecondFingerDoubleTap && mDoubleTapDetected) {
+ return finishDoubleTap(event, policyFlags);
}
break;
@@ -308,7 +370,7 @@
// The processing of the double tap is deferred until the finger is
// lifted, so that we can detect a long press on the second tap.
mDoubleTapDetected = true;
- return true;
+ return false;
}
private void maybeSendLongPress(MotionEvent event, int policyFlags) {
@@ -321,11 +383,7 @@
mListener.onDoubleTapAndHold(event, policyFlags);
}
- private boolean maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
- if (!mDoubleTapDetected) {
- return false;
- }
-
+ private boolean finishDoubleTap(MotionEvent event, int policyFlags) {
clear();
return mListener.onDoubleTap(event, policyFlags);
@@ -337,7 +395,7 @@
mStrokeBuffer.clear();
}
- private boolean recognizeGesture() {
+ private boolean recognizeGesture(MotionEvent event, int policyFlags) {
Gesture gesture = new Gesture();
gesture.addStroke(new GestureStroke(mStrokeBuffer));
@@ -351,16 +409,14 @@
}
try {
final int gestureId = Integer.parseInt(bestPrediction.name);
- if (mListener.onGestureCompleted(gestureId)) {
- return true;
- }
+ return mListener.onGestureCompleted(gestureId);
} catch (NumberFormatException nfe) {
Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
}
}
}
- return false;
+ return mListener.onGestureCancelled(event, policyFlags);
}
private MotionEvent mapSecondPointerToFirstPointer(MotionEvent event) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9e6c21c..b4b40ae 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3436,7 +3436,8 @@
case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
- case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: {
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_SCREENSHOT: {
return AccessibilityWindowInfo.TYPE_SYSTEM;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 3ecff40..3cc991c 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -437,6 +437,20 @@
}
@Override
+ public boolean onGestureStarted() {
+ // We have to perform gesture detection, so
+ // clear the current state and try to detect.
+ mCurrentState = STATE_GESTURE_DETECTING;
+ mSendHoverEnterAndMoveDelayed.cancel();
+ mSendHoverExitDelayed.cancel();
+ mExitGestureDetectionModeDelayed.post();
+ // Send accessibility event to announce the start
+ // of gesture recognition.
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
+ return false;
+ }
+
+ @Override
public boolean onGestureCompleted(int gestureId) {
if (mCurrentState != STATE_GESTURE_DETECTING) {
return false;
@@ -450,36 +464,26 @@
}
@Override
- public void onGestureStarted() {
- // We have to perform gesture detection, so
- // clear the current state and try to detect.
- mCurrentState = STATE_GESTURE_DETECTING;
- mSendHoverEnterAndMoveDelayed.cancel();
- mSendHoverExitDelayed.cancel();
- mExitGestureDetectionModeDelayed.post();
- // Send accessibility event to announce the start
- // of gesture recognition.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
- }
+ public boolean onGestureCancelled(MotionEvent event, int policyFlags) {
+ if (mCurrentState == STATE_GESTURE_DETECTING) {
+ endGestureDetection();
+ return true;
+ } else if (mCurrentState == STATE_TOUCH_EXPLORING) {
+ // If the finger is still moving, pass the event on.
+ if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
+ final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
+ final int pointerIdBits = (1 << pointerId);
- @Override
- public void onGestureCancelled(MotionEvent event, int policyFlags) {
- if (mCurrentState == STATE_GESTURE_DETECTING) {
- endGestureDetection();
- } else if (mCurrentState == STATE_TOUCH_EXPLORING) {
- // If the finger is still moving, pass the event on.
- if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
- final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
- final int pointerIdBits = (1 << pointerId);
-
- // We have just decided that the user is touch,
- // exploring so start sending events.
- mSendHoverEnterAndMoveDelayed.addEvent(event);
- mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
- mSendHoverExitDelayed.cancel();
- sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
- }
- }
+ // We have just decided that the user is touch,
+ // exploring so start sending events.
+ mSendHoverEnterAndMoveDelayed.addEvent(event);
+ mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
+ mSendHoverExitDelayed.cancel();
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
+ return true;
+ }
+ }
+ return false;
}
/**
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 8603981..3659a40 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2496,7 +2496,7 @@
return mPackageManager.queryIntentReceivers(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags, userId);
+ flags, userId).getList();
} catch (RemoteException re) {
return Collections.emptyList();
} finally {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index f1a9c44..cd4d107d 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -3495,9 +3495,8 @@
// The agent was running with a stub Application object, so shut it down.
// !!! We hardcode the confirmation UI's package name here rather than use a
// manifest flag! TODO something less direct.
- if (app.uid != Process.SYSTEM_UID
- && !app.packageName.equals("com.android.backupconfirm")
- && app.uid != Process.PHONE_UID) {
+ if (app.uid >= Process.FIRST_APPLICATION_UID
+ && !app.packageName.equals("com.android.backupconfirm")) {
if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
mActivityManager.killApplicationProcess(app.processName, app.uid);
} else {
@@ -6881,7 +6880,7 @@
// The agent was running with a stub Application object, so shut it down.
// !!! We hardcode the confirmation UI's package name here rather than use a
// manifest flag! TODO something less direct.
- if (app.uid != Process.SYSTEM_UID
+ if (app.uid >= Process.FIRST_APPLICATION_UID
&& !app.packageName.equals("com.android.backupconfirm")) {
if (DEBUG) Slog.d(TAG, "Killing host process");
mActivityManager.killApplicationProcess(app.processName, app.uid);
@@ -8625,13 +8624,15 @@
// it is explicitly not killed following that operation.
//
// We execute this kill when these conditions hold:
- // 1. the app did not request its own restore (mTargetPackage == null), and either
- // 2a. the app is a full-data target (TYPE_FULL_STREAM) or
+ // 1. it's not a system-uid process,
+ // 2. the app did not request its own restore (mTargetPackage == null), and either
+ // 3a. the app is a full-data target (TYPE_FULL_STREAM) or
// b. the app does not state android:killAfterRestore="false" in its manifest
final int appFlags = mCurrentPackage.applicationInfo.flags;
final boolean killAfterRestore =
- (mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM)
- || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0);
+ (mCurrentPackage.applicationInfo.uid >= Process.FIRST_APPLICATION_UID)
+ && ((mRestoreDescription.getDataType() == RestoreDescription.TYPE_FULL_STREAM)
+ || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0));
if (mTargetPackage == null && killAfterRestore) {
if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 4667172..c5a210c 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -150,6 +150,12 @@
int mNumTimeChanged;
/**
+ * The current set of user whitelisted apps for device idle mode, meaning these are allowed
+ * to freely schedule alarms.
+ */
+ int[] mDeviceIdleUserWhitelist = new int[0];
+
+ /**
* For each uid, this is the last time we dispatched an "allow while idle" alarm,
* used to determine the earliest we can dispatch the next such alarm.
*/
@@ -936,6 +942,7 @@
}
publishBinderService(Context.ALARM_SERVICE, mService);
+ publishLocalService(LocalService.class, new LocalService());
}
@Override
@@ -1251,14 +1258,6 @@
flags &= ~AlarmManager.FLAG_IDLE_UNTIL;
}
- // If the caller is a core system component, and not calling to do work on behalf
- // of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED. This means we
- // will allow these alarms to go off as normal even while idle, with no timing
- // restrictions.
- if (callingUid < Process.FIRST_APPLICATION_UID && workSource == null) {
- flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
- }
-
// If this is an exact time alarm, then it can't be batched with other alarms.
if (windowLength == AlarmManager.WINDOW_EXACT) {
flags |= AlarmManager.FLAG_STANDALONE;
@@ -1268,6 +1267,16 @@
// use it to wake early from idle if needed.
if (alarmClock != null) {
flags |= AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE;
+
+ // If the caller is a core system component or on the user's whitelist, and not calling
+ // to do work on behalf of someone else, then always set ALLOW_WHILE_IDLE_UNRESTRICTED.
+ // This means we will allow these alarms to go off as normal even while idle, with no
+ // timing restrictions.
+ } else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
+ || Arrays.binarySearch(mDeviceIdleUserWhitelist,
+ UserHandle.getAppId(callingUid)) >= 0)) {
+ flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
+ flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
}
setImpl(type, triggerAtTime, windowLength, interval, operation, directReceiver,
@@ -1344,6 +1353,12 @@
}
};
+ public final class LocalService {
+ public void setDeviceIdleUserWhitelist(int[] appids) {
+ setDeviceIdleUserWhitelistImpl(appids);
+ }
+ }
+
void dumpImpl(PrintWriter pw) {
synchronized (mLock) {
pw.println("Current Alarm Manager state:");
@@ -1386,6 +1401,7 @@
pw.print(" Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
pw.print(" Num time change events: "); pw.println(mNumTimeChanged);
+ pw.println(" mDeviceIdleUserWhitelist=" + Arrays.toString(mDeviceIdleUserWhitelist));
pw.println();
pw.println(" Next alarm clock information: ");
@@ -1678,6 +1694,12 @@
}
}
+ void setDeviceIdleUserWhitelistImpl(int[] appids) {
+ synchronized (mLock) {
+ mDeviceIdleUserWhitelist = appids;
+ }
+ }
+
AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) {
synchronized (mLock) {
return mNextAlarmClockForUser.get(userId);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 32f2d59..bfe9e8e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -25,6 +25,7 @@
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -57,6 +58,7 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -107,8 +109,21 @@
private final SparseArray<UidState> mUidStates = new SparseArray<>();
- /** These are app op restrictions imposed per user from various parties */
- private final ArrayMap<IBinder, SparseArray<boolean[]>> mOpUserRestrictions = new ArrayMap<>();
+ /*
+ * These are app op restrictions imposed per user from various parties.
+ *
+ * This is organized as follows:
+ *
+ * ArrayMap w/ mapping:
+ * IBinder (for client imposing restriction) --> SparseArray w/ mapping:
+ * User handle --> Pair containing:
+ * - Array w/ index = AppOp code, value = restricted status boolean
+ * - SparseArray w/ mapping:
+ * AppOp code --> Set of packages that are not restricted for this code
+ *
+ */
+ private final ArrayMap<IBinder, SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>>
+ mOpUserRestrictions = new ArrayMap<>();
private static final class UidState {
public final int uid;
@@ -1267,11 +1282,35 @@
private boolean isOpRestricted(int uid, int code, String packageName) {
int userHandle = UserHandle.getUserId(uid);
final int restrictionSetCount = mOpUserRestrictions.size();
+
for (int i = 0; i < restrictionSetCount; i++) {
- SparseArray<boolean[]> perUserRestrictions = mOpUserRestrictions.valueAt(i);
- boolean[] opRestrictions = perUserRestrictions.get(userHandle);
- if (opRestrictions != null && opRestrictions[code]) {
+ // For each client, check that the given op is not restricted, or that the given
+ // package is exempt from the restriction.
+
+ SparseArray<Pair<boolean[],SparseArray<ArraySet<String>>>> perUserRestrictions =
+ mOpUserRestrictions.valueAt(i);
+
+ Pair<boolean[],SparseArray<ArraySet<String>>> restrictions =
+ perUserRestrictions.get(userHandle);
+ if (restrictions == null) {
+ continue; // No restrictions set by this client
+ }
+
+ boolean[] opRestrictions = restrictions.first;
+ SparseArray<ArraySet<String>> opExceptions = restrictions.second;
+
+ if (opRestrictions == null) {
+ continue; // No restrictions set by this client
+ }
+
+ if (opRestrictions[code]) {
+ if (opExceptions != null && opExceptions.get(code) != null &&
+ opExceptions.get(code).contains(packageName)) {
+ continue; // AppOps code is restricted, but this package is exempt
+ }
+
if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
+ // If we are the system, bypass user restrictions for certain codes
synchronized (this) {
Ops ops = getOpsLocked(uid, packageName, true);
if ((ops != null) && ops.isPrivileged) {
@@ -1279,6 +1318,7 @@
}
}
}
+
return true;
}
}
@@ -2069,7 +2109,8 @@
}
@Override
- public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle) {
+ public void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle,
+ String[] exceptionPackages) {
if (Binder.getCallingPid() != Process.myPid()) {
mContext.enforcePermission(Manifest.permission.MANAGE_APP_OPS_RESTRICTIONS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
@@ -2085,12 +2126,37 @@
}
verifyIncomingOp(code);
Preconditions.checkNotNull(token);
- setUserRestrictionNoCheck(code, restricted, token, userHandle);
+ setUserRestrictionNoCheck(code, restricted, token, userHandle, exceptionPackages);
}
private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
int userHandle) {
+ setUserRestrictionNoCheck(code, restricted, token, userHandle, /*exceptionPackages*/null);
+ }
+
+ private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
+ int userHandle, String[] exceptionPackages) {
+
final boolean[] opRestrictions = getOrCreateUserRestrictionsForToken(token, userHandle);
+
+ if (restricted) {
+ final SparseArray<ArraySet<String>> opExceptions =
+ getUserPackageExemptionsForToken(token, userHandle);
+
+ // If exceptionPackages is not null, update the exception packages for this AppOps code
+ ArraySet<String> exceptions = opExceptions.get(code);
+ if (exceptionPackages != null) {
+ if (exceptions == null) {
+ exceptions = new ArraySet<>(exceptionPackages.length);
+ opExceptions.put(code, exceptions);
+ } else {
+ exceptions.clear();
+ }
+
+ exceptions.addAll(Arrays.asList(exceptionPackages));
+ }
+ }
+
if (opRestrictions[code] == restricted) {
return;
}
@@ -2132,7 +2198,8 @@
checkSystemUid("removeUser");
final int tokenCount = mOpUserRestrictions.size();
for (int i = tokenCount - 1; i >= 0; i--) {
- SparseArray<boolean[]> opRestrictions = mOpUserRestrictions.valueAt(i);
+ SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> opRestrictions =
+ mOpUserRestrictions.valueAt(i);
if (opRestrictions != null) {
opRestrictions.remove(userHandle);
if (opRestrictions.size() <= 0) {
@@ -2144,15 +2211,23 @@
private void pruneUserRestrictionsForToken(IBinder token, int userHandle) {
- SparseArray<boolean[]> perTokenRestrictions = mOpUserRestrictions.get(token);
+ SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions =
+ mOpUserRestrictions.get(token);
if (perTokenRestrictions != null) {
- final boolean[] opRestrictions = perTokenRestrictions.get(userHandle);
- if (opRestrictions != null) {
- for (boolean restriction : opRestrictions) {
- if (restriction) {
- return;
+ final Pair<boolean[], SparseArray<ArraySet<String>>> restrictions =
+ perTokenRestrictions.get(userHandle);
+
+ if (restrictions != null) {
+ final boolean[] opRestrictions = restrictions.first;
+ if (opRestrictions != null) {
+ for (boolean restriction : opRestrictions) {
+ if (restriction) {
+ return;
+ }
}
}
+
+ // No restrictions set for this client
perTokenRestrictions.remove(userHandle);
if (perTokenRestrictions.size() <= 0) {
mOpUserRestrictions.remove(token);
@@ -2161,18 +2236,61 @@
}
}
+ /**
+ * Get or create the user restrictions array for a given client if it doesn't already exist.
+ *
+ * @param token the binder client creating the restriction.
+ * @param userHandle the user handle to create a restriction for.
+ *
+ * @return the array of restriction states for each AppOps code.
+ */
private boolean[] getOrCreateUserRestrictionsForToken(IBinder token, int userHandle) {
- SparseArray<boolean[]> perTokenRestrictions = mOpUserRestrictions.get(token);
+ SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions =
+ mOpUserRestrictions.get(token);
+
if (perTokenRestrictions == null) {
- perTokenRestrictions = new SparseArray<>();
+ perTokenRestrictions =
+ new SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>>();
mOpUserRestrictions.put(token, perTokenRestrictions);
}
- boolean[] opRestrictions = perTokenRestrictions.get(userHandle);
- if (opRestrictions == null) {
- opRestrictions = new boolean[AppOpsManager._NUM_OP];
- perTokenRestrictions.put(userHandle, opRestrictions);
+
+ Pair<boolean[], SparseArray<ArraySet<String>>> restrictions =
+ perTokenRestrictions.get(userHandle);
+
+ if (restrictions == null) {
+ restrictions = new Pair<boolean[], SparseArray<ArraySet<String>>>(
+ new boolean[AppOpsManager._NUM_OP], new SparseArray<ArraySet<String>>());
+ perTokenRestrictions.put(userHandle, restrictions);
}
- return opRestrictions;
+
+ return restrictions.first;
+ }
+
+ /**
+ * Get the per-package exemptions for each AppOps code for a given client and userHandle.
+ *
+ * @param token the binder client to get the exemptions for.
+ * @param userHandle the user handle to get the exemptions for.
+ *
+ * @return a mapping from the AppOps code to a set of packages exempt for that code.
+ */
+ private SparseArray<ArraySet<String>> getUserPackageExemptionsForToken(IBinder token,
+ int userHandle) {
+ SparseArray<Pair<boolean[], SparseArray<ArraySet<String>>>> perTokenRestrictions =
+ mOpUserRestrictions.get(token);
+
+ if (perTokenRestrictions == null) {
+ return null; // Don't create user restrictions accidentally
+ }
+
+ Pair<boolean[], SparseArray<ArraySet<String>>> restrictions =
+ perTokenRestrictions.get(userHandle);
+
+ if (restrictions == null) {
+ return null; // Don't create user restrictions accidentally
+ }
+
+ return restrictions.second;
}
private void checkSystemUid(String function) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 8cfeb74..799a763 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -19,7 +19,6 @@
import android.Manifest;
import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothCallback;
@@ -44,7 +43,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -53,10 +51,9 @@
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
-import android.util.Log;
+import android.util.Slog;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@@ -92,14 +89,15 @@
private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
private static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41;
private static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42;
- private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60;
- private static final int MESSAGE_TIMEOUT_BIND =100;
- private static final int MESSAGE_TIMEOUT_UNBIND =101;
+ private static final int MESSAGE_BLUETOOTH_STATE_CHANGE = 60;
+ private static final int MESSAGE_TIMEOUT_BIND = 100;
+ private static final int MESSAGE_TIMEOUT_UNBIND = 101;
private static final int MESSAGE_USER_SWITCHED = 300;
+ private static final int MESSAGE_USER_UNLOCKED = 301;
private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
- private static final int MAX_SAVE_RETRIES=3;
- private static final int MAX_ERROR_RESTART_RETRIES=6;
+ private static final int MAX_SAVE_RETRIES = 3;
+ private static final int MAX_ERROR_RESTART_RETRIES = 6;
// Bluetooth persisted setting is off
private static final int BLUETOOTH_OFF=0;
@@ -176,7 +174,7 @@
String action = intent.getAction();
if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
- if (DBG) Log.d(TAG, "Bluetooth Adapter name changed to " + newName);
+ if (DBG) Slog.d(TAG, "Bluetooth Adapter name changed to " + newName);
if (newName != null) {
storeNameAndAddress(newName, null);
}
@@ -195,10 +193,10 @@
try {
st = mBluetooth.getState();
} catch (RemoteException e) {
- Log.e(TAG,"Unable to call getState", e);
+ Slog.e(TAG,"Unable to call getState", e);
}
}
- Log.d(TAG, "state" + st);
+ Slog.d(TAG, "state" + st);
if (isAirplaneModeOn()) {
// Clear registered LE apps to force shut-off
@@ -214,16 +212,16 @@
mEnableExternal = false;
}
} catch(RemoteException e) {
- Log.e(TAG,"Unable to call onBrEdrDown", e);
+ Slog.e(TAG,"Unable to call onBrEdrDown", e);
}
} else if (st == BluetoothAdapter.STATE_ON){
// disable without persisting the setting
- Log.d(TAG, "Calling disable");
+ Slog.d(TAG, "Calling disable");
sendDisableMsg();
}
} else if (mEnableExternal) {
// enable without persisting the setting
- Log.d(TAG, "Calling enable");
+ Slog.d(TAG, "Calling enable");
sendEnableMsg(mQuietEnableExternal);
}
}
@@ -267,7 +265,7 @@
PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
} catch (PackageManager.NameNotFoundException e) {
// Some platforms, such as wearables do not have a system ui.
- Log.w(TAG, "Unable to resolve SystemUI's UID.", e);
+ Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
}
mSystemUiUid = sysUiUid;
}
@@ -320,17 +318,17 @@
* in the local cache
*/
private void loadStoredNameAndAddress() {
- if (DBG) Log.d(TAG, "Loading stored name and address");
+ if (DBG) Slog.d(TAG, "Loading stored name and address");
if (mContext.getResources().getBoolean
(com.android.internal.R.bool.config_bluetooth_address_validation) &&
Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) == 0) {
// if the valid flag is not set, don't load the address and name
- if (DBG) Log.d(TAG, "invalid bluetooth name and address stored");
+ if (DBG) Slog.d(TAG, "invalid bluetooth name and address stored");
return;
}
mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME);
mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS);
- if (DBG) Log.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
+ if (DBG) Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
}
/**
@@ -343,14 +341,14 @@
if (name != null) {
Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
mName = name;
- if (DBG) Log.d(TAG,"Stored Bluetooth name: " +
+ if (DBG) Slog.d(TAG,"Stored Bluetooth name: " +
Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME));
}
if (address != null) {
Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);
mAddress=address;
- if (DBG) Log.d(TAG,"Stored Bluetoothaddress: " +
+ if (DBG) Slog.d(TAG,"Stored Bluetoothaddress: " +
Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS));
}
@@ -361,7 +359,7 @@
public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
if (callback == null) {
- Log.w(TAG, "Callback is null in registerAdapter");
+ Slog.w(TAG, "Callback is null in registerAdapter");
return null;
}
Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
@@ -374,7 +372,7 @@
public void unregisterAdapter(IBluetoothManagerCallback callback) {
if (callback == null) {
- Log.w(TAG, "Callback is null in unregisterAdapter");
+ Slog.w(TAG, "Callback is null in unregisterAdapter");
return;
}
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
@@ -403,7 +401,7 @@
public boolean isEnabled() {
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
(!checkIfCallerIsForegroundUser())) {
- Log.w(TAG,"isEnabled(): not allowed for non-active and non system user");
+ Slog.w(TAG,"isEnabled(): not allowed for non-active and non system user");
return false;
}
@@ -411,7 +409,7 @@
try {
return (mBluetooth != null && mBluetooth.isEnabled());
} catch (RemoteException e) {
- Log.e(TAG, "isEnabled()", e);
+ Slog.e(TAG, "isEnabled()", e);
}
}
return false;
@@ -419,17 +417,17 @@
class ClientDeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
- if (DBG) Log.d(TAG, "Binder is dead - unregister Ble App");
+ if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App");
if (mBleAppCount > 0) --mBleAppCount;
if (mBleAppCount == 0) {
- if (DBG) Log.d(TAG, "Disabling LE only mode after application crash");
+ if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
try {
if (mBluetooth != null) {
mBluetooth.onBrEdrDown();
}
} catch(RemoteException e) {
- Log.e(TAG,"Unable to call onBrEdrDown", e);
+ Slog.e(TAG,"Unable to call onBrEdrDown", e);
}
}
}
@@ -459,7 +457,7 @@
try {
if (mBluetooth != null) mBluetooth.onBrEdrDown();
} catch (RemoteException e) {
- Log.e(TAG, "error when disabling bluetooth", e);
+ Slog.e(TAG, "error when disabling bluetooth", e);
}
}
}
@@ -474,11 +472,11 @@
private void disableBleScanMode() {
try {
if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
- if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable");
+ if (DBG) Slog.d(TAG, "Reseting the mEnable flag for clean disable");
mEnable = false;
}
} catch (RemoteException e) {
- Log.e(TAG, "getState()", e);
+ Slog.e(TAG, "getState()", e);
}
}
@@ -496,7 +494,7 @@
synchronized (this) {
++mBleAppCount;
}
- if (DBG) Log.d(TAG, "Registered for death Notification");
+ if (DBG) Slog.d(TAG, "Registered for death Notification");
}
} else {
@@ -508,10 +506,10 @@
synchronized (this) {
if (mBleAppCount > 0) --mBleAppCount;
}
- if (DBG) Log.d(TAG, "Unregistered for death Notification");
+ if (DBG) Slog.d(TAG, "Unregistered for death Notification");
}
}
- if (DBG) Log.d(TAG, "Updated BleAppCount" + mBleAppCount);
+ if (DBG) Slog.d(TAG, "Updated BleAppCount" + mBleAppCount);
if (mBleAppCount == 0 && mEnable) {
disableBleScanMode();
}
@@ -528,7 +526,7 @@
/** @hide*/
public boolean isBleAppPresent() {
- if (DBG) Log.d(TAG, "isBleAppPresent() count: " + mBleAppCount);
+ if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleAppCount);
return (mBleAppCount > 0);
}
@@ -536,7 +534,7 @@
* Action taken when GattService is turned off
*/
private void onBluetoothGattServiceUp() {
- if (DBG) Log.d(TAG,"BluetoothGatt Service is Up");
+ if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up");
try{
if (isBleAppPresent() == false && mBluetooth != null
&& mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
@@ -548,7 +546,7 @@
Binder.restoreCallingIdentity(callingIdentity);
}
} catch(RemoteException e) {
- Log.e(TAG,"Unable to call onServiceUp", e);
+ Slog.e(TAG,"Unable to call onServiceUp", e);
}
}
@@ -557,10 +555,10 @@
* and turn off all service and stack if no LE app needs it
*/
private void sendBrEdrDownCallback() {
- if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks");
+ if (DBG) Slog.d(TAG,"Calling sendBrEdrDownCallback callbacks");
if(mBluetooth == null) {
- Log.w(TAG, "Bluetooth handle is null");
+ Slog.w(TAG, "Bluetooth handle is null");
return;
}
@@ -568,14 +566,14 @@
try {
mBluetooth.onBrEdrDown();
} catch(RemoteException e) {
- Log.e(TAG, "Call to onBrEdrDown() failed.", e);
+ Slog.e(TAG, "Call to onBrEdrDown() failed.", e);
}
} else {
// Need to stay at BLE ON. Disconnect all Gatt connections
try{
mBluetoothGatt.unregAll();
} catch(RemoteException e) {
- Log.e(TAG, "Unable to disconnect all apps.", e);
+ Slog.e(TAG, "Unable to disconnect all apps.", e);
}
}
}
@@ -586,7 +584,7 @@
"Need BLUETOOTH ADMIN permission");
if (DBG) {
- Log.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth +
+ Slog.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth +
" mBinding = " + mBinding);
}
int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
@@ -606,14 +604,14 @@
public boolean enable() {
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
(!checkIfCallerIsForegroundUser())) {
- Log.w(TAG,"enable(): not allowed for non-active and non system user");
+ Slog.w(TAG,"enable(): not allowed for non-active and non system user");
return false;
}
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
if (DBG) {
- Log.d(TAG,"enable(): mBluetooth =" + mBluetooth +
+ Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth +
" mBinding = " + mBinding);
}
@@ -623,7 +621,7 @@
// waive WRITE_SECURE_SETTINGS permission check
sendEnableMsg(false);
}
- if (DBG) Log.d(TAG, "enable returning");
+ if (DBG) Slog.d(TAG, "enable returning");
return true;
}
@@ -633,12 +631,12 @@
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
(!checkIfCallerIsForegroundUser())) {
- Log.w(TAG,"disable(): not allowed for non-active and non system user");
+ Slog.w(TAG,"disable(): not allowed for non-active and non system user");
return false;
}
if (DBG) {
- Log.d(TAG,"disable(): mBluetooth = " + mBluetooth +
+ Slog.d(TAG,"disable(): mBluetooth = " + mBluetooth +
" mBinding = " + mBinding);
}
@@ -657,7 +655,7 @@
public void unbindAndFinish() {
if (DBG) {
- Log.d(TAG,"unbindAndFinish(): " + mBluetooth +
+ Slog.d(TAG,"unbindAndFinish(): " + mBluetooth +
" mBinding = " + mBinding);
}
@@ -669,10 +667,10 @@
try {
mBluetooth.unregisterCallback(mBluetoothCallback);
} catch (RemoteException re) {
- Log.e(TAG, "Unable to unregister BluetoothCallback",re);
+ Slog.e(TAG, "Unable to unregister BluetoothCallback",re);
}
- if (DBG) Log.d(TAG, "Sending unbind request.");
+ if (DBG) Slog.d(TAG, "Sending unbind request.");
mBluetoothBinder = null;
mBluetooth = null;
//Unbind
@@ -696,7 +694,7 @@
IBluetoothProfileServiceConnection proxy) {
if (!mEnable) {
if (DBG) {
- Log.d(TAG, "Trying to bind to profile: " + bluetoothProfile +
+ Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile +
", while Bluetooth was disabled");
}
return false;
@@ -705,7 +703,7 @@
ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
if (psc == null) {
if (DBG) {
- Log.d(TAG, "Creating new ProfileServiceConnections object for"
+ Slog.d(TAG, "Creating new ProfileServiceConnections object for"
+ " profile: " + bluetoothProfile);
}
@@ -746,7 +744,7 @@
try {
mContext.unbindService(psc);
} catch (IllegalArgumentException e) {
- Log.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
+ Slog.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
}
psc.removeAllProxies();
}
@@ -759,10 +757,15 @@
* PHASE_SYSTEM_SERVICES_READY.
*/
public void handleOnBootPhase() {
- if (DBG) Log.d(TAG, "Bluetooth boot completed");
+ if (DBG) Slog.d(TAG, "Bluetooth boot completed");
if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
- if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
+ if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth.");
sendEnableMsg(mQuietEnableExternal);
+ } else if (!isNameAndAddressSet()) {
+ if (DBG) Slog.d(TAG, "Getting adapter name and address");
+ enable();
+ waitForOnOff(true, false);
+ disable(true);
}
}
@@ -770,8 +773,16 @@
* Called when switching to a different foreground user.
*/
public void handleOnSwitchUser(int userHandle) {
- if (DBG) Log.d(TAG, "Bluetooth user switched");
- mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0));
+ if (DBG) Slog.d(TAG, "User " + userHandle + " switched");
+ mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0).sendToTarget();
+ }
+
+ /**
+ * Called when user is unlocked.
+ */
+ public void handleOnUnlockUser(int userHandle) {
+ if (DBG) Slog.d(TAG, "User " + userHandle + " unlocked");
+ mHandler.obtainMessage(MESSAGE_USER_UNLOCKED, userHandle, 0).sendToTarget();
}
/**
@@ -801,7 +812,7 @@
mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
return true;
}
- Log.w(TAG, "Unable to bind with intent: " + mIntent);
+ Slog.w(TAG, "Unable to bind with intent: " + mIntent);
return false;
}
@@ -811,7 +822,7 @@
try{
proxy.onServiceConnected(mClassName, mService);
} catch (RemoteException e) {
- Log.e(TAG, "Unable to connect to proxy", e);
+ Slog.e(TAG, "Unable to connect to proxy", e);
}
} else {
if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) {
@@ -828,11 +839,11 @@
try {
proxy.onServiceDisconnected(mClassName);
} catch (RemoteException e) {
- Log.e(TAG, "Unable to disconnect proxy", e);
+ Slog.e(TAG, "Unable to disconnect proxy", e);
}
}
} else {
- Log.w(TAG, "Trying to remove a null proxy");
+ Slog.w(TAG, "Trying to remove a null proxy");
}
}
@@ -850,11 +861,11 @@
try {
mService.linkToDeath(this, 0);
} catch (RemoteException e) {
- Log.e(TAG, "Unable to linkToDeath", e);
+ Slog.e(TAG, "Unable to linkToDeath", e);
}
if (mInvokingProxyCallbacks) {
- Log.e(TAG, "Proxy callbacks already in progress.");
+ Slog.e(TAG, "Proxy callbacks already in progress.");
return;
}
mInvokingProxyCallbacks = true;
@@ -865,7 +876,7 @@
try {
mProxies.getBroadcastItem(i).onServiceConnected(className, service);
} catch (RemoteException e) {
- Log.e(TAG, "Unable to connect to proxy", e);
+ Slog.e(TAG, "Unable to connect to proxy", e);
}
}
} finally {
@@ -882,7 +893,7 @@
mClassName = null;
if (mInvokingProxyCallbacks) {
- Log.e(TAG, "Proxy callbacks already in progress.");
+ Slog.e(TAG, "Proxy callbacks already in progress.");
return;
}
mInvokingProxyCallbacks = true;
@@ -893,7 +904,7 @@
try {
mProxies.getBroadcastItem(i).onServiceDisconnected(className);
} catch (RemoteException e) {
- Log.e(TAG, "Unable to disconnect from proxy", e);
+ Slog.e(TAG, "Unable to disconnect from proxy", e);
}
}
} finally {
@@ -905,7 +916,7 @@
@Override
public void binderDied() {
if (DBG) {
- Log.w(TAG, "Profile service for profile: " + mClassName
+ Slog.w(TAG, "Profile service for profile: " + mClassName
+ " died.");
}
onServiceDisconnected(mClassName);
@@ -919,12 +930,12 @@
private void sendBluetoothStateCallback(boolean isUp) {
try {
int n = mStateChangeCallbacks.beginBroadcast();
- if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
+ if (DBG) Slog.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
for (int i=0; i <n;i++) {
try {
mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp);
} catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i , e);
+ Slog.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i , e);
}
}
} finally {
@@ -936,15 +947,15 @@
* Inform BluetoothAdapter instances that Adapter service is up
*/
private void sendBluetoothServiceUpCallback() {
- if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
+ if (DBG) Slog.d(TAG,"Calling onBluetoothServiceUp callbacks");
try {
int n = mCallbacks.beginBroadcast();
- Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+ Slog.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
for (int i=0; i <n;i++) {
try {
mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
} catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+ Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
}
}
} finally {
@@ -955,15 +966,15 @@
* Inform BluetoothAdapter instances that Adapter service is down
*/
private void sendBluetoothServiceDownCallback() {
- if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
+ if (DBG) Slog.d(TAG,"Calling onBluetoothServiceDown callbacks");
try {
int n = mCallbacks.beginBroadcast();
- Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+ Slog.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
for (int i=0; i <n;i++) {
try {
mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
} catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+ Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
}
}
} finally {
@@ -977,7 +988,7 @@
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
(!checkIfCallerIsForegroundUser())) {
- Log.w(TAG,"getAddress(): not allowed for non-active and non system user");
+ Slog.w(TAG,"getAddress(): not allowed for non-active and non system user");
return null;
}
@@ -991,10 +1002,11 @@
try {
return mBluetooth.getAddress();
} catch (RemoteException e) {
- Log.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e);
+ Slog.e(TAG, "getAddress(): Unable to retrieve address remotely..Returning cached address",e);
}
}
}
+
// mAddress is accessed from outside.
// It is alright without a lock. Here, bluetooth is off, no other thread is
// changing mAddress
@@ -1007,7 +1019,7 @@
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
(!checkIfCallerIsForegroundUser())) {
- Log.w(TAG,"getName(): not allowed for non-active and non system user");
+ Slog.w(TAG,"getName(): not allowed for non-active and non system user");
return null;
}
@@ -1016,7 +1028,7 @@
try {
return mBluetooth.getName();
} catch (RemoteException e) {
- Log.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e);
+ Slog.e(TAG, "getName(): Unable to retrieve name remotely..Returning cached name",e);
}
}
}
@@ -1028,7 +1040,7 @@
private class BluetoothServiceConnection implements ServiceConnection {
public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
+ if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
// TBD if (className.getClassName().equals(IBluetooth.class.getName())) {
if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
@@ -1037,7 +1049,7 @@
} else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
msg.arg1 = SERVICE_IBLUETOOTHGATT;
} else {
- Log.e(TAG, "Unknown service connected: " + className.getClassName());
+ Slog.e(TAG, "Unknown service connected: " + className.getClassName());
return;
}
msg.obj = service;
@@ -1046,7 +1058,7 @@
public void onServiceDisconnected(ComponentName className) {
// Called if we unexpected disconnected.
- if (DBG) Log.d(TAG, "BluetoothServiceConnection, disconnected: " +
+ if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " +
className.getClassName());
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
@@ -1054,7 +1066,7 @@
} else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
msg.arg1 = SERVICE_IBLUETOOTHGATT;
} else {
- Log.e(TAG, "Unknown service disconnected: " + className.getClassName());
+ Slog.e(TAG, "Unknown service disconnected: " + className.getClassName());
return;
}
mHandler.sendMessage(msg);
@@ -1070,11 +1082,11 @@
@Override
public void handleMessage(Message msg) {
- if (DBG) Log.d (TAG, "Message: " + msg.what);
+ if (DBG) Slog.d (TAG, "Message: " + msg.what);
switch (msg.what) {
case MESSAGE_ENABLE:
if (DBG) {
- Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
+ Slog.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
}
mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
mEnable = true;
@@ -1098,14 +1110,14 @@
{
IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
boolean added = mCallbacks.register(callback);
- Log.d(TAG,"Added callback: " + (callback == null? "null": callback) +":" +added );
+ Slog.d(TAG,"Added callback: " + (callback == null? "null": callback) +":" +added );
}
break;
case MESSAGE_UNREGISTER_ADAPTER:
{
IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
boolean removed = mCallbacks.unregister(callback);
- Log.d(TAG,"Removed callback: " + (callback == null? "null": callback) +":" + removed);
+ Slog.d(TAG,"Removed callback: " + (callback == null? "null": callback) +":" + removed);
break;
}
case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
@@ -1148,7 +1160,7 @@
}
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
{
- if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
+ if (DBG) Slog.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
IBinder service = (IBinder) msg.obj;
synchronized(mConnection) {
@@ -1169,17 +1181,26 @@
boolean enableHciSnoopLog = (Settings.Secure.getInt(mContentResolver,
Settings.Secure.BLUETOOTH_HCI_LOG, 0) == 1);
if (!mBluetooth.configHciSnoopLog(enableHciSnoopLog)) {
- Log.e(TAG,"IBluetooth.configHciSnoopLog return false");
+ Slog.e(TAG,"IBluetooth.configHciSnoopLog return false");
}
} catch (RemoteException e) {
- Log.e(TAG,"Unable to call configHciSnoopLog", e);
+ Slog.e(TAG,"Unable to call configHciSnoopLog", e);
+ }
+
+ if (!isNameAndAddressSet()) {
+ try {
+ storeNameAndAddress(mBluetooth.getName(),
+ mBluetooth.getAddress());
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to grab names", re);
+ }
}
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
} catch (RemoteException re) {
- Log.e(TAG, "Unable to register BluetoothCallback",re);
+ Slog.e(TAG, "Unable to register BluetoothCallback",re);
}
//Inform BluetoothAdapter instances that service is up
sendBluetoothServiceUpCallback();
@@ -1188,17 +1209,17 @@
try {
if (mQuietEnable == false) {
if(!mBluetooth.enable()) {
- Log.e(TAG,"IBluetooth.enable() returned false");
+ Slog.e(TAG,"IBluetooth.enable() returned false");
}
}
else
{
if(!mBluetooth.enableNoAutoConnect()) {
- Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+ Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
}
}
} catch (RemoteException e) {
- Log.e(TAG,"Unable to call enable()",e);
+ Slog.e(TAG,"Unable to call enable()",e);
}
}
@@ -1210,7 +1231,7 @@
break;
}
case MESSAGE_TIMEOUT_BIND: {
- Log.e(TAG, "MESSAGE_TIMEOUT_BIND");
+ Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
synchronized(mConnection) {
mBinding = false;
}
@@ -1220,7 +1241,7 @@
{
int prevState = msg.arg1;
int newState = msg.arg2;
- if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState);
+ if (DBG) Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState);
mState = newState;
bluetoothStateChangeHandler(prevState, newState);
// handle error state transition case from TURNING_ON to OFF
@@ -1239,7 +1260,7 @@
newState == BluetoothAdapter.STATE_BLE_ON) {
// bluetooth is working, reset the counter
if (mErrorRecoveryRetryCounter != 0) {
- Log.w(TAG, "bluetooth is recovered from error");
+ Slog.w(TAG, "bluetooth is recovered from error");
mErrorRecoveryRetryCounter = 0;
}
}
@@ -1247,7 +1268,7 @@
}
case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
{
- Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
+ Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
synchronized(mConnection) {
if (msg.arg1 == SERVICE_IBLUETOOTH) {
// if service is unbinded already, do nothing and return
@@ -1257,7 +1278,7 @@
mBluetoothGatt = null;
break;
} else {
- Log.e(TAG, "Bad msg.arg1: " + msg.arg1);
+ Slog.e(TAG, "Bad msg.arg1: " + msg.arg1);
break;
}
}
@@ -1292,7 +1313,7 @@
}
case MESSAGE_RESTART_BLUETOOTH_SERVICE:
{
- Log.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:"
+ Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:"
+" Restart IBluetooth service");
/* Enable without persisting the setting as
it doesnt change when IBluetooth
@@ -1304,19 +1325,17 @@
case MESSAGE_TIMEOUT_UNBIND:
{
- Log.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
+ Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
synchronized(mConnection) {
mUnbinding = false;
}
break;
}
- case MESSAGE_USER_SWITCHED:
- {
- if (DBG) {
- Log.d(TAG, "MESSAGE_USER_SWITCHED");
- }
+ case MESSAGE_USER_SWITCHED: {
+ if (DBG) Slog.d(TAG, "MESSAGE_USER_SWITCHED");
mHandler.removeMessages(MESSAGE_USER_SWITCHED);
+
/* disable and enable BT when detect a user switch */
if (mEnable && mBluetooth != null) {
synchronized (mConnection) {
@@ -1325,7 +1344,7 @@
try {
mBluetooth.unregisterCallback(mBluetoothCallback);
} catch (RemoteException re) {
- Log.e(TAG, "Unable to unregister",re);
+ Slog.e(TAG, "Unable to unregister",re);
}
}
}
@@ -1379,11 +1398,25 @@
// delay sending MESSAGE_USER_SWITCHED
mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
if (DBG) {
- Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
+ Slog.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
}
}
break;
}
+ case MESSAGE_USER_UNLOCKED: {
+ if (DBG) Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
+ mHandler.removeMessages(MESSAGE_USER_SWITCHED);
+
+ synchronized (mConnection) {
+ if (mEnable && !mBinding && (mBluetooth == null)) {
+ // We should be connected, but we gave up for some
+ // reason; maybe the Bluetooth service wasn't encryption
+ // aware, so try binding again.
+ if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock");
+ handleEnable(mQuietEnable);
+ }
+ }
+ }
}
}
}
@@ -1408,16 +1441,16 @@
try {
if (!mQuietEnable) {
if(!mBluetooth.enable()) {
- Log.e(TAG,"IBluetooth.enable() returned false");
+ Slog.e(TAG,"IBluetooth.enable() returned false");
}
}
else {
if(!mBluetooth.enableNoAutoConnect()) {
- Log.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+ Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
}
}
} catch (RemoteException e) {
- Log.e(TAG,"Unable to call enable()",e);
+ Slog.e(TAG,"Unable to call enable()",e);
}
}
}
@@ -1427,7 +1460,7 @@
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
intent.setComponent(comp);
if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {
- Log.e(TAG, "Fail to bind to: " + intent);
+ Slog.e(TAG, "Fail to bind to: " + intent);
return false;
}
return true;
@@ -1436,14 +1469,14 @@
private void handleDisable() {
synchronized(mConnection) {
if (mBluetooth != null) {
- if (DBG) Log.d(TAG,"Sending off request.");
+ if (DBG) Slog.d(TAG,"Sending off request.");
try {
if(!mBluetooth.disable()) {
- Log.e(TAG,"IBluetooth.disable() returned false");
+ Slog.e(TAG,"IBluetooth.disable() returned false");
}
} catch (RemoteException e) {
- Log.e(TAG,"Unable to call disable()",e);
+ Slog.e(TAG,"Unable to call disable()",e);
}
}
}
@@ -1466,7 +1499,7 @@
callingAppId == Process.NFC_UID ||
callingAppId == mSystemUiUid;
if (DBG) {
- Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
+ Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
+ " callingUser=" + callingUser
+ " parentUser=" + parentUser
+ " foregroundUser=" + foregroundUser);
@@ -1478,7 +1511,7 @@
}
private void sendBleStateChanged(int prevState, int newState) {
- if (DBG) Log.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState);
+ if (DBG) Slog.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState);
// Send broadcast message to everyone else
Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
@@ -1498,9 +1531,9 @@
if (newState == BluetoothAdapter.STATE_OFF) {
// If Bluetooth is off, send service down event to proxy objects, and unbind
- if (DBG) Log.d(TAG, "Bluetooth is complete turn off");
+ if (DBG) Slog.d(TAG, "Bluetooth is complete turn off");
if (canUnbindBluetoothService()) {
- if (DBG) Log.d(TAG, "Good to unbind!");
+ if (DBG) Slog.d(TAG, "Good to unbind!");
sendBluetoothServiceDownCallback();
unbindAndFinish();
sendBleStateChanged(prevState, newState);
@@ -1510,12 +1543,12 @@
} else if (!intermediate_off) {
// connect to GattService
- if (DBG) Log.d(TAG, "Bluetooth is in LE only mode");
+ if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode");
if (mBluetoothGatt != null) {
- if (DBG) Log.d(TAG, "Calling BluetoothGattServiceUp");
+ if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp");
onBluetoothGattServiceUp();
} else {
- if (DBG) Log.d(TAG, "Binding Bluetooth GATT service");
+ if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service");
if (mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_BLUETOOTH_LE)) {
Intent i = new Intent(IBluetoothGatt.class.getName());
@@ -1527,7 +1560,7 @@
isStandardBroadcast = false;
} else if (intermediate_off){
- if (DBG) Log.d(TAG, "Intermediate off, back to LE only mode");
+ if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode");
// For LE only mode, broadcast as is
sendBleStateChanged(prevState, newState);
sendBluetoothStateCallback(false); // BT is OFF for general users
@@ -1583,7 +1616,7 @@
if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
}
} catch (RemoteException e) {
- Log.e(TAG, "getState()", e);
+ Slog.e(TAG, "getState()", e);
break;
}
}
@@ -1594,7 +1627,7 @@
}
i++;
}
- Log.e(TAG,"waitForOnOff time out");
+ Slog.e(TAG,"waitForOnOff time out");
return false;
}
@@ -1619,21 +1652,21 @@
if (mHandler.hasMessages(MESSAGE_BLUETOOTH_STATE_CHANGE)) return false;
return (mBluetooth.getState() == BluetoothAdapter.STATE_OFF);
} catch (RemoteException e) {
- Log.e(TAG, "getState()", e);
+ Slog.e(TAG, "getState()", e);
}
}
return false;
}
private void recoverBluetoothServiceFromError() {
- Log.e(TAG,"recoverBluetoothServiceFromError");
+ Slog.e(TAG,"recoverBluetoothServiceFromError");
synchronized (mConnection) {
if (mBluetooth != null) {
//Unregister callback object
try {
mBluetooth.unregisterCallback(mBluetoothCallback);
} catch (RemoteException re) {
- Log.e(TAG, "Unable to unregister",re);
+ Slog.e(TAG, "Unable to unregister",re);
}
}
}
diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java
index 019d03d..1bf4e3a 100644
--- a/services/core/java/com/android/server/BluetoothService.java
+++ b/services/core/java/com/android/server/BluetoothService.java
@@ -18,10 +18,8 @@
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
-import android.util.Log;
class BluetoothService extends SystemService {
- private static final String TAG = "BluetoothService";
private BluetoothManagerService mBluetoothManagerService;
public BluetoothService(Context context) {
@@ -36,17 +34,20 @@
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
- Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
- publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService);
+ publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE,
+ mBluetoothManagerService);
} else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- Log.d(TAG, "onBootPhase: PHASE_ACTIVITY_MANAGER_READY");
mBluetoothManagerService.handleOnBootPhase();
}
}
@Override
public void onSwitchUser(int userHandle) {
- Log.d(TAG, "onSwitchUser: switching to user " + userHandle);
mBluetoothManagerService.handleOnSwitchUser(userHandle);
}
+
+ @Override
+ public void onUnlockUser(int userHandle) {
+ mBluetoothManagerService.handleOnUnlockUser(userHandle);
+ }
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 079b2f2..3c13577 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -164,7 +164,7 @@
implements PendingIntent.OnFinished {
private static final String TAG = "ConnectivityService";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
private static final boolean LOGD_RULES = false;
@@ -455,7 +455,7 @@
*/
private class LegacyTypeTracker {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
private static final String TAG = "CSLegacyTypeTracker";
@@ -3253,8 +3253,12 @@
}
int user = UserHandle.getUserId(Binder.getCallingUid());
synchronized(mVpns) {
- setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpns.get(user),
- profile));
+ Vpn vpn = mVpns.get(user);
+ if (vpn == null) {
+ Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
+ return false;
+ }
+ setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
}
} else {
setLockdownTracker(null);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 62fa7d5..7bf4b56 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -120,6 +120,7 @@
private AlarmManager mAlarmManager;
private IBatteryStats mBatteryStats;
private PowerManagerInternal mLocalPowerManager;
+ private AlarmManagerService.LocalService mLocalAlarmManager;
private INetworkPolicyManager mNetworkPolicyManager;
private DisplayManager mDisplayManager;
private SensorManager mSensorManager;
@@ -269,6 +270,17 @@
private int[] mPowerSaveWhitelistAllAppIdArray = new int[0];
/**
+ * App IDs that have been white-listed by the user to opt out of power save restrictions.
+ */
+ private final SparseBooleanArray mPowerSaveWhitelistUserAppIds = new SparseBooleanArray();
+
+ /**
+ * Current app IDs that are in the user power save white list. This array can
+ * be shared with others because it will not be modified once set.
+ */
+ private int[] mPowerSaveWhitelistUserAppIdArray = new int[0];
+
+ /**
* List of end times for UIDs that are temporarily marked as being allowed to access
* the network and acquire wakelocks. Times are in milliseconds.
*/
@@ -964,6 +976,10 @@
return getSystemPowerWhitelistInternal();
}
+ @Override public String[] getUserPowerWhitelist() {
+ return getUserPowerWhitelistInternal();
+ }
+
@Override public String[] getFullPowerWhitelistExceptIdle() {
return getFullPowerWhitelistExceptIdleInternal();
}
@@ -980,6 +996,10 @@
return getAppIdWhitelistInternal();
}
+ @Override public int[] getAppIdUserWhitelist() {
+ return getAppIdUserWhitelistInternal();
+ }
+
@Override public int[] getAppIdTempWhitelist() {
return getAppIdTempWhitelistInternal();
}
@@ -1161,6 +1181,7 @@
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mBatteryStats = BatteryStatsService.getService();
mLocalPowerManager = getLocalService(PowerManagerInternal.class);
+ mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class);
mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mDisplayManager = (DisplayManager) getContext().getSystemService(
@@ -1227,6 +1248,7 @@
getContext().registerReceiver(mReceiver, filter);
mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
+ mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
mDisplayManager.registerDisplayListener(mDisplayListener, null);
updateDisplayLocked();
@@ -1291,6 +1313,17 @@
}
}
+ public String[] getUserPowerWhitelistInternal() {
+ synchronized (this) {
+ int size = mPowerSaveWhitelistUserApps.size();
+ String[] apps = new String[size];
+ for (int i = 0; i < mPowerSaveWhitelistUserApps.size(); i++) {
+ apps[i] = mPowerSaveWhitelistUserApps.keyAt(i);
+ }
+ return apps;
+ }
+ }
+
public String[] getFullPowerWhitelistExceptIdleInternal() {
synchronized (this) {
int size = mPowerSaveWhitelistAppsExceptIdle.size() + mPowerSaveWhitelistUserApps.size();
@@ -1351,6 +1384,12 @@
}
}
+ public int[] getAppIdUserWhitelistInternal() {
+ synchronized (this) {
+ return mPowerSaveWhitelistUserAppIdArray;
+ }
+ }
+
public int[] getAppIdTempWhitelistInternal() {
synchronized (this) {
return mTempWhitelistAppIdArray;
@@ -1993,11 +2032,15 @@
private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps,
ArrayMap<String, Integer> userApps, SparseBooleanArray outAppIds) {
outAppIds.clear();
- for (int i=0; i<systemApps.size(); i++) {
- outAppIds.put(systemApps.valueAt(i), true);
+ if (systemApps != null) {
+ for (int i = 0; i < systemApps.size(); i++) {
+ outAppIds.put(systemApps.valueAt(i), true);
+ }
}
- for (int i=0; i<userApps.size(); i++) {
- outAppIds.put(userApps.valueAt(i), true);
+ if (userApps != null) {
+ for (int i = 0; i < userApps.size(); i++) {
+ outAppIds.put(userApps.valueAt(i), true);
+ }
}
int size = outAppIds.size();
int[] appids = new int[size];
@@ -2012,6 +2055,8 @@
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistExceptIdleAppIds);
mPowerSaveWhitelistAllAppIdArray = buildAppIdArray(mPowerSaveWhitelistApps,
mPowerSaveWhitelistUserApps, mPowerSaveWhitelistAllAppIds);
+ mPowerSaveWhitelistUserAppIdArray = buildAppIdArray(null,
+ mPowerSaveWhitelistUserApps, mPowerSaveWhitelistUserAppIds);
if (mLocalPowerManager != null) {
if (DEBUG) {
Slog.d(TAG, "Setting wakelock whitelist to "
@@ -2019,6 +2064,13 @@
}
mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
}
+ if (mLocalAlarmManager != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Setting alarm whitelist to "
+ + Arrays.toString(mPowerSaveWhitelistUserAppIdArray));
+ }
+ mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
+ }
}
private void updateTempWhitelistAppIdsLocked() {
@@ -2536,6 +2588,15 @@
pw.println();
}
}
+ size = mPowerSaveWhitelistUserAppIds.size();
+ if (size > 0) {
+ pw.println(" Whitelist user app ids:");
+ for (int i = 0; i < size; i++) {
+ pw.print(" ");
+ pw.print(mPowerSaveWhitelistUserAppIds.keyAt(i));
+ pw.println();
+ }
+ }
size = mPowerSaveWhitelistAllAppIds.size();
if (size > 0) {
pw.println(" Whitelist all app ids:");
diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java
index 9313148..8ca675a 100644
--- a/services/core/java/com/android/server/DiskStatsService.java
+++ b/services/core/java/com/android/server/DiskStatsService.java
@@ -80,7 +80,7 @@
reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw);
reportFreeSpace(new File("/system"), "System", pw);
- if (StorageManager.isNativeFileBasedEncryptionEnabled()) {
+ if (StorageManager.isFileEncryptedNativeOnly()) {
pw.println("File-based Encryption: true");
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 63c9822..9b8f2d2 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -39,6 +39,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.AppGlobals;
@@ -47,7 +48,6 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.SynchronousUserSwitchObserver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -465,7 +465,7 @@
super(handler);
}
- public void registerContentObserverLocked(int userId) {
+ public void registerContentObserverLocked(@UserIdInt int userId) {
if (mRegistered && mUserId == userId) {
return;
}
@@ -774,7 +774,16 @@
}
@Override
+ public void onSwitchUser(@UserIdInt int userHandle) {
+ // Called on the system server's main looper 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.
+ // TODO: Dispatch this to a worker thread as needed.
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
StatusBarManagerService statusBarService = (StatusBarManagerService) ServiceManager
.getService(Context.STATUS_BAR_SERVICE);
@@ -783,12 +792,14 @@
}
@Override
- public void onUnlockUser(int userHandle) {
+ 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(int userId) {
+ void onUnlockUser(@UserIdInt int userId) {
synchronized(mMethodMap) {
final int currentUserId = mSettings.getCurrentUserId();
if (DEBUG) {
@@ -804,6 +815,12 @@
}
}
+ void onSwitchUser(@UserIdInt int userId) {
+ synchronized (mMethodMap) {
+ switchUserLocked(userId);
+ }
+ }
+
public InputMethodManagerService(Context context) {
mIPackageManager = AppGlobals.getPackageManager();
mContext = context;
@@ -852,25 +869,6 @@
mNotificationShown = false;
int userId = 0;
try {
- ActivityManagerNative.getDefault().registerUserSwitchObserver(
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId)
- throws RemoteException {
- synchronized(mMethodMap) {
- switchUserLocked(newUserId);
- }
- }
-
- @Override
- public void onUserSwitchComplete(int newUserId) throws RemoteException {
- }
-
- @Override
- public void onForegroundProfileSwitch(int newProfileId) {
- // Ignore.
- }
- });
userId = ActivityManagerNative.getDefault().getCurrentUser().id;
} catch (RemoteException e) {
Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
@@ -1960,14 +1958,6 @@
throw new IllegalArgumentException("Unknown id: " + id);
}
- if (mCurClient != null && mCurAttribute != null) {
- // We have already made sure that the package name belongs to the application's UID.
- // No further UID check is required.
- if (SystemConfig.getInstance().getFixedImeApps().contains(mCurAttribute.packageName)) {
- return;
- }
- }
-
// See if we need to notify a subtype change within the same IME.
if (id.equals(mCurMethodId)) {
final int subtypeCount = info.getSubtypeCount();
@@ -3598,6 +3588,7 @@
private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue";
private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
+ private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
private final AtomicFile mAdditionalInputMethodSubtypeFile;
private final HashMap<String, InputMethodInfo> mMethodMap;
private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
@@ -3694,6 +3685,8 @@
out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
out.attribute(null, ATTR_IS_AUXILIARY,
String.valueOf(subtype.isAuxiliary() ? 1 : 0));
+ out.attribute(null, ATTR_IS_ASCII_CAPABLE,
+ String.valueOf(subtype.isAsciiCapable() ? 1 : 0));
out.endTag(null, NODE_SUBTYPE);
}
out.endTag(null, NODE_IMI);
@@ -3759,6 +3752,8 @@
parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE);
final boolean isAuxiliary = "1".equals(String.valueOf(
parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
+ final boolean isAsciiCapable = "1".equals(String.valueOf(
+ parser.getAttributeValue(null, ATTR_IS_ASCII_CAPABLE)));
final InputMethodSubtype subtype = new InputMethodSubtypeBuilder()
.setSubtypeNameResId(label)
.setSubtypeIconResId(icon)
@@ -3767,6 +3762,7 @@
.setSubtypeMode(imeSubtypeMode)
.setSubtypeExtraValue(imeSubtypeExtraValue)
.setIsAuxiliary(isAuxiliary)
+ .setIsAsciiCapable(isAsciiCapable)
.build();
tempSubtypesArray.add(subtype);
}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index a3322fc..4536e04 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -839,11 +839,11 @@
Slog.d(TAG, "Thinking about init, mSystemReady=" + mSystemReady
+ ", mDaemonConnected=" + mDaemonConnected);
if (mSystemReady && mDaemonConnected
- && !StorageManager.isNativeFileBasedEncryptionEnabled()) {
+ && !StorageManager.isFileEncryptedNativeOnly()) {
// When booting a device without native support, make sure that our
// user directories are locked or unlocked based on the current
// emulation status.
- final boolean initLocked = StorageManager.isEmulatedFileBasedEncryptionEnabled();
+ final boolean initLocked = StorageManager.isFileEncryptedEmulatedOnly();
Slog.d(TAG, "Setting up emulation state, initlocked=" + initLocked);
final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
for (UserInfo user : users) {
@@ -1940,7 +1940,7 @@
waitForReady();
if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) {
- if (StorageManager.isNativeFileBasedEncryptionEnabled()) {
+ if (StorageManager.isFileEncryptedNativeOnly()) {
throw new IllegalStateException(
"Emulation not available on device with native FBE");
}
@@ -2811,7 +2811,7 @@
@Override
public boolean isUserKeyUnlocked(int userId) {
- if (StorageManager.isFileBasedEncryptionEnabled()) {
+ if (StorageManager.isFileEncryptedNativeOrEmulated()) {
synchronized (mLock) {
return ArrayUtils.contains(mLocalUnlockedUsers, userId);
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 799d0bd..329f716 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -22,8 +22,10 @@
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST;
@@ -43,7 +45,6 @@
import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsListResult;
import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
-
import android.annotation.NonNull;
import android.app.ActivityManagerNative;
import android.content.Context;
@@ -226,6 +227,12 @@
*/
@GuardedBy("mQuotaLock")
private SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
+ /**
+ * Set of UIDs that are to be blocked/allowed by firewall controller. This set of Ids matches
+ * to device on power-save mode.
+ */
+ @GuardedBy("mQuotaLock")
+ private SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
/** Set of states for the child firewall chains. True if the chain is active. */
@GuardedBy("mQuotaLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
@@ -286,8 +293,8 @@
Watchdog.getInstance().addMonitor(this);
}
- static NetworkManagementService create(Context context,
- String socket) throws InterruptedException {
+ static NetworkManagementService create(Context context, String socket)
+ throws InterruptedException {
final NetworkManagementService service = new NetworkManagementService(context, socket);
final CountDownLatch connectedSignal = service.mConnectedSignal;
if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
@@ -303,8 +310,15 @@
}
public void systemReady() {
- prepareNativeDaemon();
- if (DBG) Slog.d(TAG, "Prepared");
+ if (DBG) {
+ final long start = System.currentTimeMillis();
+ prepareNativeDaemon();
+ final long delta = System.currentTimeMillis() - start;
+ Slog.d(TAG, "Prepared in " + delta + "ms");
+ return;
+ } else {
+ prepareNativeDaemon();
+ }
}
private IBatteryStats getBatteryStats() {
@@ -339,8 +353,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -358,8 +371,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -376,8 +388,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).interfaceAdded(iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -399,8 +410,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).interfaceRemoved(iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -417,8 +427,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).limitReached(limitName, iface);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -476,8 +485,7 @@
try {
mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
Integer.toString(type), isActive, tsNanos);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -520,7 +528,7 @@
Log.wtf(TAG, "problem enabling bandwidth controls", e);
}
} else {
- Slog.d(TAG, "not enabling bandwidth control");
+ Slog.i(TAG, "not enabling bandwidth control");
}
SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
@@ -543,7 +551,7 @@
synchronized (mQuotaLock) {
int size = mActiveQuotas.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active quota rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active quota rules");
final HashMap<String, Long> activeQuotas = mActiveQuotas;
mActiveQuotas = Maps.newHashMap();
for (Map.Entry<String, Long> entry : activeQuotas.entrySet()) {
@@ -553,7 +561,7 @@
size = mActiveAlerts.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active alert rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active alert rules");
final HashMap<String, Long> activeAlerts = mActiveAlerts;
mActiveAlerts = Maps.newHashMap();
for (Map.Entry<String, Long> entry : activeAlerts.entrySet()) {
@@ -563,7 +571,7 @@
size = mUidRejectOnQuota.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active UID rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active UID rules");
final SparseBooleanArray uidRejectOnQuota = mUidRejectOnQuota;
mUidRejectOnQuota = new SparseBooleanArray();
for (int i = 0; i < uidRejectOnQuota.size(); i++) {
@@ -573,7 +581,7 @@
size = mUidCleartextPolicy.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active UID cleartext policies");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active UID cleartext policies");
final SparseIntArray local = mUidCleartextPolicy;
mUidCleartextPolicy = new SparseIntArray();
for (int i = 0; i < local.size(); i++) {
@@ -585,7 +593,7 @@
size = mUidFirewallRules.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active firewall UID rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall UID rules");
final SparseIntArray uidFirewallRules = mUidFirewallRules;
mUidFirewallRules = new SparseIntArray();
for (int i = 0; i < uidFirewallRules.size(); i++) {
@@ -596,7 +604,7 @@
size = mUidFirewallStandbyRules.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active firewall standby UID rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall standby UID rules");
final SparseIntArray uidFirewallRules = mUidFirewallStandbyRules;
mUidFirewallStandbyRules = new SparseIntArray();
for (int i = 0; i < uidFirewallRules.size(); i++) {
@@ -610,7 +618,7 @@
size = mUidFirewallDozableRules.size();
if (size > 0) {
- Slog.d(TAG, "Pushing " + size + " active firewall dozable UID rules");
+ if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall dozable UID rules");
final SparseIntArray uidFirewallRules = mUidFirewallDozableRules;
mUidFirewallDozableRules = new SparseIntArray();
for (int i = 0; i < uidFirewallRules.size(); i++) {
@@ -621,6 +629,20 @@
if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)) {
setFirewallChainEnabled(FIREWALL_CHAIN_DOZABLE, true);
}
+
+ size = mUidFirewallPowerSaveRules.size();
+ if (size > 0) {
+ Slog.d(TAG, "Pushing " + size + " active firewall powersave UID rules");
+ final SparseIntArray uidFirewallRules = mUidFirewallPowerSaveRules;
+ mUidFirewallPowerSaveRules = new SparseIntArray();
+ for (int i = 0; i < uidFirewallRules.size(); i++) {
+ setFirewallUidRuleInternal(FIREWALL_CHAIN_POWERSAVE, uidFirewallRules.keyAt(i),
+ uidFirewallRules.valueAt(i));
+ }
+ }
+ if (mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE)) {
+ setFirewallChainEnabled(FIREWALL_CHAIN_POWERSAVE, true);
+ }
}
}
@@ -633,8 +655,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).addressUpdated(iface, address);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -651,8 +672,7 @@
for (int i = 0; i < length; i++) {
try {
mObservers.getBroadcastItem(i).addressRemoved(iface, address);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -670,8 +690,7 @@
try {
mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime,
addresses);
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -692,8 +711,7 @@
} else {
mObservers.getBroadcastItem(i).routeRemoved(route);
}
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -1210,7 +1228,7 @@
// TODO: remove from aidl if nobody calls externally
mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
- Slog.d(TAG, "Shutting down");
+ Slog.i(TAG, "Shutting down");
}
@Override
@@ -2023,6 +2041,9 @@
case FIREWALL_CHAIN_DOZABLE:
chainName = FIREWALL_CHAIN_NAME_DOZABLE;
break;
+ case FIREWALL_CHAIN_POWERSAVE:
+ chainName = FIREWALL_CHAIN_NAME_POWERSAVE;
+ break;
default:
throw new IllegalArgumentException("Bad child chain: " + chain);
}
@@ -2039,6 +2060,8 @@
return FIREWALL_TYPE_BLACKLIST;
case FIREWALL_CHAIN_DOZABLE:
return FIREWALL_TYPE_WHITELIST;
+ case FIREWALL_CHAIN_POWERSAVE:
+ return FIREWALL_TYPE_WHITELIST;
default:
return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST;
}
@@ -2138,6 +2161,8 @@
return mUidFirewallStandbyRules;
case FIREWALL_CHAIN_DOZABLE:
return mUidFirewallDozableRules;
+ case FIREWALL_CHAIN_POWERSAVE:
+ return mUidFirewallPowerSaveRules;
case FIREWALL_CHAIN_NONE:
return mUidFirewallRules;
default:
@@ -2151,6 +2176,8 @@
return FIREWALL_CHAIN_NAME_STANDBY;
case FIREWALL_CHAIN_DOZABLE:
return FIREWALL_CHAIN_NAME_DOZABLE;
+ case FIREWALL_CHAIN_POWERSAVE:
+ return FIREWALL_CHAIN_NAME_POWERSAVE;
case FIREWALL_CHAIN_NONE:
return FIREWALL_CHAIN_NAME_NONE;
default:
@@ -2225,8 +2252,7 @@
for (int i = 0; i < length; i++) {
try {
mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
- } catch (RemoteException e) {
- } catch (RuntimeException e) {
+ } catch (RemoteException | RuntimeException e) {
}
}
} finally {
@@ -2271,43 +2297,25 @@
}
synchronized (mUidFirewallRules) {
- pw.print("UID firewall rule: [");
- final int size = mUidFirewallRules.size();
- for (int i = 0; i < size; i++) {
- pw.print(mUidFirewallRules.keyAt(i));
- pw.print(":");
- pw.print(mUidFirewallRules.valueAt(i));
- if (i < size - 1) pw.print(",");
- }
- pw.println("]");
+ dumpUidFirewallRule(pw, "", mUidFirewallRules);
}
pw.println("UID firewall standby chain enabled: " +
mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY));
synchronized (mUidFirewallStandbyRules) {
- pw.print("UID firewall standby rule: [");
- final int size = mUidFirewallStandbyRules.size();
- for (int i = 0; i < size; i++) {
- pw.print(mUidFirewallStandbyRules.keyAt(i));
- pw.print(":");
- pw.print(mUidFirewallStandbyRules.valueAt(i));
- if (i < size - 1) pw.print(",");
- }
- pw.println("]");
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_STANDBY, mUidFirewallStandbyRules);
}
pw.println("UID firewall dozable chain enabled: " +
mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE));
synchronized (mUidFirewallDozableRules) {
- pw.print("UID firewall dozable rule: [");
- final int size = mUidFirewallDozableRules.size();
- for (int i = 0; i < size; i++) {
- pw.print(mUidFirewallDozableRules.keyAt(i));
- pw.print(":");
- pw.print(mUidFirewallDozableRules.valueAt(i));
- if (i < size - 1) pw.print(",");
- }
- pw.println("]");
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_DOZABLE, mUidFirewallDozableRules);
+ }
+
+ pw.println("UID firewall powersave chain enabled: " +
+ mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE));
+ synchronized (mUidFirewallPowerSaveRules) {
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_POWERSAVE, mUidFirewallPowerSaveRules);
}
synchronized (mIdleTimerLock) {
@@ -2324,6 +2332,20 @@
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
}
+ private void dumpUidFirewallRule(PrintWriter pw, String name, SparseIntArray rules) {
+ pw.print("UID firewall");
+ pw.print(name);
+ pw.print(" rule: [");
+ final int size = rules.size();
+ for (int i = 0; i < size; i++) {
+ pw.print(rules.keyAt(i));
+ pw.print(":");
+ pw.print(rules.valueAt(i));
+ if (i < size - 1) pw.print(",");
+ }
+ pw.println("]");
+ }
+
@Override
public void createPhysicalNetwork(int netId, String permission) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index f4c6225..11aef17 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -58,7 +58,7 @@
private static final String TAG = "NsdService";
private static final String MDNS_TAG = "mDnsConnector";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private Context mContext;
private ContentResolver mContentResolver;
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 1c1784e..30e0ceb 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -105,9 +105,6 @@
// background while in data-usage save mode, as read from the configuration files.
final ArraySet<String> mAllowInDataUsageSave = new ArraySet<>();
- // These are the app package names that should not allow IME switching.
- final ArraySet<String> mFixedImeApps = new ArraySet<>();
-
// These are the package names of apps which should be in the 'always'
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
@@ -159,10 +156,6 @@
return mAllowInDataUsageSave;
}
- public ArraySet<String> getFixedImeApps() {
- return mFixedImeApps;
- }
-
public ArraySet<String> getLinkedApps() {
return mLinkedApps;
}
@@ -411,17 +404,6 @@
XmlUtils.skipCurrentTag(parser);
continue;
- } else if ("fixed-ime-app".equals(name) && allowAll) {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at "
- + parser.getPositionDescription());
- } else {
- mFixedImeApps.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
-
} else if ("app-link".equals(name) && allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
@@ -464,7 +446,7 @@
// Some devices can be field-converted to FBE, so offer to splice in
// those features if not already defined by the static config
- if (StorageManager.isNativeFileBasedEncryptionEnabled()) {
+ if (StorageManager.isFileEncryptedNativeOnly()) {
addFeature(PackageManager.FEATURE_FILE_BASED_ENCRYPTION, 0);
addFeature(PackageManager.FEATURE_SECURELY_REMOVES_USERS, 0);
}
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index c4b4cbe..306e933 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -27,9 +27,10 @@
import org.xmlpull.v1.XmlPullParserException;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
-import android.app.SynchronousUserSwitchObserver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -76,16 +77,72 @@
private final Context mContext;
private boolean mSystemReady;
private final TextServicesMonitor mMonitor;
- private final HashMap<String, SpellCheckerInfo> mSpellCheckerMap =
- new HashMap<String, SpellCheckerInfo>();
- private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<SpellCheckerInfo>();
- private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups =
- new HashMap<String, SpellCheckerBindGroup>();
+ private final HashMap<String, SpellCheckerInfo> mSpellCheckerMap = new HashMap<>();
+ private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<>();
+ private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = new HashMap<>();
private final TextServicesSettings mSettings;
+ @NonNull
+ private final UserManager mUserManager;
- public void systemRunning() {
- if (!mSystemReady) {
- mSystemReady = true;
+ public static final class Lifecycle extends SystemService {
+ private TextServicesManagerService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mService = new TextServicesManagerService(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.TEXT_SERVICES_MANAGER_SERVICE, mService);
+ }
+
+ @Override
+ public void onSwitchUser(@UserIdInt int userHandle) {
+ // Called on the system server's main looper 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.
+ // TODO: Dispatch this to a worker thread as needed.
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mService.systemRunning();
+ }
+ }
+
+ @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);
+ }
+ }
+
+ void systemRunning() {
+ synchronized (mSpellCheckerMap) {
+ if (!mSystemReady) {
+ mSystemReady = true;
+ resetInternalState(mSettings.getCurrentUserId());
+ }
+ }
+ }
+
+ void onSwitchUser(@UserIdInt int userId) {
+ synchronized (mSpellCheckerMap) {
+ resetInternalState(userId);
+ }
+ }
+
+ void onUnlockUser(@UserIdInt int userId) {
+ synchronized(mSpellCheckerMap) {
+ final int currentUserId = mSettings.getCurrentUserId();
+ if (userId != currentUserId) {
+ return;
+ }
+ resetInternalState(currentUserId);
}
}
@@ -93,6 +150,8 @@
mSystemReady = false;
mContext = context;
+ mUserManager = mContext.getSystemService(UserManager.class);
+
final IntentFilter broadcastFilter = new IntentFilter();
broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -100,38 +159,25 @@
int userId = UserHandle.USER_SYSTEM;
try {
- ActivityManagerNative.getDefault().registerUserSwitchObserver(
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) throws RemoteException {
- synchronized(mSpellCheckerMap) {
- switchUserLocked(newUserId);
- }
- }
-
- @Override
- public void onUserSwitchComplete(int newUserId) throws RemoteException {
- }
-
- @Override
- public void onForegroundProfileSwitch(int newProfileId) {
- // Ignore.
- }
- });
userId = ActivityManagerNative.getDefault().getCurrentUser().id;
} catch (RemoteException e) {
Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
}
mMonitor = new TextServicesMonitor();
mMonitor.register(context, null, true);
- mSettings = new TextServicesSettings(context.getContentResolver(), userId);
+ final boolean useCopyOnWriteSettings =
+ !mSystemReady || !mUserManager.isUserUnlocked(userId);
+ mSettings = new TextServicesSettings(context.getContentResolver(), userId,
+ useCopyOnWriteSettings);
- // "switchUserLocked" initializes the states for the foreground user
- switchUserLocked(userId);
+ // "resetInternalState" initializes the states for the foreground user
+ resetInternalState(userId);
}
- private void switchUserLocked(int userId) {
- mSettings.setCurrentUserId(userId);
+ private void resetInternalState(@UserIdInt int userId) {
+ final boolean useCopyOnWriteSettings =
+ !mSystemReady || !mUserManager.isUserUnlocked(userId);
+ mSettings.switchCurrentUser(userId, useCopyOnWriteSettings);
updateCurrentProfileIds();
unbindServiceLocked();
buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings);
@@ -148,8 +194,7 @@
}
void updateCurrentProfileIds() {
- List<UserInfo> profiles =
- UserManager.get(mContext).getProfiles(mSettings.getCurrentUserId());
+ final List<UserInfo> profiles = mUserManager.getProfiles(mSettings.getCurrentUserId());
int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
for (int i = 0; i < currentProfileIds.length; i++) {
currentProfileIds[i] = profiles.get(i).id;
@@ -214,6 +259,9 @@
list.clear();
map.clear();
final PackageManager pm = context.getPackageManager();
+ // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default
+ // behavior of PackageManager is exactly what we want. It by default picks up appropriate
+ // services depending on the unlock state for the specified user.
final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA,
settings.getCurrentUserId());
@@ -615,8 +663,7 @@
Slog.d(TAG, "FinishSpellCheckerService");
}
synchronized(mSpellCheckerMap) {
- final ArrayList<SpellCheckerBindGroup> removeList =
- new ArrayList<SpellCheckerBindGroup>();
+ final ArrayList<SpellCheckerBindGroup> removeList = new ArrayList<>();
for (SpellCheckerBindGroup group : mSpellCheckerBindGroups.values()) {
if (group == null) continue;
// Use removeList to avoid modifying mSpellCheckerBindGroups in this loop.
@@ -757,50 +804,36 @@
synchronized(mSpellCheckerMap) {
pw.println("Current Text Services Manager state:");
- pw.println(" Spell Checker Map:");
- for (Map.Entry<String, SpellCheckerInfo> ent : mSpellCheckerMap.entrySet()) {
- pw.print(" "); pw.print(ent.getKey()); pw.println(":");
- SpellCheckerInfo info = ent.getValue();
- pw.print(" "); pw.print("id="); pw.println(info.getId());
- pw.print(" "); pw.print("comp=");
- pw.println(info.getComponent().toShortString());
- int NS = info.getSubtypeCount();
- for (int i=0; i<NS; i++) {
- SpellCheckerSubtype st = info.getSubtypeAt(i);
- pw.print(" "); pw.print("Subtype #"); pw.print(i); pw.println(":");
- pw.print(" "); pw.print("locale="); pw.println(st.getLocale());
- pw.print(" "); pw.print("extraValue=");
- pw.println(st.getExtraValue());
- }
+ pw.println(" Spell Checkers:");
+ int spellCheckerIndex = 0;
+ for (final SpellCheckerInfo info : mSpellCheckerMap.values()) {
+ pw.println(" Spell Checker #" + spellCheckerIndex);
+ info.dump(pw, " ");
+ ++spellCheckerIndex;
}
pw.println("");
pw.println(" Spell Checker Bind Groups:");
- for (Map.Entry<String, SpellCheckerBindGroup> ent
+ for (final Map.Entry<String, SpellCheckerBindGroup> ent
: mSpellCheckerBindGroups.entrySet()) {
- SpellCheckerBindGroup grp = ent.getValue();
- pw.print(" "); pw.print(ent.getKey()); pw.print(" ");
- pw.print(grp); pw.println(":");
- pw.print(" "); pw.print("mInternalConnection=");
- pw.println(grp.mInternalConnection);
- pw.print(" "); pw.print("mSpellChecker=");
- pw.println(grp.mSpellChecker);
- pw.print(" "); pw.print("mBound="); pw.print(grp.mBound);
- pw.print(" mConnected="); pw.println(grp.mConnected);
- int NL = grp.mListeners.size();
- for (int i=0; i<NL; i++) {
- InternalDeathRecipient listener = grp.mListeners.get(i);
- pw.print(" "); pw.print("Listener #"); pw.print(i); pw.println(":");
- pw.print(" "); pw.print("mTsListener=");
- pw.println(listener.mTsListener);
- pw.print(" "); pw.print("mScListener=");
- pw.println(listener.mScListener);
- pw.print(" "); pw.print("mGroup=");
- pw.println(listener.mGroup);
- pw.print(" "); pw.print("mScLocale=");
- pw.print(listener.mScLocale);
- pw.print(" mUid="); pw.println(listener.mUid);
+ final SpellCheckerBindGroup grp = ent.getValue();
+ pw.println(" " + ent.getKey() + " " + grp + ":");
+ pw.println(" " + "mInternalConnection=" + grp.mInternalConnection);
+ pw.println(" " + "mSpellChecker=" + grp.mSpellChecker);
+ pw.println(" " + "mBound=" + grp.mBound + " mConnected=" + grp.mConnected);
+ final int N = grp.mListeners.size();
+ for (int i = 0; i < N; i++) {
+ final InternalDeathRecipient listener = grp.mListeners.get(i);
+ pw.println(" " + "Listener #" + i + ":");
+ pw.println(" " + "mTsListener=" + listener.mTsListener);
+ pw.println(" " + "mScListener=" + listener.mScListener);
+ pw.println(" " + "mGroup=" + listener.mGroup);
+ pw.println(" " + "mScLocale=" + listener.mScLocale
+ + " mUid=" + listener.mUid);
}
}
+ pw.println("");
+ pw.println(" mSettings:");
+ mSettings.dumpLocked(pw, " ");
}
}
@@ -811,7 +844,7 @@
private final String TAG = SpellCheckerBindGroup.class.getSimpleName();
private final InternalServiceConnection mInternalConnection;
private final CopyOnWriteArrayList<InternalDeathRecipient> mListeners =
- new CopyOnWriteArrayList<InternalDeathRecipient>();
+ new CopyOnWriteArrayList<>();
public boolean mBound;
public ISpellCheckerService mSpellChecker;
public boolean mConnected;
@@ -885,8 +918,7 @@
}
synchronized(mSpellCheckerMap) {
final int size = mListeners.size();
- final ArrayList<InternalDeathRecipient> removeList =
- new ArrayList<InternalDeathRecipient>();
+ final ArrayList<InternalDeathRecipient> removeList = new ArrayList<>();
for (int i = 0; i < size; ++i) {
final InternalDeathRecipient tempRecipient = mListeners.get(i);
if(tempRecipient.hasSpellCheckerListener(listener)) {
@@ -1015,23 +1047,85 @@
private static class TextServicesSettings {
private final ContentResolver mResolver;
+ @UserIdInt
private int mCurrentUserId;
@GuardedBy("mLock")
private int[] mCurrentProfileIds = new int[0];
private Object mLock = new Object();
- public TextServicesSettings(ContentResolver resolver, int userId) {
+ /**
+ * On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}.
+ */
+ private final HashMap<String, String> mCopyOnWriteDataStore = new HashMap<>();
+ private boolean mCopyOnWrite = false;
+
+ public TextServicesSettings(ContentResolver resolver, @UserIdInt int userId,
+ boolean copyOnWrite) {
mResolver = resolver;
- mCurrentUserId = userId;
+ switchCurrentUser(userId, copyOnWrite);
}
- public void setCurrentUserId(int userId) {
+ /**
+ * Must be called when the current user is changed.
+ *
+ * @param userId The user ID.
+ * @param copyOnWrite If {@code true}, for each settings key
+ * (e.g. {@link Settings.Secure#SELECTED_SPELL_CHECKER}) we use the actual settings on the
+ * {@link Settings.Secure} until we do the first write operation.
+ */
+ public void switchCurrentUser(@UserIdInt int userId, boolean copyOnWrite) {
if (DBG) {
Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to "
+ userId + ", new ime = " + getSelectedSpellChecker());
}
+ if (mCurrentUserId != userId || mCopyOnWrite != copyOnWrite) {
+ mCopyOnWriteDataStore.clear();
+ // TODO: mCurrentProfileIds should be cleared here.
+ }
// TSMS settings are kept per user, so keep track of current user
mCurrentUserId = userId;
+ mCopyOnWrite = copyOnWrite;
+ // TODO: mCurrentProfileIds should be updated here.
+ }
+
+ private void putString(final String key, final String str) {
+ if (mCopyOnWrite) {
+ mCopyOnWriteDataStore.put(key, str);
+ } else {
+ Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId);
+ }
+ }
+
+ private String getString(final String key) {
+ if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) {
+ final String result = mCopyOnWriteDataStore.get(key);
+ return result != null ? result : "";
+ }
+ return Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId);
+ }
+
+ private void putInt(final String key, final int value) {
+ if (mCopyOnWrite) {
+ mCopyOnWriteDataStore.put(key, String.valueOf(value));
+ } else {
+ Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId);
+ }
+ }
+
+ private int getInt(final String key, final int defaultValue) {
+ if (mCopyOnWrite && mCopyOnWriteDataStore.containsKey(key)) {
+ final String result = mCopyOnWriteDataStore.get(key);
+ return result != null ? Integer.valueOf(result) : 0;
+ }
+ return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId);
+ }
+
+ private void putBoolean(final String key, final boolean value) {
+ putInt(key, value ? 1 : 0);
+ }
+
+ private boolean getBoolean(final String key, final boolean defaultValue) {
+ return getInt(key, defaultValue ? 1 : 0) == 1;
}
public void setCurrentProfileIds(int[] currentProfileIds) {
@@ -1040,7 +1134,7 @@
}
}
- public boolean isCurrentProfile(int userId) {
+ public boolean isCurrentProfile(@UserIdInt int userId) {
synchronized (mLock) {
if (userId == mCurrentUserId) return true;
for (int i = 0; i < mCurrentProfileIds.length; i++) {
@@ -1050,39 +1144,39 @@
}
}
+ @UserIdInt
public int getCurrentUserId() {
return mCurrentUserId;
}
public void putSelectedSpellChecker(String sciId) {
- Settings.Secure.putStringForUser(mResolver,
- Settings.Secure.SELECTED_SPELL_CHECKER, sciId, mCurrentUserId);
+ putString(Settings.Secure.SELECTED_SPELL_CHECKER, sciId);
}
public void putSelectedSpellCheckerSubtype(int hashCode) {
- Settings.Secure.putStringForUser(mResolver,
- Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode),
- mCurrentUserId);
+ putString(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode));
}
public void setSpellCheckerEnabled(boolean enabled) {
- Settings.Secure.putIntForUser(mResolver,
- Settings.Secure.SPELL_CHECKER_ENABLED, enabled ? 1 : 0, mCurrentUserId);
+ putBoolean(Settings.Secure.SPELL_CHECKER_ENABLED, enabled);
}
public String getSelectedSpellChecker() {
- return Settings.Secure.getStringForUser(mResolver,
- Settings.Secure.SELECTED_SPELL_CHECKER, mCurrentUserId);
+ return getString(Settings.Secure.SELECTED_SPELL_CHECKER);
}
public String getSelectedSpellCheckerSubtype() {
- return Settings.Secure.getStringForUser(mResolver,
- Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, mCurrentUserId);
+ return getString(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE);
}
public boolean isSpellCheckerEnabled() {
- return Settings.Secure.getIntForUser(mResolver,
- Settings.Secure.SPELL_CHECKER_ENABLED, 1, mCurrentUserId) == 1;
+ return getBoolean(Settings.Secure.SPELL_CHECKER_ENABLED, true);
+ }
+
+ public void dumpLocked(final PrintWriter pw, final String prefix) {
+ pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
+ pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds));
+ pw.println(prefix + "mCopyOnWrite=" + mCopyOnWrite);
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 772a15c..4198af9 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -64,9 +64,11 @@
// Which native processes to dump into dropbox's stack traces
public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
+ "/system/bin/audioserver",
"/system/bin/mediaserver",
"/system/bin/sdcard",
- "/system/bin/surfaceflinger"
+ "/system/bin/surfaceflinger",
+ "media.log"
};
static Watchdog sWatchdog;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a63faf1..d4dd505 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -47,6 +47,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -2460,31 +2461,12 @@
public void onResult(Bundle result) {
mNumResults++;
Intent intent = null;
-
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
- /*
- * The Authenticator API allows third party authenticators to
- * supply arbitrary intents to other apps that they can run,
- * this can be very bad when those apps are in the system like
- * the System Settings.
- */
- int authenticatorUid = Binder.getCallingUid();
- long bid = Binder.clearCallingIdentity();
- try {
- PackageManager pm = mContext.getPackageManager();
- ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
- int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
- if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid,
- targetUid)) {
- throw new SecurityException("Activity to be started with KEY_INTENT must "
- + "share Authenticator's signatures");
- }
- } finally {
- Binder.restoreCallingIdentity(bid);
- }
+ checkKeyIntent(
+ Binder.getCallingUid(),
+ intent);
}
-
IAccountManagerResponse response;
if (mExpectActivityLaunch && result != null
&& result.containsKey(AccountManager.KEY_INTENT)) {
@@ -3569,6 +3551,36 @@
return response;
}
+ /**
+ * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
+ * security policy.
+ *
+ * In particular we want to make sure that the Authenticator doesn't try to trick users
+ * into launching aribtrary intents on the device via by tricking to click authenticator
+ * supplied entries in the system Settings app.
+ */
+ protected void checkKeyIntent(
+ int authUid,
+ Intent intent) throws SecurityException {
+ long bid = Binder.clearCallingIdentity();
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
+ ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
+ int targetUid = targetActivityInfo.applicationInfo.uid;
+ if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid, targetUid)) {
+ String pkgName = targetActivityInfo.packageName;
+ String activityName = targetActivityInfo.name;
+ String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
+ + "does not share a signature with the supplying authenticator (%s).";
+ throw new SecurityException(
+ String.format(tmpl, activityName, pkgName, mAccountType));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(bid);
+ }
+ }
+
private void close() {
synchronized (mSessions) {
if (mSessions.remove(toString()) == null) {
@@ -3711,27 +3723,9 @@
}
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
- /*
- * The Authenticator API allows third party authenticators to
- * supply arbitrary intents to other apps that they can run,
- * this can be very bad when those apps are in the system like
- * the System Settings.
- */
- int authenticatorUid = Binder.getCallingUid();
- long bid = Binder.clearCallingIdentity();
- try {
- PackageManager pm = mContext.getPackageManager();
- ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
- int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
- if (PackageManager.SIGNATURE_MATCH !=
- pm.checkSignatures(authenticatorUid, targetUid)) {
- throw new SecurityException(
- "Activity to be started with KEY_INTENT must " +
- "share Authenticator's signatures");
- }
- } finally {
- Binder.restoreCallingIdentity(bid);
- }
+ checkKeyIntent(
+ Binder.getCallingUid(),
+ intent);
}
if (result != null
&& !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8c0ec78..63a0e87 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -319,7 +319,7 @@
+ " (pid=" + Binder.getCallingPid()
+ ") when starting service " + service);
}
- callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
} else {
callerFg = true;
}
@@ -831,7 +831,7 @@
"BIND_TREAT_LIKE_ACTIVITY");
}
- final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
ServiceLookupResult res =
@@ -1138,7 +1138,7 @@
for (int i=b.apps.size()-1; i>=0; i--) {
ProcessRecord client = b.apps.valueAt(i).client;
if (client != null && client.setSchedGroup
- != Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ != ProcessList.SCHED_GROUP_BACKGROUND) {
inFg = true;
break;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3330a82..95dbd0f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -101,6 +101,8 @@
import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.IDevicePolicyManager;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.IBackupManager;
@@ -350,6 +352,11 @@
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
+import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
+import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -535,9 +542,11 @@
final ActivityStarter mActivityStarter;
/** Task stack change listeners. */
- private RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
+ private final RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
new RemoteCallbackList<ITaskStackListener>();
+ final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter();
+
public IntentFirewall mIntentFirewall;
// Whether we should show our dialogs (ANR, crash, etc) or just perform their
@@ -1897,7 +1906,9 @@
case SYSTEM_USER_UNLOCK_MSG: {
final int userId = msg.arg1;
mSystemServiceManager.unlockUser(userId);
- mRecentTasks.loadUserRecentsLocked(userId);
+ synchronized (ActivityManagerService.this) {
+ mRecentTasks.loadUserRecentsLocked(userId);
+ }
if (userId == UserHandle.USER_SYSTEM) {
startPersistentApps(PackageManager.MATCH_ENCRYPTION_UNAWARE);
}
@@ -2154,15 +2165,20 @@
} break;
case VR_MODE_CHANGE_MSG: {
VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
- final boolean vrMode = msg.arg1 != 0;
- vrService.setVrMode(vrMode);
-
- if (mInVrMode != vrMode) {
- synchronized (ActivityManagerService.this) {
+ final ActivityRecord r = (ActivityRecord) msg.obj;
+ boolean vrMode;
+ ComponentName requestedPackage;
+ int userId;
+ synchronized (ActivityManagerService.this) {
+ vrMode = r.requestedVrComponent != null;
+ requestedPackage = r.requestedVrComponent;
+ userId = r.userId;
+ if (mInVrMode != vrMode) {
mInVrMode = vrMode;
mShowDialogs = shouldShowDialogs(mConfiguration, mInVrMode);
}
}
+ vrService.setVrMode(vrMode, requestedPackage, userId);
} break;
}
}
@@ -2935,7 +2951,7 @@
final void applyUpdateVrModeLocked(ActivityRecord r) {
mHandler.sendMessage(
- mHandler.obtainMessage(VR_MODE_CHANGE_MSG, (r.isVrActivity) ? 1 : 0, 0));
+ mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
}
final void showAskCompatModeDialogLocked(ActivityRecord r) {
@@ -3251,9 +3267,9 @@
boolean isNextTransitionForward() {
int transit = mWindowManager.getPendingAppTransition();
- return transit == AppTransition.TRANSIT_ACTIVITY_OPEN
- || transit == AppTransition.TRANSIT_TASK_OPEN
- || transit == AppTransition.TRANSIT_TASK_TO_FRONT;
+ return transit == TRANSIT_ACTIVITY_OPEN
+ || transit == TRANSIT_TASK_OPEN
+ || transit == TRANSIT_TASK_TO_FRONT;
}
int startIsolatedProcess(String entryPoint, String[] entryPointArgs,
@@ -4374,7 +4390,7 @@
AppGlobals.getPackageManager().queryIntentActivities(
intent, r.resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId()).getList();
// Look for the original activity in the list...
final int N = resolves != null ? resolves.size() : 0;
@@ -5261,13 +5277,17 @@
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
- if (packageName != null && packageName.equals(mDeviceOwnerName)) {
- throw new SecurityException("Clearing DeviceOwner data is forbidden.");
- }
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
userId = mUserController.handleIncomingUser(pid, uid, userId, false,
ALLOW_FULL_ONLY, "clearApplicationUserData", null);
+
+ final DevicePolicyManagerInternal dpmi = LocalServices
+ .getService(DevicePolicyManagerInternal.class);
+ if (dpmi != null && dpmi.hasDeviceOwnerOrProfileOwner(packageName, userId)) {
+ throw new SecurityException("Cannot clear data for a device owner or a profile owner");
+ }
+
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -5327,6 +5347,7 @@
Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
Uri.fromParts("package", packageName, null));
intent.putExtra(Intent.EXTRA_UID, pkgUid);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(pkgUid));
broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
null, null, 0, null, null, null, null, false, false, userId);
} catch (RemoteException e) {
@@ -6229,7 +6250,7 @@
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = ProcessList.INVALID_ADJ;
- app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.forcingToForeground = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
@@ -6287,9 +6308,10 @@
// If the app is being launched for restore or full backup, set it up specially
boolean isRestrictedBackupMode = false;
if (mBackupTarget != null && mBackupAppName.equals(processName)) {
- isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
- || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
- || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
+ isRestrictedBackupMode = mBackupTarget.appInfo.uid >= Process.FIRST_APPLICATION_UID
+ && ((mBackupTarget.backupMode == BackupRecord.RESTORE)
+ || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
+ || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
}
notifyPackageUse(app.instrumentationInfo != null
@@ -6475,15 +6497,13 @@
}
@Override
- public void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade) {
+ public void keyguardGoingAway(int flags) {
enforceNotIsolatedCaller("keyguardGoingAway");
final long token = Binder.clearCallingIdentity();
try {
synchronized (this) {
if (DEBUG_LOCKSCREEN) logLockScreen("");
- mWindowManager.keyguardGoingAway(disableWindowAnimations,
- keyguardGoingToNotificationShade);
+ mWindowManager.keyguardGoingAway(flags);
if (mLockScreenShown == LOCK_SCREEN_SHOWN) {
mLockScreenShown = LOCK_SCREEN_HIDDEN;
updateSleepIfNeededLocked();
@@ -6643,7 +6663,7 @@
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
- ActivityRecord.activityResumedLocked(token);
+ stack.activityResumedLocked(token);
}
}
Binder.restoreCallingIdentity(origId);
@@ -9086,7 +9106,8 @@
preserveWindow = false;
}
- mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode, preserveWindow);
+ mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode, preserveWindow,
+ false /* deferResume */);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -9151,7 +9172,7 @@
throw new IllegalArgumentException("Expected in-place ActivityOption " +
"with valid animation");
}
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_IN_PLACE, false);
+ mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
mWindowManager.overridePendingAppTransitionInPlace(opts.getPackageName(),
opts.getCustomInPlaceResId());
mWindowManager.executeAppTransition();
@@ -9220,7 +9241,7 @@
// Kill the running processes.
for (int i = 0; i < procsToKill.size(); i++) {
ProcessRecord pr = procsToKill.get(i);
- if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
&& pr.curReceiver == null) {
pr.kill("remove task", true);
} else {
@@ -9530,6 +9551,55 @@
}
}
+ @Override
+ public void swapDockedAndFullscreenStack() throws RemoteException {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "swapDockedAndFullscreenStack()");
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ final ActivityStack fullscreenStack = mStackSupervisor.getStack(
+ FULLSCREEN_WORKSPACE_STACK_ID);
+ final TaskRecord topTask = fullscreenStack != null ? fullscreenStack.topTask()
+ : null;
+ final ActivityStack dockedStack = mStackSupervisor.getStack(DOCKED_STACK_ID);
+ final ArrayList<TaskRecord> tasks = dockedStack != null ? dockedStack.getAllTasks()
+ : null;
+ if (topTask == null || tasks == null || tasks.size() == 0) {
+ Slog.w(TAG,
+ "Unable to swap tasks, either docked or fullscreen stack is empty.");
+ return;
+ }
+
+ // TODO: App transition
+ mWindowManager.prepareAppTransition(TRANSIT_ACTIVITY_RELAUNCH, false);
+
+ // Defer the resume so resume/pausing while moving stacks is dangerous.
+ mStackSupervisor.moveTaskToStackLocked(topTask.taskId, DOCKED_STACK_ID,
+ false /* toTop */, !FORCE_FOCUS, "swapDockedAndFullscreenStack",
+ ANIMATE, true /* deferResume */);
+ final int size = tasks.size();
+ for (int i = 0; i < size; i++) {
+ final int id = tasks.get(i).taskId;
+ if (id == topTask.taskId) {
+ continue;
+ }
+ mStackSupervisor.moveTaskToStackLocked(id,
+ FULLSCREEN_WORKSPACE_STACK_ID, true /* toTop */, !FORCE_FOCUS,
+ "swapDockedAndFullscreenStack", ANIMATE, true /* deferResume */);
+ }
+
+ // Because we deferred the resume, to avoid conflicts with stack switches while
+ // resuming, we need to do it after all the tasks are moved.
+ mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
+
+ mWindowManager.executeAppTransition();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
/**
* Moves the input task to the docked stack.
*
@@ -9901,11 +9971,11 @@
private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
List<ProviderInfo> providers = null;
try {
- ParceledListSlice<ProviderInfo> slice = AppGlobals.getPackageManager()
+ providers = AppGlobals.getPackageManager()
.queryContentProviders(app.processName, app.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
- | MATCH_DEBUG_TRIAGED_MISSING);
- providers = slice != null ? slice.getList() : null;
+ | MATCH_DEBUG_TRIAGED_MISSING)
+ .getList();
} catch (RemoteException ex) {
}
if (DEBUG_MU) Slog.v(TAG_MU,
@@ -10495,7 +10565,7 @@
cpi.packageName, r.userId)) {
final boolean callerForeground = r != null ? r.setSchedGroup
- != Process.THREAD_GROUP_BG_NONINTERACTIVE : true;
+ != ProcessList.SCHED_GROUP_BACKGROUND : true;
// Show a permission review UI only for starting from a foreground app
if (!callerForeground) {
@@ -10856,7 +10926,7 @@
synchronized (this) {
try {
final List<ApplicationInfo> apps = AppGlobals.getPackageManager()
- .getPersistentApplications(STOCK_PM_FLAGS | matchFlags);
+ .getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
for (ApplicationInfo app : apps) {
if (!"android".equals(app.packageName)) {
addAppLocked(app, false, null /* ABI override */);
@@ -10872,7 +10942,7 @@
* belonging to any running apps.
*/
private void installEncryptionUnawareProviders(int userId) {
- if (!StorageManager.isFileBasedEncryptionEnabled()) {
+ if (!StorageManager.isFileEncryptedNativeOrEmulated()) {
// TODO: eventually pivot this back to look at current user state,
// similar to the comment in UserManager.isUserUnlocked(), but for
// now, if we started apps when "unlocked" then unaware providers
@@ -11126,7 +11196,6 @@
}
void finishRunningVoiceLocked() {
- Slog.d(TAG, "finishRunningVoiceLocked() >>>>");
if (mRunningVoice != null) {
mRunningVoice = null;
mVoiceWakeLock.release();
@@ -12023,25 +12092,51 @@
}
@Override
- public void setVrMode(IBinder token, boolean enabled) {
+ public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
throw new UnsupportedOperationException("VR mode not supported on this device!");
}
+ final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+
+ ActivityRecord r;
+ synchronized (this) {
+ r = ActivityRecord.isInStackLocked(token);
+ }
+
+ if (r == null) {
+ throw new IllegalArgumentException();
+ }
+
+ int err;
+ if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
+ VrManagerInternal.NO_ERROR) {
+ return err;
+ }
+
synchronized(this) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- throw new IllegalArgumentException();
- }
- r.isVrActivity = enabled;
+ r.requestedVrComponent = (enabled) ? packageName : null;
// Update associated state if this activity is currently focused
if (r == mFocusedActivity) {
applyUpdateVrModeLocked(r);
}
+ return 0;
}
}
+ @Override
+ public boolean isVrModePackageEnabled(ComponentName packageName) {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+ throw new UnsupportedOperationException("VR mode not supported on this device!");
+ }
+
+ final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+
+ return vrService.hasVrPackage(packageName, UserHandle.getCallingUserId()) ==
+ VrManagerInternal.NO_ERROR;
+ }
+
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("startActivity");
synchronized (this) {
@@ -12607,7 +12702,7 @@
List<ResolveInfo> ris = null;
try {
ris = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, null, MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ intent, null, MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).getList();
} catch (RemoteException e) {
}
if (ris == null) {
@@ -14812,13 +14907,13 @@
String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
char schedGroup;
switch (r.setSchedGroup) {
- case Process.THREAD_GROUP_BG_NONINTERACTIVE:
+ case ProcessList.SCHED_GROUP_BACKGROUND:
schedGroup = 'B';
break;
- case Process.THREAD_GROUP_DEFAULT:
+ case ProcessList.SCHED_GROUP_DEFAULT:
schedGroup = 'F';
break;
- case Process.THREAD_GROUP_TOP_APP:
+ case ProcessList.SCHED_GROUP_TOP_APP:
schedGroup = 'T';
break;
default:
@@ -16956,7 +17051,7 @@
continue;
}
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
- .queryIntentReceivers(intent, resolvedType, pmFlags, user);
+ .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
// If this is not the system user, we need to check for
// any receivers that should be filtered out.
@@ -17756,17 +17851,17 @@
} catch (RemoteException e) {
}
if (ii == null) {
- reportStartInstrumentationFailure(watcher, className,
+ reportStartInstrumentationFailureLocked(watcher, className,
"Unable to find instrumentation info for: " + className);
return false;
}
if (ai == null) {
- reportStartInstrumentationFailure(watcher, className,
+ reportStartInstrumentationFailureLocked(watcher, className,
"Unable to find instrumentation target package: " + ii.targetPackage);
return false;
}
if (!ai.hasCode()) {
- reportStartInstrumentationFailure(watcher, className,
+ reportStartInstrumentationFailureLocked(watcher, className,
"Instrumentation target has no code: " + ii.targetPackage);
return false;
}
@@ -17781,7 +17876,7 @@
+ " not allowed because package " + ii.packageName
+ " does not have a signature matching the target "
+ ii.targetPackage;
- reportStartInstrumentationFailure(watcher, className, msg);
+ reportStartInstrumentationFailureLocked(watcher, className, msg);
throw new SecurityException(msg);
}
@@ -17812,31 +17907,21 @@
* @param cn The component name of the instrumentation.
* @param report The error report.
*/
- private void reportStartInstrumentationFailure(IInstrumentationWatcher watcher,
+ private void reportStartInstrumentationFailureLocked(IInstrumentationWatcher watcher,
ComponentName cn, String report) {
Slog.w(TAG, report);
- try {
- if (watcher != null) {
- Bundle results = new Bundle();
- results.putString(Instrumentation.REPORT_KEY_IDENTIFIER, "ActivityManagerService");
- results.putString("Error", report);
- watcher.instrumentationStatus(cn, -1, results);
- }
- } catch (RemoteException e) {
- Slog.w(TAG, e);
+ if (watcher != null) {
+ Bundle results = new Bundle();
+ results.putString(Instrumentation.REPORT_KEY_IDENTIFIER, "ActivityManagerService");
+ results.putString("Error", report);
+ mInstrumentationReporter.reportStatus(watcher, cn, -1, results);
}
}
void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) {
if (app.instrumentationWatcher != null) {
- try {
- // NOTE: IInstrumentationWatcher *must* be oneway here
- app.instrumentationWatcher.instrumentationFinished(
- app.instrumentationClass,
- resultCode,
- results);
- } catch (RemoteException e) {
- }
+ mInstrumentationReporter.reportFinished(app.instrumentationWatcher,
+ app.instrumentationClass, resultCode, results);
}
// Can't call out of the system process with a lock held, so post a message.
@@ -18374,7 +18459,7 @@
if (app.thread == null) {
app.adjSeq = mAdjSeq;
- app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
}
@@ -18394,7 +18479,7 @@
app.adjSeq = mAdjSeq;
app.curRawAdj = app.maxAdj;
app.foregroundActivities = false;
- app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
// System processes can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
@@ -18431,14 +18516,14 @@
if (app == TOP_APP) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_TOP_APP;
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
app.adjType = "top-activity";
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
} else if (app.instrumentationClass != null) {
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.adjType = "instrumentation";
procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
} else if ((queue = isReceivingBroadcast(app)) != null) {
@@ -18448,7 +18533,7 @@
// broadcast as reflected by which queue it's active in.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = (queue == mFgBroadcastQueue)
- ? Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
app.adjType = "broadcast";
procState = ActivityManager.PROCESS_STATE_RECEIVER;
} else if (app.executingServices.size() > 0) {
@@ -18456,13 +18541,13 @@
// counts as being in the foreground.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = app.execServicesFg ?
- Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
app.adjType = "exec-service";
procState = ActivityManager.PROCESS_STATE_SERVICE;
//Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
} else {
// As far as we know the process is empty. We may change our mind later.
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
// At this point we don't actually know the adjustment. Use the cached adj
// value that the caller wants us to.
adj = cachedAdj;
@@ -18491,7 +18576,7 @@
if (procState > PROCESS_STATE_CUR_TOP) {
procState = PROCESS_STATE_CUR_TOP;
}
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.empty = false;
foregroundActivities = true;
@@ -18510,7 +18595,7 @@
if (procState > PROCESS_STATE_CUR_TOP) {
procState = PROCESS_STATE_CUR_TOP;
}
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.empty = false;
foregroundActivities = true;
@@ -18554,7 +18639,7 @@
procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
app.cached = false;
app.adjType = "fg-service";
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
} else if (app.forcingToForeground != null) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
@@ -18562,7 +18647,7 @@
app.cached = false;
app.adjType = "force-fg";
app.adjSource = app.forcingToForeground;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
@@ -18570,7 +18655,7 @@
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.cached = false;
app.adjType = "heavy";
}
@@ -18584,7 +18669,7 @@
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
adj = ProcessList.HOME_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.cached = false;
app.adjType = "home";
}
@@ -18599,7 +18684,7 @@
// We want to try to keep it around more aggressively, to give
// a good experience around switching between two apps.
adj = ProcessList.PREVIOUS_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.cached = false;
app.adjType = "previous";
}
@@ -18639,7 +18724,7 @@
for (int is = app.services.size()-1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
is--) {
ServiceRecord s = app.services.valueAt(is);
@@ -18677,13 +18762,13 @@
}
for (int conni = s.connections.size()-1;
conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
conni--) {
ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
for (int i = 0;
i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
i++) {
// XXX should compute this based on the max of
@@ -18754,7 +18839,6 @@
&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
&& adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
} else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = clientAdj;
} else {
@@ -18776,7 +18860,7 @@
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
schedGroup = client.curSchedGroup;
} else {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) {
@@ -18846,9 +18930,9 @@
adj = ProcessList.FOREGROUND_APP_ADJ;
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
- schedGroup = Process.THREAD_GROUP_TOP_APP;
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
} else {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
app.cached = false;
@@ -18866,13 +18950,13 @@
for (int provi = app.pubProviders.size()-1;
provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
provi--) {
ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
for (int i = cpr.connections.size()-1;
i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
- || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > ActivityManager.PROCESS_STATE_TOP);
i--) {
ContentProviderConnection conn = cpr.connections.get(i);
@@ -18930,7 +19014,7 @@
procState = clientProcState;
}
if (client.curSchedGroup > schedGroup) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
// If the provider has external (non-framework) process
@@ -18939,7 +19023,7 @@
if (cpr.hasExternalProcessHandles()) {
if (adj > ProcessList.FOREGROUND_APP_ADJ) {
adj = ProcessList.FOREGROUND_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.adjType = "provider";
app.adjTarget = cpr.name;
@@ -18953,7 +19037,7 @@
if (app.lastProviderTime > 0 && (app.lastProviderTime+CONTENT_PROVIDER_RETAIN_TIME) > now) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
adj = ProcessList.PREVIOUS_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.cached = false;
app.adjType = "provider";
}
@@ -19032,7 +19116,7 @@
if (adj > app.maxAdj) {
adj = app.maxAdj;
if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
- schedGroup = Process.THREAD_GROUP_DEFAULT;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
@@ -19448,17 +19532,29 @@
if (app.setSchedGroup != app.curSchedGroup) {
app.setSchedGroup = app.curSchedGroup;
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Setting process group of " + app.processName
+ "Setting sched group of " + app.processName
+ " to " + app.curSchedGroup);
if (app.waitingToKill != null && app.curReceiver == null
- && app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) {
app.kill(app.waitingToKill, true);
success = false;
} else {
+ int processGroup;
+ switch (app.curSchedGroup) {
+ case ProcessList.SCHED_GROUP_BACKGROUND:
+ processGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ break;
+ case ProcessList.SCHED_GROUP_TOP_APP:
+ 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, app.curSchedGroup);
+ Process.setProcessGroup(app.pid, processGroup);
} catch (Exception e) {
Slog.w(TAG, "Failed setting process group of " + app.pid
+ " to " + app.curSchedGroup);
@@ -19469,7 +19565,7 @@
} else {
if (app.thread != null) {
try {
- app.thread.setSchedulingGroup(app.curSchedGroup);
+ app.thread.setSchedulingGroup(processGroup);
} catch (RemoteException e) {
}
}
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 0e6dd28..43e1bdf 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -61,7 +61,7 @@
mLastLogTimeSecs = now;
ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
- if (stack != null && stack.getStackVisibilityLocked() != STACK_INVISIBLE) {
+ if (stack != null && stack.getStackVisibilityLocked(null) != STACK_INVISIBLE) {
mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
return;
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index e430dad..5219827 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -123,7 +123,7 @@
final boolean stateNotNeeded; // As per ActivityInfo.flags
boolean fullscreen; // covers the full screen?
final boolean noDisplay; // activity is not displayed?
- final boolean componentSpecified; // did caller specifiy an explicit component?
+ final boolean componentSpecified; // did caller specify an explicit component?
final boolean rootVoiceInteraction; // was this the root activity of a voice interaction?
static final int APPLICATION_ACTIVITY_TYPE = 0;
@@ -190,7 +190,7 @@
boolean forceNewConfig; // force re-create with new config next time
int launchCount; // count of launches since last state
long lastLaunchTime; // time of last launch of this activity
- boolean isVrActivity; // is the activity running in VR mode?
+ ComponentName requestedVrComponent; // the requested component for handling VR mode.
ArrayList<ActivityContainer> mChildContainers = new ArrayList<>();
String stringName; // for caching of toString().
@@ -354,7 +354,11 @@
pw.print(" forceNewConfig="); pw.println(forceNewConfig);
pw.print(prefix); pw.print("mActivityType=");
pw.println(activityTypeToString(mActivityType));
- pw.print(prefix); pw.print("vrMode="); pw.println(isVrActivity);
+ if (requestedVrComponent != null) {
+ pw.print(prefix);
+ pw.print("requestedVrComponent=");
+ pw.println(requestedVrComponent);
+ }
if (displayStartTime != 0 || startTime != 0) {
pw.print(prefix); pw.print("displayStartTime=");
if (displayStartTime == 0) pw.print("0");
@@ -718,7 +722,6 @@
}
immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
- isVrActivity = (aInfo.flags & ActivityInfo.FLAG_ENABLE_VR_MODE) != 0;
} else {
realActivity = null;
taskAffinity = null;
@@ -730,7 +733,6 @@
noDisplay = false;
mActivityType = APPLICATION_ACTIVITY_TYPE;
immersive = false;
- isVrActivity = false;
}
}
@@ -1275,13 +1277,6 @@
}
}
- static void activityResumedLocked(IBinder token) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (DEBUG_SAVED_STATE) Slog.i(TAG_STATES, "Resumed activity; dropping state of: " + r);
- r.icicle = null;
- r.haveState = false;
- }
-
static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 74c8363..09542cb 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
@@ -66,7 +67,6 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -415,6 +415,12 @@
mTaskPositioner.reset();
}
mWindowManager.detachStack(mStackId);
+ if (mStackId == DOCKED_STACK_ID) {
+ // If we removed a docked stack we want to resize it so it resizes all other stacks
+ // in the system to fullscreen.
+ mStackSupervisor.resizeDockedStackLocked(
+ null, null, null, null, null, PRESERVE_WINDOWS);
+ }
}
public void getDisplaySize(Point out) {
@@ -1089,6 +1095,13 @@
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
+ final void activityResumedLocked(IBinder token) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (DEBUG_SAVED_STATE) Slog.i(TAG_STATES, "Resumed activity; dropping state of: " + r);
+ r.icicle = null;
+ r.haveState = false;
+ }
+
final void activityStoppedLocked(ActivityRecord r, Bundle icicle,
PersistableBundle persistentState, CharSequence description) {
if (r.state != ActivityState.STOPPING) {
@@ -1115,7 +1128,7 @@
r.stopped = true;
r.state = ActivityState.STOPPED;
- mWindowManager.notifyAppStopped(r.appToken);
+ mWindowManager.notifyAppStopped(r.appToken, true);
if (getVisibleBehindActivity() == r) {
mStackSupervisor.requestVisibleBehindLocked(r, false);
@@ -1368,7 +1381,7 @@
for (int i = stacks.size() - 1; i >= 0; --i) {
ActivityStack stack = stacks.get(i);
if (stack != this && stack.isFocusable()
- && stack.getStackVisibilityLocked() != STACK_INVISIBLE) {
+ && stack.getStackVisibilityLocked(null) != STACK_INVISIBLE) {
return stack;
}
}
@@ -1387,22 +1400,42 @@
return false;
}
- private boolean hasTranslucentActivity(ActivityStack stack) {
- final ArrayList<TaskRecord> tasks = stack.getAllTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- final TaskRecord task = tasks.get(taskNdx);
+ /**
+ * Returns true if the stack is translucent and can have other contents visible behind it if
+ * needed. A stack is considered translucent if it don't contain a visible or
+ * starting (about to be visible) activity that is fullscreen (opaque).
+ * @param starting The currently starting activity or null if there is none.
+ * @param stackBehindId The id of the stack directly behind this one.
+ */
+ private boolean isStackTranslucent(ActivityRecord starting, int stackBehindId) {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
- // Conditions for an activity to obscure the stack we're
- // examining:
- // 1. Not Finishing AND Visible AND:
- // 2. Either:
- // - Full Screen Activity OR
- // - On top of Home and our stack is NOT home
- if (!r.finishing && r.visible && (r.fullscreen ||
- (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {
+ if (r.finishing) {
+ // We don't factor in finishing activities when determining translucency since
+ // they will be gone soon.
+ continue;
+ }
+
+ if (!r.visible && r != starting) {
+ // Also ignore invisible activities that are not the currently starting
+ // activity (about to be visible).
+ continue;
+ }
+
+ if (r.fullscreen) {
+ // Stack isn't translucent if it has at least one fullscreen activity
+ // that is visible.
+ return false;
+ }
+
+ if (!isHomeStack() && r.frontOfTask
+ && task.isOverHomeStack() && stackBehindId != HOME_STACK_ID) {
+ // Stack isn't translucent if it's top activity should have the home stack
+ // behind it and the stack currently behind it isn't the home stack.
return false;
}
}
@@ -1413,8 +1446,9 @@
/**
* Returns stack's visibility: {@link #STACK_INVISIBLE}, {@link #STACK_VISIBLE} or
* {@link #STACK_VISIBLE_ACTIVITY_BEHIND}.
+ * @param starting The currently starting activity or null if there is none.
*/
- int getStackVisibilityLocked() {
+ int getStackVisibilityLocked(ActivityRecord starting) {
if (!isAttached()) {
return STACK_INVISIBLE;
}
@@ -1457,30 +1491,33 @@
: STACK_INVISIBLE;
}
- // Find the first stack below focused stack that actually got something visible.
- int belowFocusedIndex = mStacks.indexOf(focusedStack) - 1;
- while (belowFocusedIndex >= 0 &&
- mStacks.get(belowFocusedIndex).topRunningActivityLocked() == null) {
- belowFocusedIndex--;
+ // Find the first stack behind focused stack that actually got something visible.
+ int stackBehindFocusedIndex = mStacks.indexOf(focusedStack) - 1;
+ while (stackBehindFocusedIndex >= 0 &&
+ mStacks.get(stackBehindFocusedIndex).topRunningActivityLocked() == null) {
+ stackBehindFocusedIndex--;
}
if ((focusedStackId == DOCKED_STACK_ID || focusedStackId == PINNED_STACK_ID)
- && stackIndex == belowFocusedIndex) {
+ && stackIndex == stackBehindFocusedIndex) {
// Stacks directly behind the docked or pinned stack are always visible.
return STACK_VISIBLE;
}
+ final int stackBehindFocusedId = (stackBehindFocusedIndex >= 0)
+ ? mStacks.get(stackBehindFocusedIndex).mStackId : INVALID_STACK_ID;
+
if (focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
- && hasTranslucentActivity(focusedStack)) {
+ && focusedStack.isStackTranslucent(starting, stackBehindFocusedId)) {
// Stacks behind the fullscreen stack with a translucent activity are always
// visible so they can act as a backdrop to the translucent activity.
// For example, dialog activities
- if (stackIndex == belowFocusedIndex) {
+ if (stackIndex == stackBehindFocusedIndex) {
return STACK_VISIBLE;
}
- if (belowFocusedIndex >= 0) {
- final ActivityStack stack = mStacks.get(belowFocusedIndex);
- if ((stack.mStackId == DOCKED_STACK_ID || stack.mStackId == PINNED_STACK_ID)
- && stackIndex == (belowFocusedIndex - 1)) {
+ if (stackBehindFocusedIndex >= 0) {
+ if ((stackBehindFocusedId == DOCKED_STACK_ID
+ || stackBehindFocusedId == PINNED_STACK_ID)
+ && stackIndex == (stackBehindFocusedIndex - 1)) {
// The stack behind the docked or pinned stack is also visible so we can have a
// complete backdrop to the translucent activity when the docked stack is up.
return STACK_VISIBLE;
@@ -1505,7 +1542,7 @@
return STACK_INVISIBLE;
}
- if (!hasTranslucentActivity(stack)) {
+ if (!stack.isStackTranslucent(starting, INVALID_STACK_ID)) {
return STACK_INVISIBLE;
}
}
@@ -1543,7 +1580,7 @@
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
boolean aboveTop = top != null;
- final int stackVisibility = getStackVisibilityLocked();
+ final int stackVisibility = getStackVisibilityLocked(starting);
final boolean stackInvisible = stackVisibility != STACK_VISIBLE;
final boolean stackVisibleBehind = stackVisibility == STACK_VISIBLE_ACTIVITY_BEHIND;
boolean behindFullscreenActivity = stackInvisible;
@@ -1772,6 +1809,12 @@
r.app.pendingUiClean = true;
r.app.thread.scheduleWindowVisibility(r.appToken, true);
r.stopFreezingScreenLocked(false);
+
+ // The activity may be waiting for stop, but that is no longer
+ // appropriate for it.
+ mStackSupervisor.mStoppingActivities.remove(r);
+ mStackSupervisor.mGoingToSleepActivities.remove(r);
+ mStackSupervisor.mWaitingVisibleActivities.remove(r);
} catch (Exception e) {
// Just skip on any failure; we'll make it
// visible when it next restarts.
@@ -2247,6 +2290,10 @@
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);
+
EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
System.identityHashCode(next), next.task.taskId, next.shortComponentName);
@@ -3802,7 +3849,8 @@
void releaseBackgroundResources(ActivityRecord r) {
if (hasVisibleBehindActivity() &&
!mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) {
- if (r == topRunningActivityLocked() && getStackVisibilityLocked() == STACK_VISIBLE) {
+ if (r == topRunningActivityLocked()
+ && getStackVisibilityLocked(null) == STACK_VISIBLE) {
// Don't release the top activity if it has requested to run behind the next
// activity and the stack is currently visible.
return;
@@ -4426,10 +4474,10 @@
"Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + r
+ " callers=" + Debug.getCallers(6));
r.forceNewConfig = false;
+ mStackSupervisor.activityRelaunchingLocked(r);
r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
!andResume, new Configuration(mService.mConfiguration),
new Configuration(r.task.mOverrideConfig), preserveWindow);
- mStackSupervisor.activityRelaunchingLocked(r);
// Note: don't need to call pauseIfSleepingLocked() here, because
// the caller will only pass in 'andResume' if this activity is
// currently resumed, which implies we aren't sleeping.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 48f31f9..d364d85 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2062,7 +2062,8 @@
}
}
- boolean resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
+ boolean resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow,
+ boolean deferResume) {
if (!task.isResizeable()) {
Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
return true;
@@ -2105,10 +2106,14 @@
if (r != null) {
final ActivityStack stack = task.stack;
kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
- // All other activities must be made visible with their correct configuration.
- ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
- if (!kept) {
- resumeFocusedStackTopActivityLocked();
+
+ if (!deferResume) {
+
+ // All other activities must be made visible with their correct configuration.
+ ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
+ if (!kept) {
+ resumeFocusedStackTopActivityLocked();
+ }
}
}
}
@@ -2247,6 +2252,12 @@
boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
String reason, boolean animate) {
+ return moveTaskToStackLocked(taskId, stackId, toTop, forceFocus, reason, animate,
+ false /* deferResume */);
+ }
+
+ boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
+ String reason, boolean animate, boolean deferResume) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
@@ -2297,16 +2308,19 @@
// Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
- kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow);
+ kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
+ !mightReplaceWindow, deferResume);
} else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
Rect bounds = task.getLaunchBounds();
if (bounds == null) {
stack.layoutTaskInStack(task, null);
bounds = task.mBounds;
}
- kept = resizeTaskLocked(task, bounds, RESIZE_MODE_FORCED, !mightReplaceWindow);
+ kept = resizeTaskLocked(task, bounds, RESIZE_MODE_FORCED, !mightReplaceWindow,
+ deferResume);
} else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
- kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow);
+ kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
+ !mightReplaceWindow, deferResume);
}
} finally {
mWindowManager.continueSurfaceLayout();
@@ -2319,10 +2333,13 @@
mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken, !kept);
}
- // The task might have already been running and its visibility needs to be synchronized with
- // the visibility of the stack / windows.
- ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
- resumeFocusedStackTopActivityLocked();
+ if (!deferResume) {
+
+ // The task might have already been running and its visibility needs to be synchronized with
+ // the visibility of the stack / windows.
+ ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
+ resumeFocusedStackTopActivityLocked();
+ }
showNonResizeableDockToastIfNeeded(task, preferredLaunchStackId, stackId);
@@ -2372,6 +2389,11 @@
if (task.mActivities.size() == 1) {
// There is only one activity in the task. So, we can just move the task over to
// the stack without re-parenting the activity in a different task.
+ if (task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
+ // Move the home stack forward if the task we just moved to the pinned stack
+ // was launched from home so home should be visible behind it.
+ moveHomeStackToFront(reason);
+ }
moveTaskToStackLocked(
task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS, reason, !ANIMATE);
} else {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 46389e2..83ad2a7 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1757,7 +1757,7 @@
// and if yes, we will launch into that stack. If not, we just put the new
// activity into parent's stack, because we can't find a better place.
final ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
- if (stack != null && stack.getStackVisibilityLocked() == STACK_INVISIBLE) {
+ if (stack != null && stack.getStackVisibilityLocked(r) == STACK_INVISIBLE) {
// There is a docked stack, but it isn't visible, so we can't launch into that.
return null;
} else {
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index 86cdbcc..ddfab4d 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -25,6 +25,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.text.BidiFormatter;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
@@ -68,18 +69,21 @@
mProc = data.proc;
mResult = data.result;
mRepeating = data.repeating;
+ BidiFormatter bidi = BidiFormatter.getInstance();
+
if ((mProc.pkgList.size() == 1) &&
(mName = context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
setTitle(res.getString(
mRepeating ? com.android.internal.R.string.aerr_application_repeated
: com.android.internal.R.string.aerr_application,
- mName.toString(), mProc.info.processName));
+ bidi.unicodeWrap(mName.toString()),
+ bidi.unicodeWrap(mProc.info.processName)));
} else {
mName = mProc.processName;
setTitle(res.getString(
mRepeating ? com.android.internal.R.string.aerr_process_repeated
: com.android.internal.R.string.aerr_process,
- mName.toString()));
+ bidi.unicodeWrap(mName.toString())));
}
setCancelable(false);
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index 6d1d9f3..c6befd7 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -27,6 +27,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.text.BidiFormatter;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
@@ -84,9 +85,11 @@
}
}
+ BidiFormatter bidi = BidiFormatter.getInstance();
+
setTitle(name2 != null
- ? res.getString(resid, name1.toString(), name2.toString())
- : res.getString(resid, name1.toString()));
+ ? res.getString(resid, bidi.unicodeWrap(name1.toString()), bidi.unicodeWrap(name2.toString()))
+ : res.getString(resid, bidi.unicodeWrap(name1.toString())));
if (aboveSystem) {
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 28882de..71a1f97 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -32,6 +32,7 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFormatException;
+import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
@@ -39,6 +40,9 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.os.health.HealthStatsParceler;
+import android.os.health.HealthStatsWriter;
+import android.os.health.UidHealthStats;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.SignalStrength;
@@ -65,7 +69,9 @@
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.List;
+import java.util.Map;
/**
* All information we are collecting about things that can happen that impact
@@ -1419,4 +1425,88 @@
}
}
}
+
+ /**
+ * Gets a snapshot of the system health for a particular uid.
+ */
+ @Override
+ public HealthStatsParceler takeUidSnapshot(int requestUid) {
+ if (requestUid != Binder.getCallingUid()) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ updateExternalStats("get-health-stats-for-uid",
+ BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ synchronized (mStats) {
+ return getHealthStatsForUidLocked(requestUid);
+ }
+ } catch (Exception ex) {
+ Slog.d(TAG, "Crashed while writing for takeUidSnapshot(" + requestUid + ")", ex);
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Gets a snapshot of the system health for a number of uids.
+ */
+ @Override
+ public HealthStatsParceler[] takeUidSnapshots(int[] requestUids) {
+ if (!onlyCaller(requestUids)) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ }
+ long ident = Binder.clearCallingIdentity();
+ int i=-1;
+ try {
+ updateExternalStats("get-health-stats-for-uids",
+ BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
+ synchronized (mStats) {
+ final int N = requestUids.length;
+ final HealthStatsParceler[] results = new HealthStatsParceler[N];
+ for (i=0; i<N; i++) {
+ results[i] = getHealthStatsForUidLocked(requestUids[i]);
+ }
+ return results;
+ }
+ } catch (Exception ex) {
+ Slog.d(TAG, "Crashed while writing for takeUidSnapshots("
+ + Arrays.toString(requestUids) + ") i=" + i, ex);
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Returns whether the Binder.getCallingUid is the only thing in requestUids.
+ */
+ private static boolean onlyCaller(int[] requestUids) {
+ final int caller = Binder.getCallingUid();
+ final int N = requestUids.length;
+ for (int i=0; i<N; i++) {
+ if (requestUids[i] != caller) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Gets a HealthStatsParceler for the given uid. You should probably call
+ * updateExternalStats first.
+ */
+ HealthStatsParceler getHealthStatsForUidLocked(int requestUid) {
+ final HealthStatsBatteryStatsWriter writer = new HealthStatsBatteryStatsWriter();
+ final HealthStatsWriter uidWriter = new HealthStatsWriter(UidHealthStats.CONSTANTS);
+ final BatteryStats.Uid uid = mStats.getUidStats().get(requestUid);
+ if (uid != null) {
+ writer.writeUid(uidWriter, mStats, uid);
+ }
+ return new HealthStatsParceler(uidWriter);
+ }
+
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 37b0af1..45e3a76 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -654,7 +654,7 @@
}
final boolean callerForeground = receiverRecord.callerApp != null
- ? receiverRecord.callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE
+ ? receiverRecord.callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND
: true;
// Show a permission review UI only for explicit broadcast from a foreground app
diff --git a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
new file mode 100644
index 0000000..39c6ce6
--- /dev/null
+++ b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
@@ -0,0 +1,473 @@
+/*
+ * 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.am;
+
+import android.os.BatteryStats;
+import static android.os.BatteryStats.STATS_SINCE_UNPLUGGED;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.health.HealthKeys;
+import android.os.health.HealthStatsParceler;
+import android.os.health.HealthStatsWriter;
+import android.os.health.PackageHealthStats;
+import android.os.health.ProcessHealthStats;
+import android.os.health.PidHealthStats;
+import android.os.health.ServiceHealthStats;
+import android.os.health.TimerStat;
+import android.os.health.UidHealthStats;
+import android.util.SparseArray;
+
+import java.util.Map;
+
+public class HealthStatsBatteryStatsWriter {
+
+ private final long mNowRealtime;
+ private final long mNowUptime;
+
+ public HealthStatsBatteryStatsWriter() {
+ mNowRealtime = SystemClock.elapsedRealtime();
+ mNowUptime = SystemClock.uptimeMillis();
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid into a HealthStatsWriter.
+ */
+ public void writeUid(HealthStatsWriter uidWriter, BatteryStats bs, BatteryStats.Uid uid) {
+ int N;
+ BatteryStats.Timer timer;
+ SparseArray<? extends BatteryStats.Uid.Sensor> sensors;
+ SparseArray<? extends BatteryStats.Uid.Pid> pids;
+ BatteryStats.ControllerActivityCounter controller;
+ long sum;
+
+ //
+ // It's a little odd for these first four to be here but it's not the end of the
+ // world. It would be easy enough to duplicate them somewhere else if this API
+ // grows.
+ //
+
+ // MEASUREMENT_REALTIME_BATTERY_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_REALTIME_BATTERY_MS,
+ bs.computeBatteryRealtime(mNowRealtime*1000, STATS_SINCE_UNPLUGGED)/1000);
+
+ // MEASUREMENT_UPTIME_BATTERY_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_UPTIME_BATTERY_MS,
+ bs.computeBatteryUptime(mNowUptime*1000, STATS_SINCE_UNPLUGGED)/1000);
+
+ // MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_REALTIME_SCREEN_OFF_BATTERY_MS,
+ bs.computeBatteryScreenOffRealtime(mNowRealtime*1000, STATS_SINCE_UNPLUGGED)/1000);
+
+ // MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_UPTIME_SCREEN_OFF_BATTERY_MS,
+ bs.computeBatteryScreenOffUptime(mNowUptime*1000, STATS_SINCE_UNPLUGGED)/1000);
+
+ //
+ // Now on to the real per-uid stats...
+ //
+
+ for (final Map.Entry<String,? extends BatteryStats.Uid.Wakelock> entry:
+ uid.getWakelockStats().entrySet()) {
+ final String key = entry.getKey();
+ final BatteryStats.Uid.Wakelock wakelock = entry.getValue();
+
+ // TIMERS_WAKELOCKS_FULL
+ timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_FULL);
+ addTimers(uidWriter, UidHealthStats.TIMERS_WAKELOCKS_FULL, key, timer);
+
+ // TIMERS_WAKELOCKS_PARTIAL
+ timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
+ addTimers(uidWriter, UidHealthStats.TIMERS_WAKELOCKS_PARTIAL, key, timer);
+
+ // TIMERS_WAKELOCKS_WINDOW
+ timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_WINDOW);
+ addTimers(uidWriter, UidHealthStats.TIMERS_WAKELOCKS_WINDOW, key, timer);
+
+ // TIMERS_WAKELOCKS_DRAW
+ timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_DRAW);
+ addTimers(uidWriter, UidHealthStats.TIMERS_WAKELOCKS_DRAW, key, timer);
+ }
+
+ // TIMERS_SYNCS
+ for (final Map.Entry<String,? extends BatteryStats.Timer> entry:
+ uid.getSyncStats().entrySet()) {
+ addTimers(uidWriter, UidHealthStats.TIMERS_SYNCS, entry.getKey(), entry.getValue());
+ }
+
+ // TIMERS_JOBS
+ for (final Map.Entry<String,? extends BatteryStats.Timer> entry:
+ uid.getJobStats().entrySet()) {
+ addTimers(uidWriter, UidHealthStats.TIMERS_JOBS, entry.getKey(), entry.getValue());
+ }
+
+ // TIMERS_SENSORS
+ sensors = uid.getSensorStats();
+ N = sensors.size();
+ for (int i=0; i<N; i++) {
+ int sensorId = sensors.keyAt(i);
+ // Battery Stats stores the GPS sensors with a bogus key in this API. Pull it out
+ // as a separate metric here so as to not expose that in the API.
+ if (sensorId == BatteryStats.Uid.Sensor.GPS) {
+ addTimer(uidWriter, UidHealthStats.TIMER_GPS_SENSOR, sensors.valueAt(i).getSensorTime());
+ } else {
+ addTimers(uidWriter, UidHealthStats.TIMERS_SENSORS, Integer.toString(sensorId),
+ sensors.valueAt(i).getSensorTime());
+ }
+ }
+
+ // STATS_PIDS
+ pids = uid.getPidStats();
+ N = sensors.size();
+ for (int i=0; i<N; i++) {
+ final HealthStatsWriter writer = new HealthStatsWriter(PidHealthStats.CONSTANTS);
+ writePid(writer, pids.valueAt(i));
+ uidWriter.addStats(UidHealthStats.STATS_PIDS, Integer.toString(pids.keyAt(i)), writer);
+ }
+
+ // STATS_PROCESSES
+ for (final Map.Entry<String,? extends BatteryStats.Uid.Proc> entry:
+ uid.getProcessStats().entrySet()) {
+ final HealthStatsWriter writer = new HealthStatsWriter(ProcessHealthStats.CONSTANTS);
+ writeProc(writer, entry.getValue());
+ uidWriter.addStats(UidHealthStats.STATS_PROCESSES, entry.getKey(), writer);
+ }
+
+ // STATS_PACKAGES
+ for (final Map.Entry<String,? extends BatteryStats.Uid.Pkg> entry:
+ uid.getPackageStats().entrySet()) {
+ final HealthStatsWriter writer = new HealthStatsWriter(PackageHealthStats.CONSTANTS);
+ writePkg(writer, entry.getValue());
+ uidWriter.addStats(UidHealthStats.STATS_PACKAGES, entry.getKey(), writer);
+ }
+
+ controller = uid.getWifiControllerActivity();
+ if (controller != null) {
+ // MEASUREMENT_WIFI_IDLE_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_IDLE_MS,
+ controller.getIdleTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_WIFI_RX_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_RX_MS,
+ controller.getRxTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_WIFI_TX_MS
+ sum = 0;
+ for (final BatteryStats.LongCounter counter: controller.getTxTimeCounters()) {
+ sum += counter.getCountLocked(STATS_SINCE_UNPLUGGED);
+ }
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_TX_MS, sum);
+ // MEASUREMENT_WIFI_POWER_MAMS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_POWER_MAMS,
+ controller.getPowerCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ }
+
+ controller = uid.getBluetoothControllerActivity();
+ if (controller != null) {
+ // MEASUREMENT_BLUETOOTH_IDLE_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_IDLE_MS,
+ controller.getIdleTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_BLUETOOTH_RX_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_RX_MS,
+ controller.getRxTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_BLUETOOTH_TX_MS
+ sum = 0;
+ for (final BatteryStats.LongCounter counter: controller.getTxTimeCounters()) {
+ sum += counter.getCountLocked(STATS_SINCE_UNPLUGGED);
+ }
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_TX_MS, sum);
+ // MEASUREMENT_BLUETOOTH_POWER_MAMS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_POWER_MAMS,
+ controller.getPowerCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ }
+
+ controller = uid.getModemControllerActivity();
+ if (controller != null) {
+ // MEASUREMENT_MOBILE_IDLE_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_IDLE_MS,
+ controller.getIdleTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_MOBILE_RX_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_RX_MS,
+ controller.getRxTimeCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ // MEASUREMENT_MOBILE_TX_MS
+ sum = 0;
+ for (final BatteryStats.LongCounter counter: controller.getTxTimeCounters()) {
+ sum += counter.getCountLocked(STATS_SINCE_UNPLUGGED);
+ }
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_TX_MS, sum);
+ // MEASUREMENT_MOBILE_POWER_MAMS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_POWER_MAMS,
+ controller.getPowerCounter().getCountLocked(STATS_SINCE_UNPLUGGED));
+ }
+
+ // MEASUREMENT_WIFI_RUNNING_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_RUNNING_MS,
+ uid.getWifiRunningTime(mNowRealtime, STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_FULL_LOCK_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_FULL_LOCK_MS,
+ uid.getFullWifiLockTime(mNowRealtime, STATS_SINCE_UNPLUGGED));
+
+ // TIMER_WIFI_SCAN
+ uidWriter.addTimer(UidHealthStats.TIMER_WIFI_SCAN,
+ uid.getWifiScanCount(STATS_SINCE_UNPLUGGED),
+ uid.getWifiScanTime(mNowRealtime, STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_MULTICAST_MS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_MULTICAST_MS,
+ uid.getWifiMulticastTime(mNowRealtime, STATS_SINCE_UNPLUGGED));
+
+ // TIMER_AUDIO
+ addTimer(uidWriter, UidHealthStats.TIMER_AUDIO, uid.getAudioTurnedOnTimer());
+
+ // TIMER_VIDEO
+ addTimer(uidWriter, UidHealthStats.TIMER_VIDEO, uid.getVideoTurnedOnTimer());
+
+ // TIMER_FLASHLIGHT
+ addTimer(uidWriter, UidHealthStats.TIMER_FLASHLIGHT, uid.getFlashlightTurnedOnTimer());
+
+ // TIMER_CAMERA
+ addTimer(uidWriter, UidHealthStats.TIMER_CAMERA, uid.getCameraTurnedOnTimer());
+
+ // TIMER_FOREGROUND_ACTIVITY
+ addTimer(uidWriter, UidHealthStats.TIMER_FOREGROUND_ACTIVITY, uid.getForegroundActivityTimer());
+
+ // TIMER_BLUETOOTH_SCAN
+ addTimer(uidWriter, UidHealthStats.TIMER_BLUETOOTH_SCAN, uid.getBluetoothScanTimer());
+
+ // TIMER_PROCESS_STATE_TOP_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_TOP_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_TOP));
+
+ // TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_FOREGROUND_SERVICE_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE));
+
+ // TIMER_PROCESS_STATE_TOP_SLEEPING_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_TOP_SLEEPING_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING));
+
+ // TIMER_PROCESS_STATE_FOREGROUND_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_FOREGROUND_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_FOREGROUND));
+
+ // TIMER_PROCESS_STATE_BACKGROUND_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_BACKGROUND_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_BACKGROUND));
+
+ // TIMER_PROCESS_STATE_CACHED_MS
+ addTimer(uidWriter, UidHealthStats.TIMER_PROCESS_STATE_CACHED_MS,
+ uid.getProcessStateTimer(BatteryStats.Uid.PROCESS_STATE_CACHED));
+
+ // TIMER_VIBRATOR
+ addTimer(uidWriter, UidHealthStats.TIMER_VIBRATOR, uid.getVibratorOnTimer());
+
+ // MEASUREMENT_OTHER_USER_ACTIVITY_COUNT
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_OTHER_USER_ACTIVITY_COUNT,
+ uid.getUserActivityCount(PowerManager.USER_ACTIVITY_EVENT_OTHER,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BUTTON_USER_ACTIVITY_COUNT,
+ uid.getUserActivityCount(PowerManager.USER_ACTIVITY_EVENT_BUTTON,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT,
+ uid.getUserActivityCount(PowerManager.USER_ACTIVITY_EVENT_TOUCH,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_MOBILE_RX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_RX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_MOBILE_TX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_TX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_RX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_RX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_TX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_TX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BLUETOOTH_RX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_RX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BLUETOOTH_TX_BYTES
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_TX_BYTES,
+ uid.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_MOBILE_RX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_RX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_MOBILE_TX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_MOBILE_TX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_RX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_RX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_WIFI_TX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_WIFI_TX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BLUETOOTH_RX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_RX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_BT_RX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_BLUETOOTH_TX_PACKETS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_BLUETOOTH_TX_PACKETS,
+ uid.getNetworkActivityPackets(BatteryStats.NETWORK_BT_TX_DATA,
+ STATS_SINCE_UNPLUGGED));
+
+ // TIMER_MOBILE_RADIO_ACTIVE
+ uidWriter.addTimer(UidHealthStats.TIMER_MOBILE_RADIO_ACTIVE,
+ uid.getMobileRadioActiveCount(STATS_SINCE_UNPLUGGED),
+ uid.getMobileRadioActiveTime(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_USER_CPU_TIME_US
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_USER_CPU_TIME_US,
+ uid.getUserCpuTimeUs(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_SYSTEM_CPU_TIME_US
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_SYSTEM_CPU_TIME_US,
+ uid.getSystemCpuTimeUs(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_CPU_POWER_MAUS
+ uidWriter.addMeasurement(UidHealthStats.MEASUREMENT_CPU_POWER_MAUS,
+ uid.getCpuPowerMaUs(STATS_SINCE_UNPLUGGED));
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid.Pid into a HealthStatsWriter.
+ */
+ public void writePid(HealthStatsWriter pidWriter, BatteryStats.Uid.Pid pid) {
+ if (pid == null) {
+ return;
+ }
+
+ // MEASUREMENT_WAKE_NESTING_COUNT
+ pidWriter.addMeasurement(PidHealthStats.MEASUREMENT_WAKE_NESTING_COUNT, pid.mWakeNesting);
+
+ // MEASUREMENT_WAKE_SUM_MS
+ pidWriter.addMeasurement(PidHealthStats.MEASUREMENT_WAKE_SUM_MS, pid.mWakeSumMs);
+
+ // MEASUREMENT_WAKE_START_MS
+ pidWriter.addMeasurement(PidHealthStats.MEASUREMENT_WAKE_SUM_MS, pid.mWakeStartMs);
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid.Proc into a HealthStatsWriter.
+ */
+ public void writeProc(HealthStatsWriter procWriter, BatteryStats.Uid.Proc proc) {
+ // MEASUREMENT_USER_TIME_MS
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_USER_TIME_MS,
+ proc.getUserTime(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_SYSTEM_TIME_MS
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_SYSTEM_TIME_MS,
+ proc.getSystemTime(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_STARTS_COUNT
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_STARTS_COUNT,
+ proc.getStarts(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_CRASHES_COUNT
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_CRASHES_COUNT,
+ proc.getNumCrashes(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_ANR_COUNT
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_ANR_COUNT,
+ proc.getNumAnrs(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_FOREGROUND_MS
+ procWriter.addMeasurement(ProcessHealthStats.MEASUREMENT_FOREGROUND_MS,
+ proc.getForegroundTime(STATS_SINCE_UNPLUGGED));
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid.Pkg into a HealthStatsWriter.
+ */
+ public void writePkg(HealthStatsWriter pkgWriter, BatteryStats.Uid.Pkg pkg) {
+ // STATS_SERVICES
+ for (final Map.Entry<String,? extends BatteryStats.Uid.Pkg.Serv> entry:
+ pkg.getServiceStats().entrySet()) {
+ final HealthStatsWriter writer = new HealthStatsWriter(ServiceHealthStats.CONSTANTS);
+ writeServ(writer, entry.getValue());
+ pkgWriter.addStats(PackageHealthStats.STATS_SERVICES, entry.getKey(), writer);
+ }
+
+ // MEASUREMENTS_WAKEUP_ALARMS_COUNT
+ for (final Map.Entry<String,? extends BatteryStats.Counter> entry:
+ pkg.getWakeupAlarmStats().entrySet()) {
+ final BatteryStats.Counter counter = entry.getValue();
+ if (counter != null) {
+ pkgWriter.addMeasurements(PackageHealthStats.MEASUREMENTS_WAKEUP_ALARMS_COUNT,
+ entry.getKey(), counter.getCountLocked(STATS_SINCE_UNPLUGGED));
+ }
+ }
+ }
+
+ /**
+ * Writes the contents of a BatteryStats.Uid.Pkg.Serv into a HealthStatsWriter.
+ */
+ public void writeServ(HealthStatsWriter servWriter, BatteryStats.Uid.Pkg.Serv serv) {
+ // MEASUREMENT_START_SERVICE_COUNT
+ servWriter.addMeasurement(ServiceHealthStats.MEASUREMENT_START_SERVICE_COUNT,
+ serv.getStarts(STATS_SINCE_UNPLUGGED));
+
+ // MEASUREMENT_LAUNCH_COUNT
+ servWriter.addMeasurement(ServiceHealthStats.MEASUREMENT_LAUNCH_COUNT,
+ serv.getLaunches(STATS_SINCE_UNPLUGGED));
+ }
+
+ /**
+ * Adds a BatteryStats.Timer into a HealthStatsWriter. Safe to pass a null timer.
+ */
+ private void addTimer(HealthStatsWriter writer, int key, BatteryStats.Timer timer) {
+ if (timer != null) {
+ writer.addTimer(key, timer.getCountLocked(STATS_SINCE_UNPLUGGED),
+ timer.getTotalTimeLocked(mNowRealtime, STATS_SINCE_UNPLUGGED));
+ }
+ }
+
+ /**
+ * Adds a named BatteryStats.Timer into a HealthStatsWriter. Safe to pass a null timer.
+ */
+ private void addTimers(HealthStatsWriter writer, int key, String name,
+ BatteryStats.Timer timer) {
+ if (timer != null) {
+ writer.addTimers(key, name, new TimerStat(timer.getCountLocked(STATS_SINCE_UNPLUGGED),
+ timer.getTotalTimeLocked(mNowRealtime, STATS_SINCE_UNPLUGGED)));
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/am/InstrumentationReporter.java b/services/core/java/com/android/server/am/InstrumentationReporter.java
new file mode 100644
index 0000000..fdbb73e
--- /dev/null
+++ b/services/core/java/com/android/server/am/InstrumentationReporter.java
@@ -0,0 +1,144 @@
+/*
+ * 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.am;
+
+import android.app.IInstrumentationWatcher;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+public class InstrumentationReporter {
+ static final boolean DEBUG = false;
+ static final String TAG = ActivityManagerDebugConfig.TAG_AM;
+
+ static final int REPORT_TYPE_STATUS = 0;
+ static final int REPORT_TYPE_FINISHED = 1;
+
+ final Object mLock = new Object();
+ ArrayList<Report> mPendingReports;
+ Thread mThread;
+
+ final class MyThread extends Thread {
+ public MyThread() {
+ super("InstrumentationReporter");
+ if (DEBUG) Slog.d(TAG, "Starting InstrumentationReporter: " + this);
+ }
+
+ @Override
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+ boolean waited = false;
+ while (true) {
+ ArrayList<Report> reports;
+ synchronized (mLock) {
+ reports = mPendingReports;
+ mPendingReports = null;
+ if (reports == null || reports.isEmpty()) {
+ if (!waited) {
+ // Sleep for a little bit, to avoid thrashing through threads.
+ try {
+ mLock.wait(10000); // 10 seconds
+ } catch (InterruptedException e) {
+ }
+ waited = true;
+ continue;
+ } else {
+ mThread = null;
+ if (DEBUG) Slog.d(TAG, "Exiting InstrumentationReporter: " + this);
+ return;
+ }
+ }
+ }
+
+ waited = false;
+
+ for (int i=0; i<reports.size(); i++) {
+ final Report rep = reports.get(i);
+ try {
+ if (rep.mType == REPORT_TYPE_STATUS) {
+ if (DEBUG) Slog.d(TAG, "Dispatch status to " + rep.mWatcher
+ + ": " + rep.mName.flattenToShortString()
+ + " code=" + rep.mResultCode + " result=" + rep.mResults);
+ rep.mWatcher.instrumentationStatus(rep.mName, rep.mResultCode,
+ rep.mResults);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Dispatch finished to " + rep.mWatcher
+ + ": " + rep.mName.flattenToShortString()
+ + " code=" + rep.mResultCode + " result=" + rep.mResults);
+ rep.mWatcher.instrumentationFinished(rep.mName, rep.mResultCode,
+ rep.mResults);
+ }
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Failure reporting to instrumentation watcher: comp="
+ + rep.mName + " results=" + rep.mResults);
+ }
+ }
+ }
+ }
+ }
+
+ final class Report {
+ final int mType;
+ final IInstrumentationWatcher mWatcher;
+ final ComponentName mName;
+ final int mResultCode;
+ final Bundle mResults;
+
+ Report(int type, IInstrumentationWatcher watcher, ComponentName name, int resultCode,
+ Bundle results) {
+ mType = type;
+ mWatcher = watcher;
+ mName = name;
+ mResultCode = resultCode;
+ mResults = results;
+ }
+ }
+
+ public void reportStatus(IInstrumentationWatcher watcher, ComponentName name, int resultCode,
+ Bundle results) {
+ if (DEBUG) Slog.d(TAG, "Report status to " + watcher
+ + ": " + name.flattenToShortString()
+ + " code=" + resultCode + " result=" + results);
+ report(new Report(REPORT_TYPE_STATUS, watcher, name, resultCode, results));
+ }
+
+ public void reportFinished(IInstrumentationWatcher watcher, ComponentName name, int resultCode,
+ Bundle results) {
+ if (DEBUG) Slog.d(TAG, "Report finished to " + watcher
+ + ": " + name.flattenToShortString()
+ + " code=" + resultCode + " result=" + results);
+ report(new Report(REPORT_TYPE_FINISHED, watcher, name, resultCode, results));
+ }
+
+ private void report(Report report) {
+ synchronized (mLock) {
+ if (mThread == null) {
+ mThread = new MyThread();
+ mThread.start();
+ }
+ if (mPendingReports == null) {
+ mPendingReports = new ArrayList<>();
+ }
+ mPendingReports.add(report);
+ mLock.notifyAll();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b49370b..f073e5c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -124,6 +124,13 @@
// Memory pages are 4K.
static final int PAGE_SIZE = 4*1024;
+ // Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE
+ static final int SCHED_GROUP_BACKGROUND = 0;
+ // Activity manager's version of Process.THREAD_GROUP_DEFAULT
+ 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;
+
// 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.
static final int MIN_CACHED_APPS = 2;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 62275a9..4eae45c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -40,6 +40,7 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.util.DisplayMetrics;
import android.util.Slog;
@@ -74,6 +75,7 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
@@ -444,9 +446,9 @@
// task as having a true root activity.
rootWasReset = true;
}
-
userId = UserHandle.getUserId(info.applicationInfo.uid);
- mUserSetupComplete = mService.mUserController.isUserSetupCompleteLocked(userId);
+ mUserSetupComplete = Settings.Secure.getIntForUser(mService.mContext.getContentResolver(),
+ USER_SETUP_COMPLETE, 0, userId) != 0;
if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
// If the activity itself has requested auto-remove, then just always do it.
autoRemoveRecents = true;
@@ -757,6 +759,14 @@
if (r.isPersistable()) {
mService.notifyTaskPersisterLocked(this, false);
}
+
+ if (stack != null && stack.mStackId == PINNED_STACK_ID) {
+ // We normally notify listeners of task stack changes on pause, however pinned stack
+ // activities are normally in the paused state so no notification will be sent there
+ // before the activity is removed. We send it here so instead.
+ mService.notifyTaskStackChangedLocked();
+ }
+
if (mActivities.isEmpty()) {
return !mReuseTask;
}
@@ -1332,8 +1342,13 @@
if (bounds == null) {
return;
}
- final int minimalSize = mMinimalSize == -1
- ? mService.mStackSupervisor.mDefaultMinimalSizeOfResizeableTask : mMinimalSize;
+ int minimalSize = mMinimalSize;
+ // If the task has no requested minimal size, we'd like to enforce a minimal size
+ // so that the user can not render the task too small to manipulate. We don't need
+ // to do this for the pinned stack as the bounds are controlled by the system.
+ if (minimalSize == -1 && stack.mStackId != PINNED_STACK_ID) {
+ minimalSize = mService.mStackSupervisor.mDefaultMinimalSizeOfResizeableTask;
+ }
final boolean adjustWidth = minimalSize > bounds.width();
final boolean adjustHeight = minimalSize > bounds.height();
if (!(adjustWidth || adjustHeight)) {
@@ -1570,6 +1585,7 @@
pw.print(prefix); pw.print("userId="); pw.print(userId);
pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
+ pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
pw.print(" mCallingPackage="); pw.println(mCallingPackage);
if (affinity != null || rootAffinity != null) {
pw.print(prefix); pw.print("affinity="); pw.print(affinity);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index addffd3..59c2682 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -24,7 +24,6 @@
import static android.app.ActivityManager.USER_OP_SUCCESS;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.os.Process.SYSTEM_UID;
-import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -46,14 +45,11 @@
import android.app.IStopUserCallback;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
@@ -71,12 +67,10 @@
import android.os.UserManagerInternal;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
-import android.provider.Settings;
import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.android.internal.R;
@@ -153,34 +147,6 @@
private final LockPatternUtils mLockPatternUtils;
- // Set of users who have completed the set-up process.
- private final SparseBooleanArray mSetupCompletedUsers = new SparseBooleanArray();
- private final UserSetupCompleteContentObserver mUserSetupCompleteContentObserver;
-
- private class UserSetupCompleteContentObserver extends ContentObserver {
- private final Uri mUserSetupComplete = Settings.Secure.getUriFor(USER_SETUP_COMPLETE);
-
- public UserSetupCompleteContentObserver(Handler handler) {
- super(handler);
- }
-
- void register(ContentResolver resolver) {
- resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL);
- synchronized (mService) {
- updateUserSetupCompleteLocked(UserHandle.USER_ALL);
- }
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri, int userId) {
- if (mUserSetupComplete.equals(uri)) {
- synchronized (mService) {
- updateUserSetupCompleteLocked(userId);
- }
- }
- }
- }
-
UserController(ActivityManagerService service) {
mService = service;
mHandler = mService.mHandler;
@@ -190,7 +156,6 @@
mUserLru.add(UserHandle.USER_SYSTEM);
mLockPatternUtils = new LockPatternUtils(mService.mContext);
updateStartedUserArrayLocked();
- mUserSetupCompleteContentObserver = new UserSetupCompleteContentObserver(mHandler);
}
void finishUserSwitch(UserState uss) {
@@ -477,7 +442,6 @@
mStartedUsers.remove(userId);
mUserLru.remove(Integer.valueOf(userId));
updateStartedUserArrayLocked();
- mSetupCompletedUsers.delete(userId);
mService.onUserStoppedLocked(userId);
// Clean up all state and processes associated with the user.
@@ -620,7 +584,7 @@
}
} else {
Slog.w(TAG, "Mount service not published; guessing locked state based on property");
- return !StorageManager.isFileBasedEncryptionEnabled();
+ return !StorageManager.isFileEncryptedNativeOrEmulated();
}
}
@@ -677,7 +641,6 @@
final Integer userIdInt = userId;
mUserLru.remove(userIdInt);
mUserLru.add(userIdInt);
- updateUserSetupCompleteLocked(userId);
if (foreground) {
mCurrentUserId = userId;
@@ -892,22 +855,6 @@
mUserSwitchObservers.finishBroadcast();
}
- void updateUserSetupCompleteLocked(int userId) {
- final ContentResolver cr = mService.mContext.getContentResolver();
- for (int i = mStartedUsers.size() - 1; i >= 0; i--) {
- int startedUser = mStartedUsers.keyAt(i);
- if (startedUser == userId || userId == UserHandle.USER_ALL) {
- final boolean setupComplete =
- Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, startedUser) != 0;
- mSetupCompletedUsers.put(startedUser, setupComplete);
- }
- }
- }
-
- boolean isUserSetupCompleteLocked(int userId) {
- return mSetupCompletedUsers.get(userId);
- }
-
private void stopBackgroundUsersIfEnforced(int oldUserId) {
// Never stop system user
if (oldUserId == UserHandle.USER_SYSTEM) {
@@ -1218,7 +1165,6 @@
void onSystemReady() {
updateCurrentProfileIdsLocked();
- mUserSetupCompleteContentObserver.register(mService.mContext.getContentResolver());
}
/**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f2a9c2c..d919737 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -58,7 +58,7 @@
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioPort;
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
@@ -6287,8 +6287,8 @@
mRecordMonitor.unregisterRecordingCallback(rcdb);
}
- public AudioRecordConfiguration[] getActiveRecordConfigurations() {
- return mRecordMonitor.getActiveRecordConfigurations();
+ public AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
+ return mRecordMonitor.getActiveRecordingConfigurations();
}
//======================
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 7e76ac4..7a085e1 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -18,7 +18,7 @@
import android.media.AudioFormat;
import android.media.AudioManager;
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
import android.media.AudioSystem;
import android.media.IRecordingConfigDispatcher;
import android.media.MediaRecorder;
@@ -39,8 +39,8 @@
private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>();
- private HashMap<Integer, AudioRecordConfiguration> mRecordConfigs =
- new HashMap<Integer, AudioRecordConfiguration>();
+ private HashMap<Integer, AudioRecordingConfiguration> mRecordConfigs =
+ new HashMap<Integer, AudioRecordingConfiguration>();
RecordingActivityMonitor() {
RecMonitorClient.sMonitor = this;
@@ -54,12 +54,15 @@
if (MediaRecorder.isSystemOnlyAudioSource(source)) {
return;
}
- if (updateSnapshot(event, session, source, recordingInfo)) {
- final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
+ final AudioRecordingConfiguration[] configs =
+ updateSnapshot(event, session, source, recordingInfo);
+ if (configs != null){
synchronized(mClients) {
+ final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
while (clientIterator.hasNext()) {
try {
- clientIterator.next().mDispatcherCb.dispatchRecordingConfigChange();
+ clientIterator.next().mDispatcherCb.dispatchRecordingConfigChange(
+ configs);
} catch (RemoteException e) {
Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e);
}
@@ -101,9 +104,9 @@
}
}
- AudioRecordConfiguration[] getActiveRecordConfigurations() {
+ AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
synchronized(mRecordConfigs) {
- return mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]);
+ return mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
}
}
@@ -115,14 +118,19 @@
* @param recordingFormat see
* {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
* for the definition of the contents of the array
- * @return true if the list of active recording sessions has been modified, false otherwise.
+ * @return null if the list of active recording sessions has not been modified, an array
+ * with the current active configurations otherwise.
*/
- private boolean updateSnapshot(int event, int session, int source, int[] recordingInfo) {
+ private AudioRecordingConfiguration[] updateSnapshot(int event, int session, int source,
+ int[] recordingInfo) {
+ final boolean configChanged;
+ final AudioRecordingConfiguration[] configs;
synchronized(mRecordConfigs) {
switch (event) {
case AudioManager.RECORD_CONFIG_EVENT_STOP:
// return failure if an unknown recording session stopped
- return (mRecordConfigs.remove(new Integer(session)) != null);
+ configChanged = (mRecordConfigs.remove(new Integer(session)) != null);
+ break;
case AudioManager.RECORD_CONFIG_EVENT_START:
final AudioFormat clientFormat = new AudioFormat.Builder()
.setEncoding(recordingInfo[0])
@@ -139,29 +147,36 @@
final int patchHandle = recordingInfo[6];
final Integer sessionKey = new Integer(session);
if (mRecordConfigs.containsKey(sessionKey)) {
- final AudioRecordConfiguration updatedConfig =
- new AudioRecordConfiguration(session, source,
+ final AudioRecordingConfiguration updatedConfig =
+ new AudioRecordingConfiguration(session, source,
clientFormat, deviceFormat, patchHandle);
if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
- return false;
+ configChanged = false;
} else {
// config exists but has been modified
mRecordConfigs.remove(sessionKey);
mRecordConfigs.put(sessionKey, updatedConfig);
- return true;
+ configChanged = true;
}
} else {
mRecordConfigs.put(sessionKey,
- new AudioRecordConfiguration(session, source,
+ new AudioRecordingConfiguration(session, source,
clientFormat, deviceFormat, patchHandle));
- return true;
+ configChanged = true;
}
+ break;
default:
Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
event, session, source));
- return false;
+ configChanged = false;
+ }
+ if (configChanged) {
+ configs = mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
+ } else {
+ configs = null;
}
}
+ return configs;
}
/**
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 90c9ddf..9e1f6b8 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -60,7 +60,7 @@
public class KeepaliveTracker {
private static final String TAG = "KeepaliveTracker";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
public static final String PERMISSION = android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index fb8b110..bce7733 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -76,7 +76,7 @@
* {@hide}
*/
public class NetworkMonitor extends StateMachine {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final String TAG = "NetworkMonitor";
private static final String DEFAULT_SERVER = "connectivitycheck.gstatic.com";
private static final int SOCKET_TIMEOUT_MS = 10000;
@@ -688,12 +688,11 @@
// Time how long it takes to get a response to our request
long requestTimestamp = SystemClock.elapsedRealtime();
- urlConnection.getInputStream();
+ httpResponseCode = urlConnection.getResponseCode();
// Time how long it takes to get a response to our request
long responseTimestamp = SystemClock.elapsedRealtime();
- httpResponseCode = urlConnection.getResponseCode();
validationLog("isCaptivePortal: ret=" + httpResponseCode +
" headers=" + urlConnection.getHeaderFields());
// NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index debda14..22cefd1 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -54,7 +54,7 @@
*/
public class PermissionMonitor {
private static final String TAG = "PermissionMonitor";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean SYSTEM = true;
private static final boolean NETWORK = false;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 760b2181..4eecc81 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -90,7 +90,7 @@
private Context mContext;
private final static String TAG = "Tethering";
- private final static boolean DBG = true;
+ private final static boolean DBG = false;
private final static boolean VDBG = false;
// TODO - remove both of these - should be part of interface inspection/selection stuff
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index f231f92..3b0b79a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -718,7 +718,8 @@
public void onUserAdded(int userHandle) {
// If the user is restricted tie them to the parent user's VPN
UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
- if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle
+ && mVpnUsers != null) {
synchronized(Vpn.this) {
try {
addVpnUserLocked(userHandle);
@@ -736,7 +737,8 @@
public void onUserRemoved(int userHandle) {
// clean up if restricted
UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
- if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) {
+ if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle
+ && mVpnUsers != null) {
synchronized(Vpn.this) {
try {
removeVpnUserLocked(userHandle);
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 212e077..03191a0 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -18,12 +18,16 @@
import android.Manifest;
import android.accounts.Account;
+import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentService;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ISyncStatusObserver;
import android.content.PeriodicSync;
import android.content.SyncAdapterType;
@@ -32,6 +36,7 @@
import android.content.SyncStatusInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ProviderInfo;
import android.database.IContentObserver;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
@@ -44,30 +49,64 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.security.InvalidParameterException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
/**
* {@hide}
*/
public final class ContentService extends IContentService.Stub {
private static final String TAG = "ContentService";
+
private Context mContext;
private boolean mFactoryTest;
+
private final ObserverNode mRootNode = new ObserverNode("");
+
private SyncManager mSyncManager = null;
private final Object mSyncManagerLock = new Object();
+ /**
+ * Map from userId to providerPackageName to [clientPackageName, uri] to
+ * value. This structure is carefully optimized to keep invalidation logic
+ * as cheap as possible.
+ */
+ @GuardedBy("mCache")
+ private final SparseArray<ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>>>
+ mCache = new SparseArray<>();
+
+ private BroadcastReceiver mCacheReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Uri data = intent.getData();
+ if (data != null) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_NULL);
+ final String packageName = data.getSchemeSpecificPart();
+ invalidateCacheLocked(userId, packageName, null);
+ }
+ }
+ };
+
private SyncManager getSyncManager() {
if (SystemProperties.getBoolean("config.disable_network", false)) {
return null;
@@ -85,13 +124,15 @@
}
@Override
- protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP,
"caller doesn't have the DUMP permission");
+ final IndentingPrintWriter pw = new IndentingPrintWriter(pw_, " ");
+
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
- long identityToken = clearCallingIdentity();
+ final long identityToken = clearCallingIdentity();
try {
if (mSyncManager == null) {
pw.println("No SyncManager created! (Disk full?)");
@@ -132,6 +173,19 @@
pw.print(" Total number of nodes: "); pw.println(counts[0]);
pw.print(" Total number of observers: "); pw.println(counts[1]);
}
+
+ synchronized (mCache) {
+ pw.println();
+ pw.println("Cached content:");
+ pw.increaseIndent();
+ for (int i = 0; i < mCache.size(); i++) {
+ pw.println("User " + mCache.keyAt(i) + ":");
+ pw.increaseIndent();
+ pw.println(mCache.valueAt(i));
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
} finally {
restoreCallingIdentity(identityToken);
}
@@ -167,6 +221,15 @@
return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
}
});
+
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ packageFilter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mCacheReceiver, UserHandle.ALL,
+ packageFilter, null, null);
}
public void systemReady() {
@@ -312,6 +375,11 @@
uri.getAuthority());
}
}
+
+ synchronized (mCache) {
+ final String providerPackageName = getProviderPackageName(uri);
+ invalidateCacheLocked(userHandle, providerPackageName, uri);
+ }
} finally {
restoreCallingIdentity(identityToken);
}
@@ -915,6 +983,86 @@
}
}
+ private @Nullable String getProviderPackageName(Uri uri) {
+ final ProviderInfo pi = mContext.getPackageManager()
+ .resolveContentProvider(uri.getAuthority(), 0);
+ return (pi != null) ? pi.packageName : null;
+ }
+
+ private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
+ String providerPackageName) {
+ ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
+ if (userCache == null) {
+ userCache = new ArrayMap<>();
+ mCache.put(userId, userCache);
+ }
+ ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
+ if (packageCache == null) {
+ packageCache = new ArrayMap<>();
+ userCache.put(providerPackageName, packageCache);
+ }
+ return packageCache;
+ }
+
+ private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
+ ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
+ if (userCache == null) return;
+
+ ArrayMap<Pair<String, Uri>, Bundle> packageCache = userCache.get(providerPackageName);
+ if (packageCache == null) return;
+
+ if (uri != null) {
+ for (int i = 0; i < packageCache.size();) {
+ final Uri key = packageCache.keyAt(i).second;
+ if (Objects.equals(key, uri)) {
+ packageCache.removeAt(i);
+ } else {
+ i++;
+ }
+ }
+ } else {
+ packageCache.clear();
+ }
+ }
+
+ @Override
+ public void putCache(String packageName, Uri key, Bundle value, int userId) {
+ enforceCrossUserPermission(userId, TAG);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
+ mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
+ packageName);
+
+ final String providerPackageName = getProviderPackageName(key);
+ final Pair<String, Uri> fullKey = Pair.create(packageName, key);
+
+ synchronized (mCache) {
+ final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
+ providerPackageName);
+ if (value != null) {
+ cache.put(fullKey, value);
+ } else {
+ cache.remove(fullKey);
+ }
+ }
+ }
+
+ @Override
+ public Bundle getCache(String packageName, Uri key, int userId) {
+ enforceCrossUserPermission(userId, TAG);
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
+ mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
+ packageName);
+
+ final String providerPackageName = getProviderPackageName(key);
+ final Pair<String, Uri> fullKey = Pair.create(packageName, key);
+
+ synchronized (mCache) {
+ final ArrayMap<Pair<String, Uri>, Bundle> cache = findOrCreateCacheLocked(userId,
+ providerPackageName);
+ return cache.get(fullKey);
+ }
+ }
+
public static ContentService main(Context context, boolean factoryTest) {
ContentService service = new ContentService(context, factoryTest);
ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 95a9875..e5342ce 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -78,8 +78,9 @@
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
-import android.util.SparseArray;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerInternal;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -113,9 +114,7 @@
* All scheduled syncs will be passed on to JobScheduler as jobs
* (See {@link #scheduleSyncOperationH(SyncOperation, long)}. This function schedules a job
* with JobScheduler with appropriate delay and constraints (according to backoffs and extras).
- * A local copy of each scheduled SyncOperation object is stored in {@link mScheduledSyncs}.This
- * acts as a cache, so that we don't have to query JobScheduler every time we want to get a list of
- * all scheduled operations. The scheduleSyncOperationH function also assigns a unique jobId to each
+ * The scheduleSyncOperationH function also assigns a unique jobId to each
* SyncOperation.
*
* Periodic Syncs:
@@ -129,14 +128,6 @@
* run at a later time. Similarly, when a sync succeeds, backoff is cleared and all associated syncs
* are rescheduled. A rescheduled sync will get a new jobId.
*
- * State of {@link mScheduledSyncs}:
- * Every one-off SyncOperation will be put into this SparseArray when it is scheduled with
- * JobScheduler. And it will be removed once JobScheduler has started the job. Periodic syncs work
- * differently. They will always be present in mScheduledSyncs until the periodic sync is removed.
- * This is to ensure that if a request to add a periodic sync comes in, we add a new one only if a
- * duplicate doesn't exist. At every point of time, mScheduledSyncs and JobScheduler will show the
- * same pending syncs.
- *
* @hide
*/
public class SyncManager {
@@ -192,6 +183,13 @@
*/
private static final long SYNC_DELAY_ON_CONFLICT = 10*1000; // 10 seconds
+ /**
+ * Generate job ids in the range [MIN_SYNC_JOB_ID, MAX_SYNC_JOB_ID) to avoid conflicts with
+ * other jobs scheduled by the system process.
+ */
+ private static final int MIN_SYNC_JOB_ID = 100000;
+ private static final int MAX_SYNC_JOB_ID = 110000;
+
private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
@@ -213,6 +211,7 @@
private final NotificationManager mNotificationMgr;
private final IBatteryStats mBatteryStats;
private JobScheduler mJobScheduler;
+ private JobSchedulerInternal mJobSchedulerInternal;
private SyncJobService mSyncJobService;
private SyncStorageEngine mSyncStorageEngine;
@@ -228,14 +227,13 @@
protected SyncAdaptersCache mSyncAdapters;
- // Cache of all operations scheduled on the JobScheduler so that JobScheduler doesn't have
- // to be queried often.
- private SparseArray<SyncOperation> mScheduledSyncs = new SparseArray<SyncOperation>(32);
private final Random mRand;
- private boolean isJobIdInUseLockedH(int jobId) {
- if (mScheduledSyncs.indexOfKey(jobId) >= 0) {
- return true;
+ private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) {
+ for (JobInfo job: pendingJobs) {
+ if (job.getId() == jobId) {
+ return true;
+ }
}
for (ActiveSyncContext asc: mActiveSyncContexts) {
if (asc.mSyncOperation.jobId == jobId) {
@@ -246,35 +244,25 @@
}
private int getUnusedJobIdH() {
- synchronized (mScheduledSyncs) {
- int newJobId;
- do {
- newJobId = mRand.nextInt(Integer.MAX_VALUE);
- } while (isJobIdInUseLockedH(newJobId));
- return newJobId;
- }
+ int newJobId;
+ do {
+ newJobId = MIN_SYNC_JOB_ID + mRand.nextInt(MAX_SYNC_JOB_ID - MIN_SYNC_JOB_ID);
+ } while (isJobIdInUseLockedH(newJobId,
+ mJobSchedulerInternal.getSystemScheduledPendingJobs()));
+ return newJobId;
}
- private void addSyncOperationToCache(SyncOperation op) {
- synchronized (mScheduledSyncs) {
- mScheduledSyncs.put(op.jobId, op);
- }
- }
-
- private void removeSyncOperationFromCache(int jobId) {
- synchronized (mScheduledSyncs) {
- mScheduledSyncs.remove(jobId);
- }
- }
-
- private List<SyncOperation> getAllPendingSyncsFromCache() {
- synchronized (mScheduledSyncs) {
- List<SyncOperation> pending = new ArrayList<SyncOperation>(mScheduledSyncs.size());
- for (int i=0; i<mScheduledSyncs.size(); i++) {
- pending.add(mScheduledSyncs.valueAt(i));
+ private List<SyncOperation> getAllPendingSyncs() {
+ verifyJobScheduler();
+ List<JobInfo> pendingJobs = mJobSchedulerInternal.getSystemScheduledPendingJobs();
+ List<SyncOperation> pendingSyncs = new ArrayList<SyncOperation>(pendingJobs.size());
+ for (JobInfo job: pendingJobs) {
+ SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
+ if (op != null) {
+ pendingSyncs.add(op);
}
- return pending;
}
+ return pendingSyncs;
}
private final BroadcastReceiver mStorageIntentReceiver =
@@ -436,7 +424,7 @@
mSyncHandler.postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
Set<String> cleanedKeys = new HashSet<String>();
for (SyncOperation opx: ops) {
if (cleanedKeys.contains(opx.key)) {
@@ -448,7 +436,6 @@
continue;
}
if (opx.key.equals(opy.key)) {
- removeSyncOperationFromCache(opy.jobId);
mJobScheduler.cancel(opy.jobId);
}
}
@@ -466,18 +453,16 @@
}
mJobScheduler = (JobScheduler) mContext.getSystemService(
Context.JOB_SCHEDULER_SERVICE);
+ mJobSchedulerInternal = LocalServices.getService(JobSchedulerInternal.class);
// Get all persisted syncs from JobScheduler
List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
- synchronized (mScheduledSyncs) {
- for (JobInfo job : pendingJobs) {
- SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
- if (op != null) {
- mScheduledSyncs.put(op.jobId, op);
- if (!op.isPeriodic) {
- // Set the pending status of this EndPoint to true. Pending icon is
- // shown on the settings activity.
- mSyncStorageEngine.markPending(op.target, true);
- }
+ for (JobInfo job : pendingJobs) {
+ SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
+ if (op != null) {
+ if (!op.isPeriodic) {
+ // Set the pending status of this EndPoint to true. Pending icon is
+ // shown on the settings activity.
+ mSyncStorageEngine.markPending(op.target, true);
}
}
}
@@ -700,7 +685,7 @@
}
private void setAuthorityPendingState(EndPoint info) {
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (!op.isPeriodic && op.target.matchesSpec(info)) {
getSyncStorageEngine().markPending(info, true);
@@ -920,11 +905,10 @@
private void removeSyncsForAuthority(EndPoint info) {
verifyJobScheduler();
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (op.target.matchesSpec(info)) {
- removeSyncOperationFromCache(op.jobId);
- getJobScheduler().cancel(op.jobId);
+ getJobScheduler().cancel(op.jobId);
}
}
}
@@ -954,7 +938,7 @@
* Get a list of periodic syncs corresponding to the given target.
*/
public List<PeriodicSync> getPeriodicSyncs(EndPoint target) {
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
List<PeriodicSync> periodicSyncs = new ArrayList<PeriodicSync>();
for (SyncOperation op: ops) {
@@ -1138,12 +1122,11 @@
* to current backoff and delayUntil values of this EndPoint.
*/
private void rescheduleSyncs(EndPoint target) {
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
int count = 0;
for (SyncOperation op: ops) {
if (!op.isPeriodic && op.target.matchesSpec(target)) {
count++;
- removeSyncOperationFromCache(op.jobId);
getJobScheduler().cancel(op.jobId);
postScheduleSyncMessage(op);
}
@@ -1244,7 +1227,7 @@
int duplicatesCount = 0;
long now = SystemClock.elapsedRealtime();
syncOperation.expectedRuntime = now + minDelay;
- List<SyncOperation> pending = getAllPendingSyncsFromCache();
+ List<SyncOperation> pending = getAllPendingSyncs();
SyncOperation opWithLeastExpectedRuntime = syncOperation;
for (SyncOperation op : pending) {
if (op.isPeriodic) {
@@ -1269,7 +1252,6 @@
if (isLoggable) {
Slog.v(TAG, "Cancelling duplicate sync " + op);
}
- removeSyncOperationFromCache(op.jobId);
getJobScheduler().cancel(op.jobId);
}
}
@@ -1287,7 +1269,6 @@
if (syncOperation.jobId == SyncOperation.NO_JOB_ID) {
syncOperation.jobId = getUnusedJobIdH();
}
- addSyncOperationToCache(syncOperation);
if (isLoggable) {
Slog.v(TAG, "scheduling sync operation " + syncOperation.toString());
@@ -1328,10 +1309,9 @@
* have null account/provider info to specify all accounts/providers.
*/
public void clearScheduledSyncOperations(SyncStorageEngine.EndPoint info) {
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (!op.isPeriodic && op.target.matchesSpec(info)) {
- removeSyncOperationFromCache(op.jobId);
getJobScheduler().cancel(op.jobId);
getSyncStorageEngine().markPending(op.target, false);
}
@@ -1346,11 +1326,10 @@
* @param extras extras bundle to uniquely identify sync.
*/
public void cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras) {
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (!op.isPeriodic && op.target.matchesSpec(info)
&& syncExtrasEquals(extras, op.extras, false)) {
- removeSyncOperationFromCache(op.jobId);
getJobScheduler().cancel(op.jobId);
}
}
@@ -1459,10 +1438,9 @@
// Clean up the storage engine database
mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (op.target.userId == userId) {
- removeSyncOperationFromCache(op.jobId);
getJobScheduler().cancel(op.jobId);
}
}
@@ -1628,7 +1606,7 @@
protected void dumpPendingSyncs(PrintWriter pw) {
pw.println("Pending Syncs:");
- List<SyncOperation> pendingSyncs = getAllPendingSyncsFromCache();
+ List<SyncOperation> pendingSyncs = getAllPendingSyncs();
int count = 0;
for (SyncOperation op: pendingSyncs) {
if (!op.isPeriodic) {
@@ -1642,7 +1620,7 @@
protected void dumpPeriodicSyncs(PrintWriter pw) {
pw.println("Periodic Syncs:");
- List<SyncOperation> pendingSyncs = getAllPendingSyncsFromCache();
+ List<SyncOperation> pendingSyncs = getAllPendingSyncs();
int count = 0;
for (SyncOperation op: pendingSyncs) {
if (op.isPeriodic) {
@@ -2252,10 +2230,6 @@
private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
synchronized (this) {
if (!mBootCompleted || !mProvisioned) {
- if (msg.what == MESSAGE_START_SYNC) {
- SyncOperation op = (SyncOperation) msg.obj;
- addSyncOperationToCache(op);
- }
// Need to copy the message bc looper will recycle it.
Message m = Message.obtain(msg);
mUnreadyQueue.add(m);
@@ -2472,7 +2446,6 @@
if (op.isPeriodic) {
scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
} else {
- removeSyncOperationFromCache(op.jobId);
scheduleSyncOperationH(op, delay);
}
}
@@ -2482,7 +2455,6 @@
if (op.isPeriodic) {
scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
} else {
- removeSyncOperationFromCache(op.jobId);
scheduleSyncOperationH(op, delay);
}
}
@@ -2508,7 +2480,7 @@
if (op.isPeriodic) {
// Don't allow this periodic to run if a previous instance failed and is currently
// scheduled according to some backoff criteria.
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation syncOperation: ops) {
if (syncOperation.sourcePeriodicId == op.jobId) {
mSyncJobService.callJobFinished(op.jobId, false);
@@ -2528,9 +2500,6 @@
deferSyncH(op, 0 /* No minimum delay */);
return;
}
- } else {
- // Remove SyncOperation entry from mScheduledSyncs cache for non periodic jobs.
- removeSyncOperationFromCache(op.jobId);
}
// Check for conflicting syncs.
@@ -2609,10 +2578,9 @@
}
}
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (!containsAccountAndUser(accounts, op.target.account, op.target.userId)) {
- removeSyncOperationFromCache(op.jobId);
getJobScheduler().cancel(op.jobId);
}
}
@@ -2658,7 +2626,7 @@
+ " flexMillis: " + flex
+ " extras: " + extras.toString());
}
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (op.isPeriodic && op.target.matchesSpec(target)
&& syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
@@ -2697,7 +2665,7 @@
*/
private void removePeriodicSyncInternalH(SyncOperation syncOperation) {
// Remove this periodic sync and all one-off syncs initiated by it.
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) {
ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId);
@@ -2705,7 +2673,6 @@
mSyncJobService.callJobFinished(syncOperation.jobId, false);
runSyncFinishedOrCanceledH(null, asc);
}
- removeSyncOperationFromCache(op.jobId);
getJobScheduler().cancel(op.jobId);
}
}
@@ -2713,7 +2680,7 @@
private void removePeriodicSyncH(EndPoint target, Bundle extras) {
verifyJobScheduler();
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (op.isPeriodic && op.target.matchesSpec(target)
&& syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
@@ -2889,7 +2856,7 @@
private void reschedulePeriodicSyncH(SyncOperation syncOperation) {
// Ensure that the periodic sync wasn't removed.
SyncOperation periodicSync = null;
- List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<SyncOperation> ops = getAllPendingSyncs();
for (SyncOperation op: ops) {
if (op.isPeriodic && syncOperation.matchesPeriodicOperation(op)) {
periodicSync = op;
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 088d96e..4527f1f 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -381,8 +381,8 @@
| DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
}
+ final Resources res = getContext().getResources();
if (mBuiltInDisplayId == SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
- final Resources res = getContext().getResources();
mInfo.name = res.getString(
com.android.internal.R.string.display_manager_built_in_display_name);
mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
@@ -416,6 +416,11 @@
if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
}
+
+ if (!res.getBoolean(
+ com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
+ }
}
}
return mInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java b/services/core/java/com/android/server/job/JobSchedulerInternal.java
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
copy to services/core/java/com/android/server/job/JobSchedulerInternal.java
index 264c2c4..75170ec 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/DockingTopTaskEvent.java
+++ b/services/core/java/com/android/server/job/JobSchedulerInternal.java
@@ -14,18 +14,20 @@
* limitations under the License
*/
-package com.android.systemui.recents.events.activity;
+package com.android.server.job;
-import com.android.systemui.recents.events.EventBus;
+import android.app.job.JobInfo;
+
+import java.util.List;
/**
- * Fires when the user invoked the gesture to dock the top/left task.
+ * JobScheduler local system service interface.
+ * {@hide} Only for use within the system server.
*/
-public class DockingTopTaskEvent extends EventBus.Event {
+public interface JobSchedulerInternal {
- public int dragMode;
-
- public DockingTopTaskEvent(int dragMode) {
- this.dragMode = dragMode;
- }
+ /**
+ * Returns a list of pending jobs scheduled by the system service.
+ */
+ List<JobInfo> getSystemScheduledPendingJobs();
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index fba7e7d..cee46195 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -45,16 +45,18 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
-
import android.util.SparseIntArray;
import android.util.TimeUtils;
+
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.ArrayUtils;
import com.android.internal.app.ProcessStats;
@@ -504,6 +506,7 @@
@Override
public void onStart() {
+ publishLocalService(JobSchedulerInternal.class, new LocalService());
publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
}
@@ -1177,6 +1180,28 @@
return -1;
}
+ final class LocalService implements JobSchedulerInternal {
+
+ /**
+ * Returns a list of all pending jobs. A running job is not considered pending. Periodic
+ * jobs are always considered pending.
+ */
+ public List<JobInfo> getSystemScheduledPendingJobs() {
+ synchronized (mLock) {
+ final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
+ mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
+ @Override
+ public void process(JobStatus job) {
+ if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
+ pendingJobs.add(job.getJob());
+ }
+ }
+ });
+ return pendingJobs;
+ }
+ }
+ }
+
/**
* Binder stub trampoline implementation
*/
@@ -1337,8 +1362,48 @@
Binder.restoreCallingIdentity(identityToken);
}
}
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ResultReceiver resultReceiver) throws RemoteException {
+ (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
+ this, in, out, err, args, resultReceiver);
+ }
};
+ // Shell command infrastructure: run the given job immediately
+ int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
+ if (DEBUG) {
+ Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
+ + " " + jobId + " f=" + force);
+ }
+
+ try {
+ final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
+ if (uid < 0) {
+ return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
+ }
+
+ synchronized (mLock) {
+ final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
+ if (js == null) {
+ return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
+ }
+
+ js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
+ if (!js.isConstraintsSatisfied()) {
+ js.overrideState = 0;
+ return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
+ }
+
+ mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
+ }
+ } catch (RemoteException e) {
+ // can't happen
+ }
+ return 0;
+ }
+
private String printContextIdToJobMap(JobStatus[] map, String initial) {
StringBuilder s = new StringBuilder(initial + ": ");
for (int i=0; i<map.length; i++) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
new file mode 100644
index 0000000..2d62c1c
--- /dev/null
+++ b/services/core/java/com/android/server/job/JobSchedulerShellCommand.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 com.android.server.job;
+
+import android.app.AppGlobals;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+
+public class JobSchedulerShellCommand extends ShellCommand {
+ public static final int CMD_ERR_NO_PACKAGE = -1000;
+ public static final int CMD_ERR_NO_JOB = -1001;
+ public static final int CMD_ERR_CONSTRAINTS = -1002;
+
+ JobSchedulerService mInternal;
+ IPackageManager mPM;
+
+ JobSchedulerShellCommand(JobSchedulerService service) {
+ mInternal = service;
+ mPM = AppGlobals.getPackageManager();
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ if ("run".equals(cmd)) {
+ return runJob();
+ } else {
+ return handleDefaultCommands(cmd);
+ }
+ } catch (Exception e) {
+ pw.println("Exception: " + e);
+ }
+ return -1;
+ }
+
+ private int runJob() {
+ try {
+ final int uid = Binder.getCallingUid();
+ final int perm = mPM.checkUidPermission(
+ "android.permission.CHANGE_APP_IDLE_STATE", uid);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Uid " + uid
+ + " not permitted to force scheduled jobs");
+ }
+ } catch (RemoteException e) {
+ // Can't happen
+ }
+
+ final PrintWriter pw = getOutPrintWriter();
+ boolean force = false;
+ int userId = UserHandle.USER_SYSTEM;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-f":
+ case "--force":
+ force = true;
+ break;
+
+ case "-u":
+ case "--user":
+ userId = Integer.parseInt(getNextArgRequired());
+ break;
+
+ default:
+ pw.println("Error: unknown option '" + opt + "'");
+ return -1;
+ }
+ }
+
+ final String pkgName = getNextArgRequired();
+ final int jobId = Integer.parseInt(getNextArgRequired());
+
+ int ret = mInternal.executeRunCommand(pkgName, userId, jobId, force);
+ switch (ret) {
+ case CMD_ERR_NO_PACKAGE:
+ pw.print("Package not found: ");
+ pw.print(pkgName);
+ pw.print(" / user ");
+ pw.println(userId);
+ break;
+
+ case CMD_ERR_NO_JOB:
+ pw.print("Could not find job ");
+ pw.print(jobId);
+ pw.print(" in package ");
+ pw.print(pkgName);
+ pw.print(" / user ");
+ pw.println(userId);
+ break;
+
+ case CMD_ERR_CONSTRAINTS:
+ pw.print("Job ");
+ pw.print(jobId);
+ pw.print(" in package ");
+ pw.print(pkgName);
+ pw.print(" / user ");
+ pw.print(userId);
+ pw.println(" has functional constraints but --force not specified");
+ break;
+
+ default:
+ // success!
+ pw.print("Running job");
+ if (force) {
+ pw.print(" [FORCED]");
+ }
+ pw.println();
+ break;
+ }
+ return ret;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+
+ pw.println("Job scheduler (jobscheduler) commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" run [-f | --force] [-u | --user USER_ID] PACKAGE JOB_ID");
+ pw.println(" Trigger immediate execution of a specific scheduled job.");
+ pw.println(" Options:");
+ pw.println(" -f or --force: run the job even if technical constraints such as");
+ pw.println(" connectivity are not currently met");
+ pw.println(" -u or --user: specify which user's job is to be run; the default is");
+ pw.println(" the primary or system user");
+ pw.println();
+ }
+
+}
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index 9837a56..55f37b8 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -210,6 +210,10 @@
mJobSet.forEachJob(functor);
}
+ public void forEachJob(int uid, JobStatusFunctor functor) {
+ mJobSet.forEachJob(uid, functor);
+ }
+
public interface JobStatusFunctor {
public void process(JobStatus jobStatus);
}
@@ -870,5 +874,14 @@
}
}
}
+
+ public void forEachJob(int uid, JobStatusFunctor functor) {
+ ArraySet<JobStatus> jobs = mJobs.get(uid);
+ if (jobs != null) {
+ for (int i = jobs.size() - 1; i >= 0; i--) {
+ functor.process(jobs.valueAt(i));
+ }
+ }
+ }
}
}
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 98bf8a9..4a2c88c 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -55,6 +55,11 @@
static final int CONSTRAINT_APP_NOT_IDLE = 1<<6;
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<7;
+ // Soft override: ignore constraints like time that don't affect API availability
+ public static final int OVERRIDE_SOFT = 1;
+ // Full override: ignore all constraints including API-affecting like connectivity
+ public static final int OVERRIDE_FULL = 2;
+
final JobInfo job;
/** Uid of the package requesting this job. */
final int callingUid;
@@ -91,6 +96,9 @@
public int lastEvaluatedPriority;
+ // Used by shell commands
+ public int overrideState = 0;
+
/**
* For use only by ContentObserverController: state it is maintaining about content URIs
* being observed.
@@ -370,7 +378,7 @@
*/
public boolean isReady() {
// Deadline constraint trumps other constraints (except for periodic jobs where deadline
- // (is an implementation detail. A periodic job should only run if it's constraints are
+ // is an implementation detail. A periodic job should only run if its constraints are
// satisfied).
// AppNotIdle implicit constraint trumps all!
return (isConstraintsSatisfied()
@@ -384,12 +392,27 @@
CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED |
CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
+ // Soft override covers all non-"functional" constraints
+ static final int SOFT_OVERRIDE_CONSTRAINTS =
+ CONSTRAINT_CHARGING | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE;
+
/**
* @return Whether the constraints set on this job are satisfied.
*/
public boolean isConstraintsSatisfied() {
+ if (overrideState == OVERRIDE_FULL) {
+ // force override: the job is always runnable
+ return true;
+ }
+
final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
- final int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+
+ int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
+ if (overrideState == OVERRIDE_SOFT) {
+ // override: pretend all 'soft' requirements are satisfied
+ sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
+ }
+
return (sat & req) == req;
}
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index 9f1435a..2807ec8 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -40,7 +40,7 @@
public class IpConfigStore {
private static final String TAG = "IpConfigStore";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
protected final DelayedDiskWrite mWriter;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 09b7a18..3acd2ca 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -42,6 +42,7 @@
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
@@ -68,6 +69,7 @@
import static android.net.wifi.WifiManager.EXTRA_WIFI_CONFIGURATION;
import static android.net.wifi.WifiManager.EXTRA_WIFI_INFO;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+
import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
@@ -294,6 +296,7 @@
final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray();
final SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
+ final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
/** Set of states for the child firewall chains. True if the chain is active. */
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
@@ -522,9 +525,11 @@
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);
}
}
@@ -1175,13 +1180,6 @@
return;
}
- // If we are in restrict power mode, we want to treat all interfaces
- // as metered, to restrict access to the network by uid. However, we
- // will not have a bandwidth limit. Also only do this if restrict
- // background data use is *not* enabled, since that takes precedence
- // use over those networks can have a cost associated with it).
- final boolean powerSave = mRestrictPower && !mRestrictBackground;
-
// First, generate identities of all connected networks so we can
// quickly compare them against all defined policies below.
final ArrayList<Pair<String, NetworkIdentity>> connIdents = new ArrayList<>(states.length);
@@ -1193,9 +1191,6 @@
final String baseIface = state.linkProperties.getInterfaceName();
if (baseIface != null) {
connIdents.add(Pair.create(baseIface, ident));
- if (powerSave) {
- connIfaces.add(baseIface);
- }
}
// Stacked interfaces are considered to have same identity as
@@ -1205,9 +1200,6 @@
final String stackedIface = stackedLink.getInterfaceName();
if (stackedIface != null) {
connIdents.add(Pair.create(stackedIface, ident));
- if (powerSave) {
- connIfaces.add(stackedIface);
- }
}
}
}
@@ -1254,8 +1246,7 @@
}
if (LOGD) {
- Slog.d(TAG, "applying policy " + policy.toString() + " to ifaces "
- + Arrays.toString(ifaces));
+ Slog.d(TAG, "applying policy " + policy + " to ifaces " + Arrays.toString(ifaces));
}
final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED;
@@ -1286,9 +1277,6 @@
removeInterfaceQuota(iface);
setInterfaceQuota(iface, quotaBytes);
newMeteredIfaces.add(iface);
- if (powerSave) {
- connIfaces.remove(iface);
- }
}
}
@@ -1631,7 +1619,7 @@
try {
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, policy, true);
+ setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1651,7 +1639,7 @@
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
policy |= oldPolicy;
if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, policy, true);
+ setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
}
}
}
@@ -1668,11 +1656,22 @@
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
policy = oldPolicy & ~policy;
if (oldPolicy != policy) {
- setUidPolicyUncheckedLocked(uid, policy, true);
+ setUidPolicyUncheckedLocked(uid, oldPolicy, policy, true);
}
}
}
+ private void setUidPolicyUncheckedLocked(int uid, int oldPolicy, int policy, boolean persist) {
+ setUidPolicyUncheckedLocked(uid, policy, persist);
+
+ // Checks if app was added or removed to the blacklist.
+ if ((oldPolicy == POLICY_NONE && policy == POLICY_REJECT_METERED_BACKGROUND)
+ || (oldPolicy == POLICY_REJECT_METERED_BACKGROUND && policy == POLICY_NONE)) {
+ mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0)
+ .sendToTarget();
+ }
+ }
+
private void setUidPolicyUncheckedLocked(int uid, int policy, boolean persist) {
mUidPolicy.put(uid, policy);
@@ -2000,7 +1999,20 @@
public int getRestrictBackgroundByCaller() {
mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
final int uid = Binder.getCallingUid();
+
synchronized (mRulesLock) {
+ // Must clear identity because getUidPolicy() is restricted to system.
+ final long token = Binder.clearCallingIdentity();
+ final int policy;
+ try {
+ policy = getUidPolicy(uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (policy == POLICY_REJECT_METERED_BACKGROUND) {
+ // App is blacklisted.
+ return RESTRICT_BACKGROUND_STATUS_ENABLED;
+ }
if (!mRestrictBackground) {
return RESTRICT_BACKGROUND_STATUS_DISABLED;
}
@@ -2299,9 +2311,14 @@
// state changed, push updated rules
mUidState.put(uid, uidState);
updateRulesForUidStateChangeLocked(uid, oldUidState, uidState);
- if (mDeviceIdleMode && isProcStateAllowedWhileIdle(oldUidState)
- != isProcStateAllowedWhileIdle(uidState)) {
- updateRuleForDeviceIdleLocked(uid);
+ if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
+ != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) {
+ if (mDeviceIdleMode) {
+ updateRuleForDeviceIdleLocked(uid);
+ }
+ if (mRestrictPower) {
+ updateRulesForRestrictPowerLocked(uid);
+ }
}
}
}
@@ -2317,6 +2334,9 @@
if (mDeviceIdleMode) {
updateRuleForDeviceIdleLocked(uid);
}
+ if (mRestrictPower) {
+ updateRulesForRestrictPowerLocked(uid);
+ }
}
}
}
@@ -2354,15 +2374,36 @@
}
}
- static boolean isProcStateAllowedWhileIdle(int procState) {
+ static boolean isProcStateAllowedWhileIdleOrPowerSaveMode(int procState) {
return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
}
+ void updateRulesForRestrictPowerLocked() {
+ updateRulesForWhitelistedPowerSaveLocked(mRestrictPower, FIREWALL_CHAIN_POWERSAVE,
+ mUidFirewallPowerSaveRules);
+ }
+
+ void updateRulesForRestrictPowerLocked(int uid) {
+ updateRulesForWhitelistedPowerSaveLocked(uid, mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
+ }
+
void updateRulesForDeviceIdleLocked() {
- if (mDeviceIdleMode) {
- // sync the whitelists before enable dozable chain. We don't care about the rules if
+ updateRulesForWhitelistedPowerSaveLocked(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE,
+ mUidFirewallDozableRules);
+ }
+
+ void updateRuleForDeviceIdleLocked(int uid) {
+ updateRulesForWhitelistedPowerSaveLocked(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,
+ SparseIntArray rules) {
+ if (enabled) {
+ // Sync the whitelists before enabling the chain. We don't care about the rules if
// we are disabling the chain.
- final SparseIntArray uidRules = mUidFirewallDozableRules;
+ final SparseIntArray uidRules = rules;
uidRules.clear();
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
@@ -2381,24 +2422,26 @@
}
}
for (int i = mUidState.size() - 1; i >= 0; i--) {
- if (isProcStateAllowedWhileIdle(mUidState.valueAt(i))) {
+ if (isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.valueAt(i))) {
uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW);
}
}
- setUidFirewallRules(FIREWALL_CHAIN_DOZABLE, uidRules);
+ setUidFirewallRules(chain, uidRules);
}
- enableFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode);
+ enableFirewallChainLocked(chain, enabled);
}
- void updateRuleForDeviceIdleLocked(int uid) {
- if (mDeviceIdleMode) {
+ // 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) {
+ if (enabled) {
int appId = UserHandle.getAppId(uid);
if (mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId)
- || isProcStateAllowedWhileIdle(mUidState.get(uid))) {
- setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_ALLOW);
+ || isProcStateAllowedWhileIdleOrPowerSaveMode(mUidState.get(uid))) {
+ setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
} else {
- setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT);
+ setUidFirewallRule(chain, uid, FIREWALL_RULE_DEFAULT);
}
}
@@ -2454,10 +2497,14 @@
* {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value.
*/
void updateRulesForGlobalChangeLocked(boolean restrictedNetworksChanged) {
+ long start;
+ if (LOGD) start = System.currentTimeMillis();
+
final PackageManager pm = mContext.getPackageManager();
updateRulesForDeviceIdleLocked();
updateRulesForAppIdleLocked();
+ updateRulesForRestrictPowerLocked();
// update rules for all installed applications
final List<UserInfo> users = mUserManager.getUsers();
@@ -2465,8 +2512,12 @@
PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
- for (UserInfo user : users) {
- for (ApplicationInfo app : apps) {
+ final int usersSize = users.size();
+ final int appsSize = apps.size();
+ for (int i = 0; i < usersSize; i++) {
+ final UserInfo user = users.get(i);
+ for (int j = 0; j < appsSize; j++) {
+ final ApplicationInfo app = apps.get(j);
final int uid = UserHandle.getUid(user.id, app.uid);
updateRulesForUidLocked(uid);
}
@@ -2481,16 +2532,23 @@
normalizePoliciesLocked();
updateNetworkRulesLocked();
}
+ if (LOGD) {
+ final long delta = System.currentTimeMillis() - start;
+ Slog.d(TAG, "updateRulesForGlobalChangeLocked(" + restrictedNetworksChanged + ") took "
+ + delta + "ms");
+ }
}
void updateRulesForTempWhitelistChangeLocked() {
final List<UserInfo> users = mUserManager.getUsers();
- for (UserInfo user : users) {
- for (int i = mPowerSaveTempWhitelistAppIds.size() - 1; i >= 0; i--) {
- int appId = mPowerSaveTempWhitelistAppIds.keyAt(i);
+ for (int i = 0; i < users.size(); i++) {
+ final UserInfo user = users.get(i);
+ for (int j = mPowerSaveTempWhitelistAppIds.size() - 1; j >= 0; j--) {
+ int appId = mPowerSaveTempWhitelistAppIds.keyAt(j);
int uid = UserHandle.getUid(user.id, appId);
updateRuleForAppIdleLocked(uid);
updateRuleForDeviceIdleLocked(uid);
+ updateRulesForRestrictPowerLocked(uid);
}
}
}
@@ -2583,6 +2641,12 @@
uidRules = RULE_REJECT_ALL;
}
+ // Check powersave state, which is whitelist
+ if (mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE)
+ && mUidFirewallPowerSaveRules.get(uid, FIREWALL_RULE_DEFAULT) != FIREWALL_RULE_ALLOW) {
+ uidRules = RULE_REJECT_ALL;
+ }
+
// Check standby state, which is blacklist
if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)
&& mUidFirewallStandbyRules.get(uid, FIREWALL_RULE_DEFAULT) == FIREWALL_RULE_DENY) {
@@ -2810,6 +2874,8 @@
mUidFirewallDozableRules.put(uid, rule);
} else if (chain == FIREWALL_CHAIN_STANDBY) {
mUidFirewallStandbyRules.put(uid, rule);
+ } else if (chain == FIREWALL_CHAIN_POWERSAVE) {
+ mUidFirewallPowerSaveRules.put(uid, rule);
}
try {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index a626b5b..9cfb590 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -24,6 +24,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -364,9 +365,12 @@
private List<NetworkPolicy> getWifiPolicies() throws RemoteException {
// First gets a list of saved wi-fi networks.
final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
- final Set<String> ssids = new HashSet<>(configs.size());
- for (WifiConfiguration config : configs) {
- ssids.add(removeDoubleQuotes(config.SSID));
+ final int size = configs != null ? configs.size() : 0;
+ final Set<String> ssids = new HashSet<>(size);
+ if (configs != null) {
+ for (WifiConfiguration config : configs) {
+ ssids.add(removeDoubleQuotes(config.SSID));
+ }
}
// Then gets the saved policies.
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index d986e94b..673dd8f 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -17,8 +17,8 @@
package com.android.server.net;
import static android.net.NetworkStats.IFACE_ALL;
-import static android.net.NetworkStats.ROAMING_DEFAULT;
-import static android.net.NetworkStats.ROAMING_ROAMING;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.ROAMING_YES;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
@@ -242,7 +242,7 @@
entry.uid = key.uid;
entry.set = key.set;
entry.tag = key.tag;
- entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_ROAMING : ROAMING_DEFAULT;
+ entry.roaming = key.ident.isAnyMemberRoaming() ? ROAMING_YES : ROAMING_NO;
entry.rxBytes = historyEntry.rxBytes;
entry.rxPackets = historyEntry.rxPackets;
entry.txBytes = historyEntry.txBytes;
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 9820a12..c19b51f 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import android.app.AutomaticZenRule;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -44,7 +45,6 @@
public class ConditionProviders extends ManagedServices {
private final ArrayList<ConditionRecord> mRecords = new ArrayList<>();
- private final ArrayMap<IBinder, IConditionListener> mListeners = new ArrayMap<>();
private final ArraySet<String> mSystemConditionProviderNames;
private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
= new ArraySet<>();
@@ -103,12 +103,6 @@
}
}
}
- if (filter == null) {
- pw.print(" mListeners("); pw.print(mListeners.size()); pw.println("):");
- for (int i = 0; i < mListeners.size(); i++) {
- pw.print(" "); pw.println(mListeners.keyAt(i));
- }
- }
pw.print(" mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames);
for (int i = 0; i < mSystemConditionProviders.size(); i++) {
mSystemConditionProviders.valueAt(i).dump(pw, filter);
@@ -173,16 +167,12 @@
}
}
- private Condition[] validateConditions(String pkg, Condition[] conditions) {
+ private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) {
if (conditions == null || conditions.length == 0) return null;
final int N = conditions.length;
final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
for (int i = 0; i < N; i++) {
final Uri id = conditions[i].id;
- if (!Condition.isValidId(id, pkg)) {
- Slog.w(TAG, "Ignoring condition from " + pkg + " for invalid id: " + id);
- continue;
- }
if (valid.containsKey(id)) {
Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
continue;
@@ -219,16 +209,9 @@
synchronized(mMutex) {
if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
+ (conditions == null ? null : Arrays.asList(conditions)));
- conditions = validateConditions(pkg, conditions);
+ conditions = removeDuplicateConditions(pkg, conditions);
if (conditions == null || conditions.length == 0) return;
final int N = conditions.length;
- for (IConditionListener listener : mListeners.values()) {
- try {
- listener.onConditionsReceived(conditions);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error sending conditions to listener " + listener, e);
- }
- }
for (int i = 0; i < N; i++) {
final Condition c = conditions[i];
final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index ab3cb83..b13fec1 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -26,7 +26,9 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.Condition;
@@ -63,15 +65,18 @@
private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>();
private final SparseArray<CalendarTracker> mTrackers = new SparseArray<>();
private final Handler mWorker;
+ private final HandlerThread mThread;
private boolean mConnected;
private boolean mRegistered;
private boolean mBootComplete; // don't hammer the calendar provider until boot completes.
private long mNextAlarmTime;
- public EventConditionProvider(Looper worker) {
+ public EventConditionProvider() {
if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()");
- mWorker = new Handler(worker);
+ mThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
+ mThread.start();
+ mWorker = new Handler(mThread.getLooper());
}
@Override
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 0d6e3e5..6cf3940 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -301,7 +302,9 @@
* */
public void registerGuestService(ManagedServiceInfo guest) {
checkNotNull(guest.service);
- checkType(guest.service);
+ if (!checkType(guest.service)) {
+ throw new IllegalArgumentException();
+ }
if (registerServiceImpl(guest) != null) {
onServiceAdded(guest);
}
@@ -325,12 +328,16 @@
component.flattenToShortString());
}
- final int[] userIds = mUserProfiles.getCurrentProfileIds();
- for (int userId : userIds) {
- if (enabled) {
- registerServiceLocked(component, userId);
- } else {
- unregisterServiceLocked(component, userId);
+
+ synchronized (mMutex) {
+ final int[] userIds = mUserProfiles.getCurrentProfileIds();
+
+ for (int userId : userIds) {
+ if (enabled) {
+ registerServiceLocked(component, userId);
+ } else {
+ unregisterServiceLocked(component, userId);
+ }
}
}
}
@@ -450,7 +457,7 @@
return queryPackageForServices(packageName, userId, null);
}
- protected Set<ComponentName> queryPackageForServices(String packageName, int userId,
+ public Set<ComponentName> queryPackageForServices(String packageName, int userId,
String category) {
Set<ComponentName> installed = new ArraySet<>();
final PackageManager pm = mContext.getPackageManager();
@@ -633,7 +640,21 @@
}
}
+ /**
+ * Inject a system service into the management list.
+ */
+ public void registerSystemService(final ComponentName name, final int userid) {
+ synchronized (mMutex) {
+ registerServiceLocked(name, userid, true /* isSystem */);
+ }
+ }
+
private void registerServiceLocked(final ComponentName name, final int userid) {
+ registerServiceLocked(name, userid, false /* isSystem */);
+ }
+
+ private void registerServiceLocked(final ComponentName name, final int userid,
+ final boolean isSystem) {
if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
final String servicesBindingTag = name.toString() + "/" + userid;
@@ -691,7 +712,7 @@
try {
mService = asInterface(binder);
info = newServiceInfo(mService, name,
- userid, false /*isSystem*/, this, targetSdkVersion);
+ userid, isSystem, this, targetSdkVersion);
binder.linkToDeath(info, 0);
added = mServices.add(info);
} catch (RemoteException e) {
@@ -887,6 +908,7 @@
return false;
}
if (this.userid == UserHandle.USER_ALL) return true;
+ if (this.userid == UserHandle.USER_SYSTEM) return true;
if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
return supportsProfiles() && mUserProfiles.isCurrentProfile(nid);
}
@@ -920,9 +942,9 @@
public static class UserProfiles {
// Profiles of the current user.
- private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
+ private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
- public void updateCache(Context context) {
+ public void updateCache(@NonNull Context context) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
if (userManager != null) {
int currentUserId = ActivityManager.getCurrentUser();
@@ -954,12 +976,12 @@
}
}
- protected static class Config {
- String caption;
- String serviceInterface;
- String secureSettingName;
- String bindPermission;
- String settingsAction;
- int clientLabel;
+ public static class Config {
+ public String caption;
+ public String serviceInterface;
+ public String secureSettingName;
+ public String bindPermission;
+ public String settingsAction;
+ public int clientLabel;
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index b57cc75..bcdeb66 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -23,7 +23,7 @@
import android.util.Slog;
/**
- * This {@link com.android.server.notification.NotificationSignalExtractor} noticies noisy
+ * This {@link com.android.server.notification.NotificationSignalExtractor} notices noisy
* notifications and marks them to get a temporary ranking bump.
*/
public class NotificationIntrusivenessExtractor implements NotificationSignalExtractor {
@@ -44,9 +44,15 @@
return null;
}
- final Notification notification = record.getNotification();
- if (record.getImportance() > NotificationListenerService.Ranking.IMPORTANCE_DEFAULT) {
- record.setRecentlyIntrusive(true);
+ if (record.getImportance() >= NotificationListenerService.Ranking.IMPORTANCE_DEFAULT) {
+ final Notification notification = record.getNotification();
+ if ((notification.defaults & Notification.DEFAULT_VIBRATE) != 0 ||
+ notification.vibrate != null ||
+ (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
+ notification.sound != null ||
+ notification.fullScreenIntent != null) {
+ record.setRecentlyIntrusive(true);
+ }
}
return new RankingReconsideration(record.getKey(), HANG_TIME_MS) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3855579..516602e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,21 +16,21 @@
package com.android.server.notification;
-import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL_ALL;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CANCEL;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CANCEL_ALL;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CLICK;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_ERROR;
-import static android.service.notification.NotificationAssistantService.REASON_GROUP_OPTIMIZATION;
-import static android.service.notification.NotificationAssistantService.REASON_GROUP_SUMMARY_CANCELED;
-import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL;
-import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL_ALL;
-import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_BANNED;
-import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_CHANGED;
-import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_SUSPENDED;
-import static android.service.notification.NotificationAssistantService.REASON_PROFILE_TURNED_OFF;
-import static android.service.notification.NotificationAssistantService.REASON_USER_STOPPED;
+import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR;
+import static android.service.notification.NotificationRankerService.REASON_GROUP_OPTIMIZATION;
+import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED;
+import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL;
+import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED;
+import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
+import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
+import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
+import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
@@ -100,7 +100,7 @@
import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
import android.service.notification.IStatusBarNotificationHolder;
-import android.service.notification.NotificationAssistantService;
+import android.service.notification.NotificationRankerService;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.StatusBarNotification;
@@ -128,10 +128,9 @@
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
-import com.android.server.notification.ManagedServices.UserProfiles;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
-import com.android.server.vr.VrStateListener;
+import com.android.server.notification.ManagedServices.UserProfiles;
import libcore.io.IoUtils;
@@ -161,6 +160,7 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
/** {@hide} */
@@ -213,6 +213,7 @@
/** notification_enqueue status value for an ignored notification. */
private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
+ private String mRankerServicePackageName;
private IActivityManager mAm;
AudioManager mAudioManager;
@@ -220,14 +221,11 @@
StatusBarManagerInternal mStatusBar;
Vibrator mVibrator;
private VrManagerInternal mVrManagerInternal;
- private final NotificationVrListener mVrListener = new NotificationVrListener();
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
private final HandlerThread mRankingThread = new HandlerThread("ranker",
Process.THREAD_PRIORITY_BACKGROUND);
- private final HandlerThread mAssistantThread = new HandlerThread("assistant",
- Process.THREAD_PRIORITY_BACKGROUND);
private Light mNotificationLight;
Light mAttentionLight;
@@ -293,14 +291,13 @@
private final UserProfiles mUserProfiles = new UserProfiles();
private NotificationListeners mListeners;
- private NotificationAssistant mAssistant;
+ private NotificationRankers mRankerServices;
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
private RankingHandler mRankingHandler;
- private Handler mAssistantHandler;
private static class Archive {
final int mBufferSize;
@@ -758,7 +755,7 @@
}
}
mListeners.onPackagesChanged(queryReplace, pkgList);
- mAssistant.onPackagesChanged(queryReplace, pkgList);
+ mRankerServices.onPackagesChanged(queryReplace, pkgList);
mConditionProviders.onPackagesChanged(queryReplace, pkgList);
mRankingHelper.onPackagesChanged(queryReplace, pkgList);
}
@@ -807,7 +804,7 @@
// Refresh managed services
mConditionProviders.onUserSwitched(user);
mListeners.onUserSwitched(user);
- mAssistant.onUserSwitched(user);
+ mRankerServices.onUserSwitched(user);
mZenModeHelper.onUserSwitched(user);
} else if (action.equals(Intent.ACTION_USER_ADDED)) {
mUserProfiles.updateCache(context);
@@ -818,7 +815,7 @@
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
mConditionProviders.onUserUnlocked(user);
mListeners.onUserUnlocked(user);
- mAssistant.onUserUnlocked(user);
+ mRankerServices.onUserUnlocked(user);
mZenModeHelper.onUserUnlocked(user);
}
}
@@ -856,14 +853,6 @@
}
}
- private final class NotificationVrListener extends VrStateListener {
- @Override
- public void onVrStateChanged(final boolean enabled) {
- mListeners.setCategoryState(NotificationListenerService.CATEGORY_VR_NOTIFICATIONS,
- enabled);
- }
- }
-
private SettingsObserver mSettingsObserver;
private ZenModeHelper mZenModeHelper;
@@ -900,9 +889,12 @@
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+ // This is the package that contains the AOSP framework update.
+ mRankerServicePackageName = getContext().getPackageManager()
+ .getServicesSystemSharedLibraryPackageName();
+
mHandler = new WorkerHandler();
mRankingThread.start();
- mAssistantThread.start();
String[] extractorNames;
try {
extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
@@ -911,7 +903,6 @@
}
mUsageStats = new NotificationUsageStats(getContext());
mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
- mAssistantHandler = new Handler(mAssistantThread.getLooper());
mRankingHelper = new RankingHelper(getContext(),
mRankingHandler,
mUsageStats,
@@ -946,8 +937,26 @@
importOldBlockDb();
+ // This is a MangedServices object that keeps track of the listeners.
mListeners = new NotificationListeners();
- mAssistant = new NotificationAssistant();
+
+ // This is a MangedServices object that keeps track of the ranker.
+ mRankerServices = new NotificationRankers();
+ // Find the updatable ranker and register it.
+ Set<ComponentName> rankerComponents = mRankerServices.queryPackageForServices(
+ mRankerServicePackageName, UserHandle.USER_SYSTEM, null);
+ Iterator<ComponentName> iterator = rankerComponents.iterator();
+ if (iterator.hasNext()) {
+ ComponentName rankerComponent = iterator.next();
+ if (iterator.hasNext()) {
+ Slog.e(TAG, "found multiple ranker services:" + rankerComponents);
+ } else {
+ mRankerServices.registerSystemService(rankerComponent, UserHandle.USER_SYSTEM);
+ }
+ } else {
+ Slog.w(TAG, "could not start ranker service: none found");
+ }
+
mStatusBar = getLocalService(StatusBarManagerInternal.class);
mStatusBar.setNotificationDelegate(mNotificationDelegate);
@@ -1064,14 +1073,13 @@
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
mVrManagerInternal = getLocalService(VrManagerInternal.class);
- mVrManagerInternal.registerListener(mVrListener);
mZenModeHelper.onSystemReady();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
// bind to listener services.
mSettingsObserver.observe();
mListeners.onBootPhaseAppsCanStart();
- mAssistant.onBootPhaseAppsCanStart();
+ mRankerServices.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
}
}
@@ -1435,8 +1443,8 @@
* Remove a listener binder directly
*/
@Override
- public void unregisterListener(INotificationListener listener, int userid) {
- mListeners.unregisterService(listener, userid);
+ public void unregisterListener(INotificationListener token, int userid) {
+ mListeners.unregisterService(token, userid);
}
/**
@@ -1489,8 +1497,8 @@
checkCallerIsSystemOrSameApp(component.getPackageName());
long identity = Binder.clearCallingIdentity();
try {
- ManagedServices manager = mAssistant.isComponentEnabledForCurrentProfiles(component)
- ? mAssistant
+ ManagedServices manager = mRankerServices.isComponentEnabledForCurrentProfiles(component)
+ ? mRankerServices
: mListeners;
manager.setComponentState(component, true);
} finally {
@@ -1983,7 +1991,7 @@
}
@Override
- public void setImportanceFromAssistant(INotificationListener token, String key,
+ public void setImportanceFromRankerService(INotificationListener token, String key,
int importance, CharSequence explanation) throws RemoteException {
if (importance == IMPORTANCE_NONE) {
throw new IllegalArgumentException("blocking not allowed: key=" + key);
@@ -1991,7 +1999,7 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationList) {
- mAssistant.checkServiceTokenLocked(token);
+ mRankerServices.checkServiceTokenLocked(token);
NotificationRecord n = mNotificationsByKey.get(key);
n.setImportance(importance, explanation);
mRankingHandler.requestSort();
@@ -2141,8 +2149,9 @@
pw.print(listener.component);
}
pw.println(')');
- pw.println("\n Notification assistant:");
- mAssistant.dump(pw, filter);
+ pw.println("\n mRankerServicePackageName: " + mRankerServicePackageName);
+ pw.println("\n Notification ranker services:");
+ mRankerServices.dump(pw, filter);
}
pw.println("\n Policy access:");
pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
@@ -2369,9 +2378,9 @@
}
}
- // tell the assistant about the notification
- if (mAssistant.isEnabled()) {
- mAssistant.onNotificationEnqueued(r);
+ // tell the ranker service about the notification
+ if (mRankerServices.isEnabled()) {
+ mRankerServices.onNotificationEnqueued(r);
// TODO delay the code below here for 100ms or until there is an answer
}
@@ -3488,21 +3497,21 @@
}
}
- public class NotificationAssistant extends ManagedServices {
+ public class NotificationRankers extends ManagedServices {
- public NotificationAssistant() {
+ public NotificationRankers() {
super(getContext(), mHandler, mNotificationList, mUserProfiles);
}
@Override
protected Config getConfig() {
Config c = new Config();
- c.caption = "notification assistant";
- c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
- c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
- c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
+ c.caption = "notification ranker service";
+ c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
+ c.secureSettingName = null;
+ c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
- c.clientLabel = R.string.notification_assistant_binding_label;
+ c.clientLabel = R.string.notification_ranker_binding_label;
return c;
}
@@ -3530,10 +3539,10 @@
final StatusBarNotification sbn = r.sbn;
TrimCache trimCache = new TrimCache(sbn);
- // mServices is the list inside ManagedServices of all the assistants,
+ // mServices is the list inside ManagedServices of all the rankers,
// There should be only one, but it's a list, so while we enforce
// singularity elsewhere, we keep it general here, to avoid surprises.
- for (final ManagedServiceInfo info : NotificationAssistant.this.mServices) {
+ for (final ManagedServiceInfo info : NotificationRankers.this.mServices) {
boolean sbnVisible = isVisibleToListener(sbn, info);
if (!sbnVisible) {
continue;
@@ -3542,7 +3551,7 @@
final int importance = r.getImportance();
final boolean fromUser = r.isImportanceFromUser();
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
- mAssistantHandler.post(new Runnable() {
+ mHandler.post(new Runnable() {
@Override
public void run() {
notifyEnqueued(info, sbnToPost, importance, fromUser);
@@ -3553,12 +3562,12 @@
private void notifyEnqueued(final ManagedServiceInfo info,
final StatusBarNotification sbn, int importance, boolean fromUser) {
- final INotificationListener assistant = (INotificationListener) info.service;
+ final INotificationListener ranker = (INotificationListener) info.service;
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
- assistant.onNotificationEnqueued(sbnHolder, importance, fromUser);
+ ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
} catch (RemoteException ex) {
- Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+ Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 9cdece5..0945065 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -50,7 +50,7 @@
mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
}
if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.EVENT_PATH)) {
- mConditionProviders.addSystemProvider(new EventConditionProvider(helper.getLooper()));
+ mConditionProviders.addSystemProvider(new EventConditionProvider());
}
mConditionProviders.setCallback(this);
}
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index d6b59f9..27c8293 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -31,6 +31,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.UserHandle;
+import android.print.PrintManager;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import android.provider.MediaStore;
@@ -579,7 +580,7 @@
// Print Spooler
PackageParser.Package printSpoolerPackage = getSystemPackageLPr(
- "com.android.printspooler");
+ PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
if (printSpoolerPackage != null
&& doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
grantRuntimePermissionsLPw(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
@@ -714,7 +715,8 @@
private PackageParser.Package getDefaultSystemHandlerServicePackageLPr(
Intent intent, int userId) {
List<ResolveInfo> handlers = mService.queryIntentServices(intent,
- intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId);
+ intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId)
+ .getList();
if (handlers == null) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 1476e6e..206a143 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -166,6 +166,10 @@
mInstaller.execute("rmpackagedir", packageDir);
}
+ public void rmProfiles(String pkgName) throws InstallerException {
+ mInstaller.execute("rmprofiles", pkgName);
+ }
+
public void createUserConfig(int userid) throws InstallerException {
mInstaller.execute("mkuserconfig", userid);
}
@@ -203,6 +207,11 @@
mInstaller.execute("linkfile", relativePath, fromBase, toBase);
}
+ public void moveAb(String apkPath, String instructionSet, String outputPath)
+ throws InstallerException {
+ mInstaller.execute("move_ab", apkPath, instructionSet, outputPath);
+ }
+
private static void assertValidInstructionSet(String instructionSet)
throws InstallerException {
for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 8d75f60..6cc0544 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,6 +16,9 @@
package com.android.server.pm;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
@@ -27,14 +30,19 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
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.pm.UserInfo;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IInterface;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -43,16 +51,18 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import java.util.List;
/**
* Service that manages requests and callbacks for launchers that support
- * managed profiles.
+ * managed profiles.
*/
-
public class LauncherAppsService extends SystemService {
private final LauncherAppsImpl mLauncherAppsImpl;
@@ -67,21 +77,35 @@
publishBinderService(Context.LAUNCHER_APPS_SERVICE, mLauncherAppsImpl);
}
- class LauncherAppsImpl extends ILauncherApps.Stub {
+ @VisibleForTesting
+ static class LauncherAppsImpl extends ILauncherApps.Stub {
private static final boolean DEBUG = false;
private static final String TAG = "LauncherAppsService";
private final Context mContext;
private final PackageManager mPm;
private final UserManager mUm;
+ private final ShortcutServiceInternal mShortcutServiceInternal;
private final PackageCallbackList<IOnAppsChangedListener> mListeners
= new PackageCallbackList<IOnAppsChangedListener>();
- private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+ private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
public LauncherAppsImpl(Context context) {
mContext = context;
mPm = mContext.getPackageManager();
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mShortcutServiceInternal = Preconditions.checkNotNull(
+ LocalServices.getService(ShortcutServiceInternal.class));
+ mShortcutServiceInternal.addListener(mPackageMonitor);
+ }
+
+ @VisibleForTesting
+ int injectBinderCallingUid() {
+ return getCallingUid();
+ }
+
+ private int getCallingUserId() {
+ return UserHandle.getUserId(injectBinderCallingUid());
}
/*
@@ -154,7 +178,8 @@
/**
* Checks if the caller is in the same group as the userToCheck.
*/
- private void ensureInUserProfiles(UserHandle userToCheck, String message) {
+ @VisibleForTesting // We override it in unit tests
+ void ensureInUserProfiles(UserHandle userToCheck, String message) {
final int callingUserId = UserHandle.getCallingUserId();
final int targetUserId = userToCheck.getIdentifier();
@@ -174,6 +199,22 @@
}
}
+ @VisibleForTesting // We override it in unit tests
+ void verifyCallingPackage(String callingPackage) {
+ int packageUid = -1;
+ try {
+ packageUid = mPm.getPackageUidAsUser(callingPackage,
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ UserHandle.getUserId(getCallingUid()));
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Package not found: " + callingPackage);
+ }
+ if (packageUid != Binder.getCallingUid()) {
+ throw new SecurityException("Calling package name mismatch");
+ }
+ }
+
/**
* Checks if the user is enabled.
*/
@@ -264,6 +305,96 @@
}
}
+ private void ensureShortcutPermission(@NonNull String callingPackage, UserHandle user) {
+ verifyCallingPackage(callingPackage);
+ ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
+
+ if (!mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
+ user.getIdentifier())) {
+ throw new SecurityException("Caller can't access shortcut information");
+ }
+ }
+
+ @Override
+ public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
+ String packageName, ComponentName componentName, int flags, UserHandle user)
+ throws RemoteException {
+ ensureShortcutPermission(callingPackage, user);
+
+ return new ParceledListSlice<>(
+ mShortcutServiceInternal.getShortcuts(callingPackage, changedSince, packageName,
+ componentName, flags, user.getIdentifier()));
+ }
+
+ @Override
+ public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
+ List<String> ids, UserHandle user) throws RemoteException {
+ ensureShortcutPermission(callingPackage, user);
+
+ return new ParceledListSlice<>(
+ mShortcutServiceInternal.getShortcutInfo(callingPackage, packageName,
+ ids, user.getIdentifier()));
+ }
+
+ @Override
+ public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
+ UserHandle user) throws RemoteException {
+ ensureShortcutPermission(callingPackage, user);
+
+ mShortcutServiceInternal.pinShortcuts(callingPackage, packageName,
+ ids, user.getIdentifier());
+ }
+
+ @Override
+ public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut,
+ UserHandle user) {
+ ensureShortcutPermission(callingPackage, user);
+
+ return mShortcutServiceInternal.getShortcutIconResId(callingPackage, shortcut,
+ user.getIdentifier());
+ }
+
+ @Override
+ public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut,
+ UserHandle user) {
+ ensureShortcutPermission(callingPackage, user);
+
+ return mShortcutServiceInternal.getShortcutIconFd(callingPackage, shortcut,
+ user.getIdentifier());
+ }
+
+ @Override
+ public boolean hasShortcutHostPermission(String callingPackage) throws RemoteException {
+ verifyCallingPackage(callingPackage);
+ return mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
+ getCallingUserId());
+ }
+
+ @Override
+ public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
+ Rect sourceBounds, Bundle startActivityOptions, UserHandle user)
+ throws RemoteException {
+ ensureShortcutPermission(callingPackage, user);
+
+ final Intent intent = mShortcutServiceInternal.createShortcutIntent(callingPackage,
+ packageName, shortcutId, user.getIdentifier());
+ if (intent == null) {
+ return false;
+ }
+ // Note the target activity doesn't have to be exported.
+
+ intent.setSourceBounds(sourceBounds);
+ prepareIntentForLaunch(intent, sourceBounds);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivityAsUser(intent, startActivityOptions, user);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return true;
+ }
+
@Override
public boolean isActivityEnabled(ComponentName component, UserHandle user)
throws RemoteException {
@@ -293,9 +424,7 @@
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- launchIntent.setSourceBounds(sourceBounds);
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ prepareIntentForLaunch(launchIntent, sourceBounds);
launchIntent.setPackage(component.getPackageName());
long ident = Binder.clearCallingIdentity();
@@ -332,6 +461,13 @@
}
}
+ 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 {
@@ -355,7 +491,7 @@
}
- private class MyPackageMonitor extends PackageMonitor {
+ private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
/** Checks if user is a profile of or same as listeningUser.
* and the user is enabled. */
@@ -390,6 +526,8 @@
}
}
+ // TODO Simplify with lambdas.
+
@Override
public void onPackageAdded(String packageName, int uid) {
UserHandle user = new UserHandle(getChangingUserId());
@@ -523,6 +661,28 @@
super.onPackagesUnsuspended(packages);
}
+ @Override
+ public void onShortcutChanged(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @UserIdInt int userId) {
+ final UserHandle user = UserHandle.of(userId);
+
+ final int n = mListeners.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
+ UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
+ if (!isEnabledProfileOf(user, listeningUser, "onShortcutChanged")) continue;
+
+ // STOPSHIP Skip if the receiver doesn't have the permission.
+
+ try {
+ listener.onShortcutChanged(user, packageName,
+ new ParceledListSlice<>(shortcuts));
+ } catch (RemoteException re) {
+ Slog.d(TAG, "Callback failed ", re);
+ }
+ }
+ mListeners.finishBroadcast();
+ }
}
class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 3d2a355..67aeed1 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -16,36 +16,28 @@
package com.android.server.pm;
-import android.app.AppGlobals;
+import static com.android.server.pm.Installer.DEXOPT_OTA;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+
import android.content.Context;
-import android.content.Intent;
import android.content.pm.IOtaDexopt;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Package;
-import android.content.pm.ResolveInfo;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
-import dalvik.system.DexFile;
+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.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
-
-import static com.android.server.pm.Installer.DEXOPT_OTA;
/**
* A service for A/B OTA dexopting.
@@ -70,6 +62,9 @@
// 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);
}
public static OtaDexoptService main(Context context,
@@ -150,20 +145,50 @@
false /* extractOnly */);
}
- private ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
- List<ResolveInfo> ris = null;
- try {
- ris = AppGlobals.getPackageManager().queryIntentReceivers(
- intent, null, 0, userId);
- } catch (RemoteException e) {
+ private void moveAbArtifacts(Installer installer) {
+ if (mDexoptPackages != null) {
+ throw new IllegalStateException("Should not be ota-dexopting when trying to move.");
}
- ArraySet<String> pkgNames = new ArraySet<String>(ris == null ? 0 : ris.size());
- if (ris != null) {
- for (ResolveInfo ri : ris) {
- pkgNames.add(ri.activityInfo.packageName);
+
+ // Look into all packages.
+ Collection<PackageParser.Package> pkgs = mPackageManagerService.getPackages();
+ for (PackageParser.Package pkg : pkgs) {
+ if (pkg == null) {
+ continue;
+ }
+
+ // Does the package have code? If not, there won't be any artifacts.
+ if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
+ continue;
+ }
+ if (pkg.codePath == null) {
+ Slog.w(TAG, "Package " + pkg + " can be optimized but has null codePath");
+ continue;
+ }
+
+ // If the path is in /system or /vendor, ignore. It will have been ota-dexopted into
+ // /data/ota and moved into the dalvik-cache already.
+ if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor")) {
+ continue;
+ }
+
+ final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
+ final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
+ final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
+ for (String dexCodeInstructionSet : dexCodeInstructionSets) {
+ for (String path : paths) {
+ String oatDir = PackageDexOptimizer.getOatDir(new File(pkg.codePath)).
+ getAbsolutePath();
+
+ // TODO: Check first whether there is an artifact, to save the roundtrip time.
+
+ try {
+ installer.moveAb(path, dexCodeInstructionSet, oatDir);
+ } catch (InstallerException e) {
+ }
+ }
}
}
- return pkgNames;
}
private static class OTADexoptPackageDexOptimizer extends
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index c9613b4..561682c 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -191,7 +191,6 @@
throw new IllegalStateException("Invalid dexopt:" + dexoptNeeded);
}
-
Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index b84ffa3..ef53905 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -34,6 +34,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstallerSession;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
@@ -58,6 +59,7 @@
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.ExceptionUtils;
import android.util.MathUtils;
@@ -77,6 +79,7 @@
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -86,6 +89,7 @@
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String TAG = "PackageInstaller";
private static final boolean LOGD = true;
+ private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
private static final int MSG_COMMIT = 0;
@@ -171,6 +175,25 @@
@GuardedBy("mLock")
private File mInheritedFilesBase;
+ private static final FileFilter sAddedFilter = new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ // Installers can't stage directories, so it's fine to ignore
+ // entries like "lost+found".
+ if (file.isDirectory()) return false;
+ if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
+ return true;
+ }
+ };
+ private static final FileFilter sRemovedFilter = new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ if (file.isDirectory()) return false;
+ if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
+ return true;
+ }
+ };
+
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
@@ -346,6 +369,32 @@
}
@Override
+ public void removeSplit(String splitName) {
+ if (TextUtils.isEmpty(params.appPackageName)) {
+ throw new IllegalStateException("Must specify package name to remove a split");
+ }
+ try {
+ createRemoveSplitMarker(splitName);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ private void createRemoveSplitMarker(String splitName) throws IOException {
+ try {
+ final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
+ if (!FileUtils.isValidExtFilename(markerName)) {
+ throw new IllegalArgumentException("Invalid marker: " + markerName);
+ }
+ final File target = new File(resolveStageDir(), markerName);
+ target.createNewFile();
+ Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
+ } catch (ErrnoException e) {
+ throw e.rethrowAsIOException();
+ }
+ }
+
+ @Override
public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
try {
return openWriteInternal(name, offsetBytes, lengthBytes);
@@ -608,22 +657,28 @@
mResolvedStagedFiles.clear();
mResolvedInheritedFiles.clear();
- final File[] files = mResolvedStageDir.listFiles();
- if (ArrayUtils.isEmpty(files)) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
+ final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);
+ final List<String> removeSplitList = new ArrayList<>();
+ if (!ArrayUtils.isEmpty(removedFiles)) {
+ for (File removedFile : removedFiles) {
+ final String fileName = removedFile.getName();
+ final String splitName = fileName.substring(
+ 0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());
+ removeSplitList.add(splitName);
+ }
}
+ final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
+ if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
+ }
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
- for (File file : files) {
-
- // Installers can't stage directories, so it's fine to ignore
- // entries like "lost+found".
- if (file.isDirectory()) continue;
-
+ for (File addedFile : addedFiles) {
final ApkLite apk;
try {
- apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
+ apk = PackageParser.parseApkLite(
+ addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
@@ -642,7 +697,7 @@
mSignatures = apk.signatures;
}
- assertApkConsistent(String.valueOf(file), apk);
+ assertApkConsistent(String.valueOf(addedFile), apk);
// Take this opportunity to enforce uniform naming
final String targetName;
@@ -657,8 +712,8 @@
}
final File targetFile = new File(mResolvedStageDir, targetName);
- if (!file.equals(targetFile)) {
- file.renameTo(targetFile);
+ if (!addedFile.equals(targetFile)) {
+ addedFile.renameTo(targetFile);
}
// Base is coming from session
@@ -669,6 +724,27 @@
mResolvedStagedFiles.add(targetFile);
}
+ if (removeSplitList.size() > 0) {
+ // validate split names marked for removal
+ final int flags = mSignatures == null ? PackageManager.GET_SIGNATURES : 0;
+ final PackageInfo pkg = mPm.getPackageInfo(params.appPackageName, flags, userId);
+ for (String splitName : removeSplitList) {
+ if (!ArrayUtils.contains(pkg.splitNames, splitName)) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Split not found: " + splitName);
+ }
+ }
+
+ // ensure we've got appropriate package name, version code and signatures
+ if (mPackageName == null) {
+ mPackageName = pkg.packageName;
+ mVersionCode = pkg.versionCode;
+ }
+ if (mSignatures == null) {
+ mSignatures = pkg.signatures;
+ }
+ }
+
if (params.mode == SessionParams.MODE_FULL_INSTALL) {
// Full installs must include a base package
if (!stagedSplits.contains(null)) {
@@ -707,8 +783,8 @@
for (int i = 0; i < existing.splitNames.length; i++) {
final String splitName = existing.splitNames[i];
final File splitFile = new File(existing.splitCodePaths[i]);
-
- if (!stagedSplits.contains(splitName)) {
+ final boolean splitRemoved = removeSplitList.contains(splitName);
+ if (!stagedSplits.contains(splitName) && !splitRemoved) {
mResolvedInheritedFiles.add(splitFile);
}
}
@@ -748,6 +824,11 @@
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
+ apk.packageName + " inconsistent with " + mPackageName);
}
+ if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
+ + " specified package " + params.appPackageName
+ + " inconsistent with " + apk.packageName);
+ }
if (mVersionCode != apk.versionCode) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
+ " version code " + apk.versionCode + " inconsistent with "
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 31311f7..b624087 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -103,6 +103,7 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
import android.content.BroadcastReceiver;
@@ -174,7 +175,6 @@
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -2449,7 +2449,7 @@
// since core system apps like SettingsProvider and SystemUI
// can't wait for user to start
final int storageFlags;
- if (StorageManager.isFileBasedEncryptionEnabled()) {
+ if (StorageManager.isFileEncryptedNativeOrEmulated()) {
storageFlags = StorageManager.FLAG_STORAGE_DE;
} else {
storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
@@ -2564,7 +2564,7 @@
private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
- final List<ResolveInfo> matches = queryIntentReceivers(intent, PACKAGE_MIME_TYPE,
+ final List<ResolveInfo> matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
@@ -2579,7 +2579,7 @@
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
- final List<ResolveInfo> matches = queryIntentActivities(intent, PACKAGE_MIME_TYPE,
+ final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
@@ -2591,7 +2591,7 @@
private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
- final List<ResolveInfo> matches = queryIntentReceivers(intent, PACKAGE_MIME_TYPE,
+ final List<ResolveInfo> matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
ResolveInfo best = null;
final int N = matches.size();
@@ -2626,7 +2626,7 @@
}
final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE);
- final List<ResolveInfo> resolvers = queryIntentServices(resolverIntent, null,
+ final List<ResolveInfo> resolvers = queryIntentServicesInternal(resolverIntent, null,
MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
final int N = resolvers.size();
@@ -2671,7 +2671,7 @@
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
- final List<ResolveInfo> matches = queryIntentActivities(intent, PACKAGE_MIME_TYPE,
+ final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_ENCRYPTION_AWARE_AND_UNAWARE, UserHandle.USER_SYSTEM);
if (matches.size() == 0) {
return null;
@@ -2766,7 +2766,7 @@
private List<String> resolveAllBrowserApps(int userId) {
// Resolve the canonical browser intent and check that the handleAllWebDataURI boolean is set
- List<ResolveInfo> list = queryIntentActivities(sBrowserIntent, null,
+ List<ResolveInfo> list = queryIntentActivitiesInternal(sBrowserIntent, null,
PackageManager.MATCH_ALL, userId);
final int count = list.size();
@@ -2786,7 +2786,7 @@
}
private boolean packageIsBrowser(String packageName, int userId) {
- List<ResolveInfo> list = queryIntentActivities(sBrowserIntent, null,
+ List<ResolveInfo> list = queryIntentActivitiesInternal(sBrowserIntent, null,
PackageManager.MATCH_ALL, userId);
final int N = list.size();
for (int i = 0; i < N; i++) {
@@ -3035,9 +3035,15 @@
}
@Override
- public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) {
+ public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String group,
+ int flags) {
// reader
synchronized (mPackages) {
+ if (group != null && !mPermissionGroups.containsKey(group)) {
+ // This is thrown as NameNotFoundException
+ return null;
+ }
+
ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
for (BasePermission p : mSettings.mPermissions.values()) {
if (group == null) {
@@ -3050,11 +3056,7 @@
}
}
}
-
- if (out.size() > 0) {
- return out;
- }
- return mPermissionGroups.containsKey(group) ? out : null;
+ return new ParceledListSlice<>(out);
}
}
@@ -3068,7 +3070,7 @@
}
@Override
- public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
+ public @NonNull ParceledListSlice<PermissionGroupInfo> getAllPermissionGroups(int flags) {
// reader
synchronized (mPackages) {
final int N = mPermissionGroups.size();
@@ -3077,7 +3079,7 @@
for (PackageParser.PermissionGroup pg : mPermissionGroups.values()) {
out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
}
- return out;
+ return new ParceledListSlice<>(out);
}
}
@@ -3229,7 +3231,7 @@
* Return if the user key is currently unlocked.
*/
private boolean isUserKeyUnlocked(int userId) {
- if (StorageManager.isFileBasedEncryptionEnabled()) {
+ if (StorageManager.isFileEncryptedNativeOrEmulated()) {
final IMountService mount = IMountService.Stub
.asInterface(ServiceManager.getService("mount"));
if (mount == null) {
@@ -3475,22 +3477,17 @@
}
@Override
- public FeatureInfo[] getSystemAvailableFeatures() {
- Collection<FeatureInfo> featSet;
+ public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {
synchronized (mPackages) {
- featSet = mAvailableFeatures.values();
- int size = featSet.size();
- if (size > 0) {
- FeatureInfo[] features = new FeatureInfo[size+1];
- featSet.toArray(features);
- FeatureInfo fi = new FeatureInfo();
- fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
- FeatureInfo.GL_ES_VERSION_UNDEFINED);
- features[size] = fi;
- return features;
- }
+ final ArrayList<FeatureInfo> res = new ArrayList<>(mAvailableFeatures.values());
+
+ final FeatureInfo fi = new FeatureInfo();
+ fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
+ FeatureInfo.GL_ES_VERSION_UNDEFINED);
+ res.add(fi);
+
+ return new ParceledListSlice<>(res);
}
- return null;
}
@Override
@@ -4446,6 +4443,13 @@
}
@Override
+ public List<String> getAllPackages() {
+ synchronized (mPackages) {
+ return new ArrayList<String>(mPackages.keySet());
+ }
+ }
+
+ @Override
public String[] getPackagesForUid(int uid) {
uid = UserHandle.getAppId(uid);
// reader
@@ -4570,7 +4574,8 @@
flags = updateFlagsForResolve(flags, userId, intent);
enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "resolve intent");
- List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
+ final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
+ userId);
final ResolveInfo bestChoice =
chooseBestActivity(intent, resolvedType, flags, query, userId);
@@ -4602,7 +4607,8 @@
filter.dump(new PrintStreamPrinter(System.out), " ");
}
intent.setComponent(null);
- List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
+ final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
+ userId);
// Find any earlier preferred or last chosen entries and nuke them
findPreferredActivity(intent, resolvedType,
flags, query, 0, false, true, false, userId);
@@ -4615,7 +4621,8 @@
public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
final int userId = UserHandle.getCallingUserId();
if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
- List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
+ final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
+ userId);
return findPreferredActivity(intent, resolvedType, flags, query, 0,
false, false, false, userId);
}
@@ -5026,7 +5033,13 @@
}
@Override
- public List<ResolveInfo> queryIntentActivities(Intent intent,
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivities(Intent intent,
+ String resolvedType, int flags, int userId) {
+ return new ParceledListSlice<>(
+ queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
+ }
+
+ private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
flags = updateFlagsForResolve(flags, userId, intent);
@@ -5517,7 +5530,14 @@
}
@Override
- public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
+ Intent[] specifics, String[] specificTypes, Intent intent,
+ String resolvedType, int flags, int userId) {
+ return new ParceledListSlice<>(queryIntentActivityOptionsInternal(caller, specifics,
+ specificTypes, intent, resolvedType, flags, userId));
+ }
+
+ private @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
@@ -5527,7 +5547,7 @@
"query intent activity options");
final String resultsAction = intent.getAction();
- List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags
+ final List<ResolveInfo> results = queryIntentActivitiesInternal(intent, resolvedType, flags
| PackageManager.GET_RESOLVED_FILTER, userId);
if (DEBUG_INTENT_MATCHING) {
@@ -5692,8 +5712,14 @@
}
@Override
- public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
- int userId) {
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
+ String resolvedType, int flags, int userId) {
+ return new ParceledListSlice<>(
+ queryIntentReceiversInternal(intent, resolvedType, flags, userId));
+ }
+
+ private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
+ String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
flags = updateFlagsForResolve(flags, userId, intent);
ComponentName comp = intent.getComponent();
@@ -5725,7 +5751,7 @@
return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,
userId);
}
- return null;
+ return Collections.emptyList();
}
}
@@ -5733,7 +5759,7 @@
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForResolve(flags, userId, intent);
- List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, userId);
+ List<ResolveInfo> query = queryIntentServicesInternal(intent, resolvedType, flags, userId);
if (query != null) {
if (query.size() >= 1) {
// If there is more than one service with the same priority,
@@ -5745,8 +5771,14 @@
}
@Override
- public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags,
- int userId) {
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentServices(Intent intent,
+ String resolvedType, int flags, int userId) {
+ return new ParceledListSlice<>(
+ queryIntentServicesInternal(intent, resolvedType, flags, userId));
+ }
+
+ private @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
+ String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
flags = updateFlagsForResolve(flags, userId, intent);
ComponentName comp = intent.getComponent();
@@ -5778,12 +5810,18 @@
return mServices.queryIntentForPackage(intent, resolvedType, flags, pkg.services,
userId);
}
- return null;
+ return Collections.emptyList();
}
}
@Override
- public List<ResolveInfo> queryIntentContentProviders(
+ public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
+ String resolvedType, int flags, int userId) {
+ return new ParceledListSlice<>(
+ queryIntentContentProvidersInternal(intent, resolvedType, flags, userId));
+ }
+
+ private @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
flags = updateFlagsForResolve(flags, userId, intent);
@@ -5816,7 +5854,7 @@
return mProviders.queryIntentForPackage(
intent, resolvedType, flags, pkg.providers, userId);
}
- return null;
+ return Collections.emptyList();
}
}
@@ -6072,7 +6110,12 @@
&& UserHandle.getAppId(Binder.getCallingUid()) == pkg.applicationInfo.uid;
}
- public List<ApplicationInfo> getPersistentApplications(int flags) {
+ @Override
+ public @NonNull ParceledListSlice<ApplicationInfo> getPersistentApplications(int flags) {
+ return new ParceledListSlice<>(getPersistentApplicationsInternal(flags));
+ }
+
+ private @NonNull List<ApplicationInfo> getPersistentApplicationsInternal(int flags) {
final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();
// reader
@@ -6154,11 +6197,11 @@
}
@Override
- public ParceledListSlice<ProviderInfo> queryContentProviders(String processName,
+ public @NonNull ParceledListSlice<ProviderInfo> queryContentProviders(String processName,
int uid, int flags) {
final int userId = processName != null ? UserHandle.getUserId(uid)
: UserHandle.getCallingUserId();
- if (!sUserManager.exists(userId)) return null;
+ if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForComponent(flags, userId, processName);
ArrayList<ProviderInfo> finalList = null;
@@ -6190,7 +6233,7 @@
return new ParceledListSlice<ProviderInfo>(finalList);
}
- return null;
+ return ParceledListSlice.emptyList();
}
@Override
@@ -6203,10 +6246,14 @@
}
@Override
- public List<InstrumentationInfo> queryInstrumentation(String targetPackage,
+ public @NonNull ParceledListSlice<InstrumentationInfo> queryInstrumentation(
+ String targetPackage, int flags) {
+ return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags));
+ }
+
+ private @NonNull List<InstrumentationInfo> queryInstrumentationInternal(String targetPackage,
int flags) {
- ArrayList<InstrumentationInfo> finalList =
- new ArrayList<InstrumentationInfo>();
+ ArrayList<InstrumentationInfo> finalList = new ArrayList<InstrumentationInfo>();
// reader
synchronized (mPackages) {
@@ -6844,7 +6891,8 @@
// Extract pacakges only if profile-guided compilation is enabled because
// otherwise BackgroundDexOptService will not dexopt them later.
- if (!isUpgrade()) {
+ boolean prunedCache = VMRuntime.didPruneDalvikCache();
+ if (!isUpgrade() && !prunedCache) {
return;
}
@@ -6872,8 +6920,11 @@
}
if (PackageDexOptimizer.canOptimizePackage(pkg)) {
+ // If the cache was pruned, any compiled odex files will likely be out of date
+ // and would have to be patched (would be SELF_PATCHOAT, which is deprecated).
+ // Instead, force the extraction in this case.
performDexOpt(pkg.packageName, null /* instructionSet */,
- false /* useProfiles */, true /* extractOnly */, false /* force */);
+ false /* useProfiles */, true /* extractOnly */, prunedCache);
}
}
}
@@ -10775,25 +10826,41 @@
}
}
- // TODO: investigate and add more restrictions for suspending crucial packages.
+ /**
+ * TODO: cache and disallow blocking the active dialer.
+ *
+ * @see also DefaultPermissionGrantPolicy#grantDefaultSystemHandlerPermissions
+ */
private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
if (isPackageDeviceAdmin(packageName, userId)) {
- Slog.w(TAG, "Not suspending/un-suspending package \"" + packageName
- + "\": has active device admin");
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": has an active device admin");
return false;
}
String activeLauncherPackageName = getActiveLauncherPackageName(userId);
if (packageName.equals(activeLauncherPackageName)) {
- Slog.w(TAG, "Not suspending/un-suspending package \"" + packageName
- + "\" because it is set as the active launcher");
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": contains the active launcher");
+ return false;
+ }
+
+ if (packageName.equals(mRequiredInstallerPackage)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": required for package installation");
+ return false;
+ }
+
+ if (packageName.equals(mRequiredVerifierPackage)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": required for package verification");
return false;
}
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null && isPrivilegedApp(pkg)) {
- Slog.w(TAG, "Not suspending/un-suspending package \"" + packageName
- + "\" because it is a privileged app");
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": is a privileged app");
return false;
}
@@ -11081,21 +11148,22 @@
}
@Override
- public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+ public @NonNull ParceledListSlice<IntentFilterVerificationInfo> getIntentFilterVerifications(
+ String packageName) {
synchronized (mPackages) {
- return mSettings.getIntentFilterVerificationsLPr(packageName);
+ return new ParceledListSlice<>(mSettings.getIntentFilterVerificationsLPr(packageName));
}
}
@Override
- public List<IntentFilter> getAllIntentFilters(String packageName) {
+ public @NonNull ParceledListSlice<IntentFilter> getAllIntentFilters(String packageName) {
if (TextUtils.isEmpty(packageName)) {
- return Collections.<IntentFilter>emptyList();
+ return ParceledListSlice.emptyList();
}
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null || pkg.activities == null) {
- return Collections.<IntentFilter>emptyList();
+ return ParceledListSlice.emptyList();
}
final int count = pkg.activities.size();
ArrayList<IntentFilter> result = new ArrayList<>();
@@ -11105,7 +11173,7 @@
result.addAll(activity.intents);
}
}
- return result;
+ return new ParceledListSlice<>(result);
}
}
@@ -11831,7 +11899,7 @@
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Query all live verifiers based on current user state
- final List<ResolveInfo> receivers = queryIntentReceivers(verification,
+ final List<ResolveInfo> receivers = queryIntentReceiversInternal(verification,
PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier());
if (DEBUG_VERIFY) {
@@ -15106,12 +15174,29 @@
}
@Override
+ public void clearApplicationProfileData(String packageName) {
+ enforceSystemOrRoot("Only the system can clear all profile data");
+ try {
+ mInstaller.rmProfiles(packageName);
+ } catch (InstallerException ex) {
+ Log.e(TAG, "Could not clear profile data of package " + packageName);
+ }
+ }
+
+ @Override
public void clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, final int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CLEAR_APP_USER_DATA, null);
+
enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */, "clear application data");
+
+ final DevicePolicyManagerInternal dpmi = LocalServices
+ .getService(DevicePolicyManagerInternal.class);
+ if (dpmi != null && dpmi.hasDeviceOwnerOrProfileOwner(packageName, userId)) {
+ throw new SecurityException("Cannot clear data for a device owner or a profile owner");
+ }
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
public void run() {
@@ -15123,8 +15208,8 @@
clearExternalStorageDataSync(packageName, userId, true);
if (succeeded) {
// invoke DeviceStorageMonitor's update method to clear any notifications
- DeviceStorageMonitorInternal
- dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
+ DeviceStorageMonitorInternal dsm = LocalServices
+ .getService(DeviceStorageMonitorInternal.class);
if (dsm != null) {
dsm.checkMemory();
}
@@ -15523,22 +15608,6 @@
return true;
}
-
- @Override
- public void addPackageToPreferred(String packageName) {
- Slog.w(TAG, "addPackageToPreferred: this is now a no-op");
- }
-
- @Override
- public void removePackageFromPreferred(String packageName) {
- Slog.w(TAG, "removePackageFromPreferred: this is now a no-op");
- }
-
- @Override
- public List<PackageInfo> getPreferredPackages(int flags) {
- return new ArrayList<PackageInfo>();
- }
-
private int getUidTargetSdkVersionLockedLPr(int uid) {
Object obj = mSettings.getUserIdLPr(uid);
if (obj instanceof SharedUserSetting) {
@@ -16387,14 +16456,17 @@
@Override
public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) {
+ return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
+ }
+
+ ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+ int userId) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
-
- final int callingUserId = UserHandle.getCallingUserId();
- List<ResolveInfo> list = queryIntentActivities(intent, null,
- PackageManager.GET_META_DATA, callingUserId);
+ List<ResolveInfo> list = queryIntentActivitiesInternal(intent, null,
+ PackageManager.GET_META_DATA, userId);
ResolveInfo preferred = findPreferredActivity(intent, null, 0, list, 0,
- true, false, false, callingUserId);
+ true, false, false, userId);
allHomeCandidates.clear();
if (list != null) {
@@ -17387,8 +17459,9 @@
}
private String dumpDomainString(String packageName) {
- List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName);
- List<IntentFilter> filters = getAllIntentFilters(packageName);
+ List<IntentFilterVerificationInfo> iviList = getIntentFilterVerifications(packageName)
+ .getList();
+ List<IntentFilter> filters = getAllIntentFilters(packageName).getList();
ArraySet<String> result = new ArraySet<>();
if (iviList.size() > 0) {
@@ -18263,7 +18336,7 @@
* the app.
*/
private boolean maybeMigrateAppData(String volumeUuid, int userId, PackageParser.Package pkg) {
- if (pkg.isSystemApp() && !StorageManager.isFileBasedEncryptionEnabled()
+ if (pkg.isSystemApp() && !StorageManager.isFileEncryptedNativeOrEmulated()
&& PackageManager.APPLY_FORCE_DEVICE_ENCRYPTED) {
final int storageTarget = pkg.applicationInfo.isForceDeviceEncrypted()
? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
@@ -19182,6 +19255,12 @@
public ApplicationInfo getApplicationInfo(String packageName, int userId) {
return PackageManagerService.this.getApplicationInfo(packageName, 0 /*flags*/, userId);
}
+
+ @Override
+ public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+ int userId) {
+ return PackageManagerService.this.getHomeActivitiesAsUser(allHomeCandidates, userId);
+ }
}
@Override
@@ -19209,4 +19288,14 @@
boolean isHistoricalPackageUsageAvailable() {
return mPackageUsage.isHistoricalPackageUsageAvailable();
}
+
+ /**
+ * Return a <b>copy</b> of the collection of packages known to the package manager.
+ * @return A copy of the values of mPackages.
+ */
+ Collection<PackageParser.Package> getPackages() {
+ synchronized (mPackages) {
+ return new ArrayList<>(mPackages.values());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index a3ac514..f79d6ee 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -16,13 +16,15 @@
package com.android.server.pm;
+import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
+import static com.android.server.pm.PackageManagerService.TAG;
+
import android.app.AppGlobals;
import android.content.Intent;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Package;
import android.content.pm.ResolveInfo;
-import android.os.UserHandle;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;
@@ -35,9 +37,6 @@
import java.util.List;
import java.util.Set;
-import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
-import static com.android.server.pm.PackageManagerService.TAG;
-
/**
* Class containing helper methods for the PackageManagerService.
*
@@ -49,7 +48,8 @@
private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
List<ResolveInfo> ris = null;
try {
- ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId);
+ ris = AppGlobals.getPackageManager().queryIntentReceivers(intent, null, 0, userId)
+ .getList();
} catch (RemoteException e) {
}
ArraySet<String> pkgNames = new ArraySet<String>();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index abee007..d77168c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -95,6 +95,8 @@
return runInstallCommit();
case "install-create":
return runInstallCreate();
+ case "install-remove":
+ return runInstallRemove();
case "install-write":
return runInstallWrite();
case "compile":
@@ -136,11 +138,12 @@
pw.println("Error: must either specify a package size or an APK file");
return 1;
}
- if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
+ if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
- if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+ if (doCommitSession(sessionId, false /*logSuccess*/)
+ != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
abandonSession = false;
@@ -225,7 +228,20 @@
final int sessionId = Integer.parseInt(getNextArg());
final String splitName = getNextArg();
final String path = getNextArg();
- return doWriteSession(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
+ return doWriteSplit(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
+ }
+
+ private int runInstallRemove() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+
+ final int sessionId = Integer.parseInt(getNextArg());
+
+ final String splitName = getNextArg();
+ if (splitName == null) {
+ pw.println("Error: split name not specified");
+ return 1;
+ }
+ return doRemoveSplit(sessionId, splitName, true /*logSuccess*/);
}
private int runCompile() throws RemoteException {
@@ -233,17 +249,30 @@
boolean useJitProfiles = false;
boolean extractOnly = false;
boolean forceCompilation = false;
+ boolean allPackages = false;
+ boolean clearProfileData = false;
String compilationMode = "default";
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
- case "-m":
- compilationMode = getNextArgRequired();
+ case "-a":
+ allPackages = true;
+ break;
+ case "-c":
+ clearProfileData = true;
break;
case "-f":
forceCompilation = true;
break;
+ case "-m":
+ compilationMode = getNextArgRequired();
+ break;
+ case "--reset":
+ forceCompilation = true;
+ clearProfileData = true;
+ compilationMode = "extract";
+ break;
default:
pw.println("Error: Unknown option: " + opt);
return 1;
@@ -255,7 +284,7 @@
useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
extractOnly = false;
break;
- case "all":
+ case "full":
useJitProfiles = false;
extractOnly = false;
break;
@@ -272,19 +301,49 @@
return 1;
}
- String packageName = getNextArg();
- if (packageName == null) {
- pw.println("Error: package name not specified");
- return 1;
+ List<String> packageNames = null;
+ if (allPackages) {
+ packageNames = mInterface.getAllPackages();
+ } else {
+ String packageName = getNextArg();
+ if (packageName == null) {
+ pw.println("Error: package name not specified");
+ return 1;
+ }
+ packageNames = Collections.singletonList(packageName);
}
- boolean success = mInterface.performDexOpt(packageName, null /* instructionSet */,
- useJitProfiles, extractOnly, forceCompilation);
- if (success) {
+ List<String> failedPackages = new ArrayList<>();
+ for (String packageName : packageNames) {
+ if (clearProfileData) {
+ mInterface.clearApplicationProfileData(packageName);
+ }
+
+ boolean result = mInterface.performDexOpt(packageName, null /* instructionSet */,
+ useJitProfiles, extractOnly, forceCompilation);
+ if (!result) {
+ failedPackages.add(packageName);
+ }
+ }
+
+ if (failedPackages.isEmpty()) {
pw.println("Success");
return 0;
+ } else if (failedPackages.size() == 1) {
+ pw.println("Failure: package " + failedPackages.get(0) + " could not be compiled");
+ return 1;
} else {
- pw.println("Failure: package " + packageName + " could not be compiled");
+ pw.print("Failure: the following packages could not be compiled: ");
+ boolean is_first = true;
+ for (String packageName : failedPackages) {
+ if (is_first) {
+ is_first = false;
+ } else {
+ pw.print(", ");
+ }
+ pw.print(packageName);
+ }
+ pw.println();
return 1;
}
}
@@ -317,11 +376,7 @@
private int runListFeatures() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
- final List<FeatureInfo> list = new ArrayList<FeatureInfo>();
- final FeatureInfo[] rawList = mInterface.getSystemAvailableFeatures();
- for (int i=0; i<rawList.length; i++) {
- list.add(rawList[i]);
- }
+ final List<FeatureInfo> list = mInterface.getSystemAvailableFeatures().getList();
// sort by name
Collections.sort(list, new Comparator<FeatureInfo>() {
@@ -380,7 +435,7 @@
}
final List<InstrumentationInfo> list =
- mInterface.queryInstrumentation(targetPackage, 0 /*flags*/);
+ mInterface.queryInstrumentation(targetPackage, 0 /*flags*/).getList();
// sort by target package
Collections.sort(list, new Comparator<InstrumentationInfo>() {
@@ -521,7 +576,7 @@
private int runListPermissionGroups() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
- final List<PermissionGroupInfo> pgs = mInterface.getAllPermissionGroups(0);
+ final List<PermissionGroupInfo> pgs = mInterface.getAllPermissionGroups(0).getList();
final int count = pgs.size();
for (int p = 0; p < count ; p++) {
@@ -568,7 +623,7 @@
final ArrayList<String> groupList = new ArrayList<String>();
if (groups) {
final List<PermissionGroupInfo> infos =
- mInterface.getAllPermissionGroups(0 /*flags*/);
+ mInterface.getAllPermissionGroups(0 /*flags*/).getList();
final int count = infos.size();
for (int i = 0; i < count; i++) {
groupList.add(infos.get(i).name);
@@ -627,12 +682,18 @@
}
}
- String packageName = getNextArg();
+ final String packageName = getNextArg();
if (packageName == null) {
pw.println("Error: package name not specified");
return 1;
}
+ // if a split is specified, just remove it and not the whole package
+ final String splitName = getNextArg();
+ if (splitName != null) {
+ return runRemoveSplit(packageName, splitName);
+ }
+
userId = translateUserId(userId, "runUninstall");
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
@@ -670,6 +731,36 @@
}
}
+ private int runRemoveSplit(String packageName, String splitName) throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ final SessionParams sessionParams = new SessionParams(SessionParams.MODE_INHERIT_EXISTING);
+ sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ sessionParams.appPackageName = packageName;
+ final int sessionId =
+ doCreateSession(sessionParams, null /*installerPackageName*/, UserHandle.USER_ALL);
+ boolean abandonSession = true;
+ try {
+ if (doRemoveSplit(sessionId, splitName, false /*logSuccess*/)
+ != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ if (doCommitSession(sessionId, false /*logSuccess*/)
+ != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ abandonSession = false;
+ pw.println("Success");
+ return 0;
+ } finally {
+ if (abandonSession) {
+ try {
+ doAbandonSession(sessionId, false /*logSuccess*/);
+ } catch (Exception ignore) {
+ }
+ }
+ }
+ }
+
private Intent parseIntentAndUser() throws URISyntaxException {
mTargetUser = UserHandle.USER_CURRENT;
Intent intent = Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
@@ -718,7 +809,7 @@
}
try {
List<ResolveInfo> result = mInterface.queryIntentActivities(intent, null, 0,
- mTargetUser);
+ mTargetUser).getList();
PrintWriter pw = getOutPrintWriter();
if (result == null || result.size() <= 0) {
pw.println("No activities found");
@@ -745,7 +836,7 @@
}
try {
List<ResolveInfo> result = mInterface.queryIntentServices(intent, null, 0,
- mTargetUser);
+ mTargetUser).getList();
PrintWriter pw = getOutPrintWriter();
if (result == null || result.size() <= 0) {
pw.println("No services found");
@@ -772,7 +863,7 @@
}
try {
List<ResolveInfo> result = mInterface.queryIntentReceivers(intent, null, 0,
- mTargetUser);
+ mTargetUser).getList();
PrintWriter pw = getOutPrintWriter();
if (result == null || result.size() <= 0) {
pw.println("No receivers found");
@@ -909,7 +1000,7 @@
return sessionId;
}
- private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName,
+ private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
boolean logSuccess) throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
if ("-".equals(inPath)) {
@@ -965,6 +1056,27 @@
}
}
+ private int doRemoveSplit(int sessionId, String splitName, boolean logSuccess)
+ throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ PackageInstaller.Session session = null;
+ try {
+ session = new PackageInstaller.Session(
+ mInterface.getPackageInstaller().openSession(sessionId));
+ session.removeSplit(splitName);
+
+ if (logSuccess) {
+ pw.println("Success");
+ }
+ return 0;
+ } catch (IOException e) {
+ pw.println("Error: failed to remove split; " + e.getMessage());
+ return 1;
+ } finally {
+ IoUtils.closeQuietly(session);
+ }
+ }
+
private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
PackageInstaller.Session session = null;
@@ -1051,7 +1163,7 @@
prefix = " ";
}
List<PermissionInfo> ps =
- mInterface.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/);
+ mInterface.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/).getList();
final int count = ps.size();
boolean first = true;
for (int p = 0 ; p < count ; p++) {
@@ -1139,12 +1251,17 @@
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
- pw.println(" compile [-m MODE] [-f] TARGET-PACKAGE");
- pw.println(" Trigger compilation of TARGET-PACKAGE.");
+ pw.println(" compile [-m MODE] [-f] [-c] [--reset] (-a | TARGET-PACKAGE)");
+ pw.println(" Trigger compilation of TARGET-PACKAGE or all packages if \"-a\".");
pw.println(" Options:");
- pw.println(" -m: select compilation mode");
- pw.println(" MODE can be one of \"default\", \"all\", \"profile\", and \"extract\"");
+ pw.println(" -a: compile all packages");
+ pw.println(" -c: clear profile data before compiling");
pw.println(" -f: force compilation even if not needed");
+ pw.println(" -m: select compilation mode");
+ pw.println(" MODE can be one of \"default\", \"full\", \"profile\"," +
+ " and \"extract\"");
+ pw.println(" --reset: restore package to its post-install state");
+ pw.println(" shorthand for \"-c -f -m extract\"");
pw.println(" list features");
pw.println(" Prints all features of the system.");
pw.println(" list instrumentation [-f] [TARGET-PACKAGE]");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 310ad53..4c77f28 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2604,7 +2604,6 @@
if (pkg.volumeUuid != null) {
serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
}
-
if (pkg.parentPackageName != null) {
serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
}
@@ -4171,9 +4170,19 @@
};
static final Object[] PRIVATE_FLAG_DUMP_SPEC = new Object[] {
- ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, "PRIVILEGED",
- ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
+ ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
+ ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
+ ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, "PRIVILEGED",
+ ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
+ ApplicationInfo.PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED, "FORCE_DEVICE_ENCRYPTED",
+ ApplicationInfo.PRIVATE_FLAG_ENCRYPTION_AWARE, "ENCRYPTION_AWARE",
+ ApplicationInfo.PRIVATE_FLAG_AUTOPLAY, "AUTOPLAY",
+ ApplicationInfo.PRIVATE_FLAG_PARTIALLY_ENCRYPTION_AWARE, "PARTIALLY_ENCRYPTION_AWARE",
+ ApplicationInfo.PRIVATE_FLAG_EPHEMERAL, "EPHEMERAL",
+ ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER, "REQUIRED_FOR_SYSTEM_USER",
+ ApplicationInfo.PRIVATE_FLAG_RESIZEABLE_ACTIVITIES, "RESIZEABLE_ACTIVITIES",
+ ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND",
};
void dumpVersionLPr(IndentingPrintWriter pw) {
@@ -4282,6 +4291,7 @@
}
pw.print(prefix); pw.print(" versionCode="); pw.print(ps.versionCode);
if (ps.pkg != null) {
+ pw.print(" minSdk="); pw.print(ps.pkg.applicationInfo.minSdkVersion);
pw.print(" targetSdk="); pw.print(ps.pkg.applicationInfo.targetSdkVersion);
}
pw.println();
@@ -4316,6 +4326,10 @@
}
pw.print(prefix); pw.print(" versionName="); pw.println(ps.pkg.mVersionName);
pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, ps.pkg); pw.println();
+ final int apkSigningVersion = PackageParser.getApkSigningVersion(ps.pkg);
+ if (apkSigningVersion != PackageParser.APK_SIGNING_UNKNOWN) {
+ pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion);
+ }
pw.print(prefix); pw.print(" applicationInfo=");
pw.println(ps.pkg.applicationInfo.toString());
pw.print(prefix); pw.print(" flags="); printFlags(pw, ps.pkg.applicationInfo.flags,
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
new file mode 100644
index 0000000..3791eb0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -0,0 +1,2355 @@
+/*
+ * 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.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IShortcutService;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
+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.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+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.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.SELinux;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.text.format.Formatter;
+import android.text.format.Time;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import libcore.io.IoUtils;
+import libcore.util.Objects;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * TODO:
+ *
+ * - Detect when already registered instances are passed to APIs again, which might break
+ * internal bitmap handling.
+ *
+ * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
+ * -> Need to scan all packages when a user starts too.
+ * -> Clear data -> remove all dynamic? but not the pinned?
+ *
+ * - Pinned per each launcher package (multiple launchers)
+ *
+ * - Make save async (should we?)
+ *
+ * - Scan and remove orphan bitmaps (just in case).
+ *
+ * - Backup & restore
+ */
+public class ShortcutService extends IShortcutService.Stub {
+ static final String TAG = "ShortcutService";
+
+ static final boolean DEBUG = false; // STOPSHIP if true
+ static final boolean DEBUG_LOAD = false; // STOPSHIP if true
+
+ @VisibleForTesting
+ static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_DAILY_UPDATES = 10;
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96;
+
+ @VisibleForTesting
+ static final int DEFAULT_MAX_ICON_DIMENSION_LOWRAM_DP = 48;
+
+ @VisibleForTesting
+ static final String DEFAULT_ICON_PERSIST_FORMAT = CompressFormat.PNG.name();
+
+ @VisibleForTesting
+ static final int DEFAULT_ICON_PERSIST_QUALITY = 100;
+
+ private static final int SAVE_DELAY_MS = 5000; // in milliseconds.
+
+ @VisibleForTesting
+ static final String FILENAME_BASE_STATE = "shortcut_service.xml";
+
+ @VisibleForTesting
+ static final String DIRECTORY_PER_USER = "shortcut_service";
+
+ @VisibleForTesting
+ static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
+
+ static final String DIRECTORY_BITMAPS = "bitmaps";
+
+ static final String TAG_ROOT = "root";
+ static final String TAG_USER = "user";
+ static final String TAG_PACKAGE = "package";
+ static final String TAG_LAST_RESET_TIME = "last_reset_time";
+ static final String TAG_INTENT_EXTRAS = "intent-extras";
+ static final String TAG_EXTRAS = "extras";
+ static final String TAG_SHORTCUT = "shortcut";
+ static final String TAG_LAUNCHER = "launcher";
+
+ static final String ATTR_VALUE = "value";
+ static final String ATTR_NAME = "name";
+ static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
+ static final String ATTR_CALL_COUNT = "call-count";
+ static final String ATTR_LAST_RESET = "last-reset";
+ static final String ATTR_ID = "id";
+ static final String ATTR_ACTIVITY = "activity";
+ static final String ATTR_TITLE = "title";
+ static final String ATTR_INTENT = "intent";
+ static final String ATTR_WEIGHT = "weight";
+ static final String ATTR_TIMESTAMP = "timestamp";
+ static final String ATTR_FLAGS = "flags";
+ static final String ATTR_ICON_RES = "icon-res";
+ static final String ATTR_BITMAP_PATH = "bitmap-path";
+
+ @VisibleForTesting
+ interface ConfigConstants {
+ /**
+ * Key name for the throttling reset interval, in seconds. (long)
+ */
+ String KEY_RESET_INTERVAL_SEC = "reset_interval_sec";
+
+ /**
+ * Key name for the max number of modifying API calls per app for every interval. (int)
+ */
+ String KEY_MAX_DAILY_UPDATES = "max_daily_updates";
+
+ /**
+ * Key name for the max icon dimensions in DP, for non-low-memory devices.
+ */
+ String KEY_MAX_ICON_DIMENSION_DP = "max_icon_dimension_dp";
+
+ /**
+ * Key name for the max icon dimensions in DP, for low-memory devices.
+ */
+ String KEY_MAX_ICON_DIMENSION_DP_LOWRAM = "max_icon_dimension_dp_lowram";
+
+ /**
+ * Key name for the max dynamic shortcuts per app. (int)
+ */
+ String KEY_MAX_SHORTCUTS = "max_shortcuts";
+
+ /**
+ * Key name for icon compression quality, 0-100.
+ */
+ String KEY_ICON_QUALITY = "icon_quality";
+
+ /**
+ * Key name for icon compression format: "PNG", "JPEG" or "WEBP"
+ */
+ String KEY_ICON_FORMAT = "icon_format";
+ }
+
+ final Context mContext;
+
+ private final Object mLock = new Object();
+
+ private final Handler mHandler;
+
+ @GuardedBy("mLock")
+ private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
+
+ @GuardedBy("mLock")
+ private long mRawLastResetTime;
+
+ /**
+ * User ID -> UserShortcuts
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<UserShortcuts> mUsers = new SparseArray<>();
+
+ /**
+ * Max number of dynamic shortcuts that each application can have at a time.
+ */
+ private int mMaxDynamicShortcuts;
+
+ /**
+ * Max number of updating API calls that each application can make a day.
+ */
+ int mMaxDailyUpdates;
+
+ /**
+ * Actual throttling-reset interval. By default it's a day.
+ */
+ private long mResetInterval;
+
+ /**
+ * Icon max width/height in pixels.
+ */
+ private int mMaxIconDimension;
+
+ private CompressFormat mIconPersistFormat;
+ private int mIconPersistQuality;
+
+ private final PackageManagerInternal mPackageManagerInternal;
+
+ public ShortcutService(Context context) {
+ mContext = Preconditions.checkNotNull(context);
+ LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
+ mHandler = new Handler(BackgroundThread.get().getLooper());
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ /**
+ * System service lifecycle.
+ */
+ public static final class Lifecycle extends SystemService {
+ final ShortcutService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mService = new ShortcutService(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.SHORTCUT_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ mService.onBootPhase(phase);
+ }
+
+ @Override
+ public void onCleanupUser(int userHandle) {
+ synchronized (mService.mLock) {
+ mService.onCleanupUserInner(userHandle);
+ }
+ }
+
+ @Override
+ public void onUnlockUser(int userId) {
+ synchronized (mService.mLock) {
+ mService.onStartUserLocked(userId);
+ }
+ }
+ }
+
+ /** lifecycle event */
+ void onBootPhase(int phase) {
+ if (DEBUG) {
+ Slog.d(TAG, "onBootPhase: " + phase);
+ }
+ switch (phase) {
+ case SystemService.PHASE_LOCK_SETTINGS_READY:
+ initialize();
+ break;
+ }
+ }
+
+ /** lifecycle event */
+ void onStartUserLocked(int userId) {
+ // Preload
+ getUserShortcutsLocked(userId);
+ }
+
+ /** lifecycle event */
+ void onCleanupUserInner(int userId) {
+ // Unload
+ mUsers.delete(userId);
+ }
+
+ /** Return the base state file name */
+ private AtomicFile getBaseStateFile() {
+ final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
+ path.mkdirs();
+ return new AtomicFile(path);
+ }
+
+ /**
+ * Init the instance. (load the state file, etc)
+ */
+ private void initialize() {
+ synchronized (mLock) {
+ loadConfigurationLocked();
+ loadBaseStateLocked();
+ }
+ }
+
+ /**
+ * Load the configuration from Settings.
+ */
+ private void loadConfigurationLocked() {
+ updateConfigurationLocked(injectShortcutManagerConstants());
+ }
+
+ /**
+ * Load the configuration from Settings.
+ */
+ @VisibleForTesting
+ boolean updateConfigurationLocked(String config) {
+ boolean result = true;
+
+ final KeyValueListParser parser = new KeyValueListParser(',');
+ try {
+ parser.setString(config);
+ } catch (IllegalArgumentException e) {
+ // Failed to parse the settings string, log this and move on
+ // with defaults.
+ Slog.e(TAG, "Bad shortcut manager settings", e);
+ result = false;
+ }
+
+ mResetInterval = parser.getLong(
+ ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC)
+ * 1000L;
+
+ mMaxDailyUpdates = (int) parser.getLong(
+ ConfigConstants.KEY_MAX_DAILY_UPDATES, DEFAULT_MAX_DAILY_UPDATES);
+
+ mMaxDynamicShortcuts = (int) parser.getLong(
+ ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_APP);
+
+ final int iconDimensionDp = injectIsLowRamDevice()
+ ? (int) parser.getLong(
+ 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);
+
+ mMaxIconDimension = injectDipToPixel(iconDimensionDp);
+
+ mIconPersistFormat = CompressFormat.valueOf(
+ parser.getString(ConfigConstants.KEY_ICON_FORMAT, DEFAULT_ICON_PERSIST_FORMAT));
+
+ mIconPersistQuality = (int) parser.getLong(
+ ConfigConstants.KEY_ICON_QUALITY,
+ DEFAULT_ICON_PERSIST_QUALITY);
+
+ return result;
+ }
+
+ @VisibleForTesting
+ String injectShortcutManagerConstants() {
+ return android.provider.Settings.Global.getString(
+ mContext.getContentResolver(),
+ android.provider.Settings.Global.SHORTCUT_MANAGER_CONSTANTS);
+ }
+
+ @VisibleForTesting
+ int injectDipToPixel(int dip) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
+ mContext.getResources().getDisplayMetrics());
+ }
+
+ // === Persisting ===
+
+ @Nullable
+ static String parseStringAttribute(XmlPullParser parser, String attribute) {
+ return parser.getAttributeValue(null, attribute);
+ }
+
+ static int parseIntAttribute(XmlPullParser parser, String attribute) {
+ return (int) parseLongAttribute(parser, attribute);
+ }
+
+ static long parseLongAttribute(XmlPullParser parser, String attribute) {
+ final String value = parseStringAttribute(parser, attribute);
+ if (TextUtils.isEmpty(value)) {
+ return 0;
+ }
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Error parsing long " + value);
+ return 0;
+ }
+ }
+
+ @Nullable
+ static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
+ final String value = parseStringAttribute(parser, attribute);
+ if (TextUtils.isEmpty(value)) {
+ return null;
+ }
+ return ComponentName.unflattenFromString(value);
+ }
+
+ @Nullable
+ static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
+ final String value = parseStringAttribute(parser, attribute);
+ if (TextUtils.isEmpty(value)) {
+ return null;
+ }
+ try {
+ return Intent.parseUri(value, /* flags =*/ 0);
+ } catch (URISyntaxException e) {
+ Slog.e(TAG, "Error parsing intent", e);
+ return null;
+ }
+ }
+
+ static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
+ if (TextUtils.isEmpty(value)) return;
+
+ out.startTag(null, tag);
+ out.attribute(null, ATTR_VALUE, value);
+ out.endTag(null, tag);
+ }
+
+ static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
+ writeTagValue(out, tag, Long.toString(value));
+ }
+
+ static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException {
+ if (name == null) return;
+ writeTagValue(out, tag, name.flattenToString());
+ }
+
+ static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
+ throws IOException, XmlPullParserException {
+ if (bundle == null) return;
+
+ out.startTag(null, tag);
+ bundle.saveToXml(out);
+ out.endTag(null, tag);
+ }
+
+ static void writeAttr(XmlSerializer out, String name, String value) throws IOException {
+ if (TextUtils.isEmpty(value)) return;
+
+ out.attribute(null, name, value);
+ }
+
+ static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
+ writeAttr(out, name, String.valueOf(value));
+ }
+
+ static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
+ if (comp == null) return;
+ writeAttr(out, name, comp.flattenToString());
+ }
+
+ static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
+ if (intent == null) return;
+
+ writeAttr(out, name, intent.toUri(/* flags =*/ 0));
+ }
+
+ @VisibleForTesting
+ void saveBaseStateLocked() {
+ final AtomicFile file = getBaseStateFile();
+ if (DEBUG) {
+ Slog.i(TAG, "Saving to " + file.getBaseFile());
+ }
+
+ FileOutputStream outs = null;
+ try {
+ outs = file.startWrite();
+
+ // Write to XML
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(outs, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.startTag(null, TAG_ROOT);
+
+ // Body.
+ writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
+
+ // Epilogue.
+ out.endTag(null, TAG_ROOT);
+ out.endDocument();
+
+ // Close.
+ file.finishWrite(outs);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
+ file.failWrite(outs);
+ }
+ }
+
+ private void loadBaseStateLocked() {
+ mRawLastResetTime = 0;
+
+ final AtomicFile file = getBaseStateFile();
+ if (DEBUG) {
+ Slog.i(TAG, "Loading from " + file.getBaseFile());
+ }
+ try (FileInputStream in = file.openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ // Check the root tag
+ final String tag = parser.getName();
+ if (depth == 1) {
+ if (!TAG_ROOT.equals(tag)) {
+ Slog.e(TAG, "Invalid root tag: " + tag);
+ return;
+ }
+ continue;
+ }
+ // Assume depth == 2
+ switch (tag) {
+ case TAG_LAST_RESET_TIME:
+ mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
+ break;
+ default:
+ Slog.e(TAG, "Invalid tag: " + tag);
+ break;
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Use the default
+ } catch (IOException|XmlPullParserException e) {
+ Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
+
+ mRawLastResetTime = 0;
+ }
+ // Adjust the last reset time.
+ getLastResetTimeLocked();
+ }
+
+ private void saveUserLocked(@UserIdInt int userId) {
+ final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
+ if (DEBUG) {
+ Slog.i(TAG, "Saving to " + path);
+ }
+ path.mkdirs();
+ final AtomicFile file = new AtomicFile(path);
+ FileOutputStream outs = null;
+ try {
+ outs = file.startWrite();
+
+ // Write to XML
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(outs, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+
+ getUserShortcutsLocked(userId).saveToXml(out);
+
+ out.endDocument();
+
+ // Close.
+ file.finishWrite(outs);
+ } catch (IOException|XmlPullParserException e) {
+ Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
+ file.failWrite(outs);
+ }
+ }
+
+ static IOException throwForInvalidTag(int depth, String tag) throws IOException {
+ throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
+ }
+
+ @Nullable
+ private UserShortcuts loadUserLocked(@UserIdInt int userId) {
+ final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
+ if (DEBUG) {
+ Slog.i(TAG, "Loading from " + path);
+ }
+ final AtomicFile file = new AtomicFile(path);
+
+ final FileInputStream in;
+ try {
+ in = file.openRead();
+ } catch (FileNotFoundException e) {
+ if (DEBUG) {
+ Slog.i(TAG, "Not found " + path);
+ }
+ return null;
+ }
+ UserShortcuts ret = null;
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+
+ final String tag = parser.getName();
+ if (DEBUG_LOAD) {
+ Slog.d(TAG, String.format("depth=%d type=%d name=%s",
+ depth, type, tag));
+ }
+ if ((depth == 1) && TAG_USER.equals(tag)) {
+ ret = UserShortcuts.loadFromXml(parser, userId);
+ continue;
+ }
+ throwForInvalidTag(depth, tag);
+ }
+ return ret;
+ } catch (IOException|XmlPullParserException e) {
+ Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
+ return null;
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ // TODO Actually make it async.
+ private void scheduleSaveBaseState() {
+ synchronized (mLock) {
+ saveBaseStateLocked();
+ }
+ }
+
+ // TODO Actually make it async.
+ void scheduleSaveUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ saveUserLocked(userId);
+ }
+ }
+
+ /** Return the last reset time. */
+ long getLastResetTimeLocked() {
+ updateTimes();
+ return mRawLastResetTime;
+ }
+
+ /** Return the next reset time. */
+ long getNextResetTimeLocked() {
+ updateTimes();
+ return mRawLastResetTime + mResetInterval;
+ }
+
+ static boolean isClockValid(long time) {
+ return time >= 1420070400; // Thu, 01 Jan 2015 00:00:00 GMT
+ }
+
+ /**
+ * Update the last reset time.
+ */
+ private void updateTimes() {
+
+ final long now = injectCurrentTimeMillis();
+
+ final long prevLastResetTime = mRawLastResetTime;
+
+ if (mRawLastResetTime == 0) { // first launch.
+ // TODO Randomize??
+ mRawLastResetTime = now;
+ } else if (now < mRawLastResetTime) {
+ // Clock rewound.
+ if (isClockValid(now)) {
+ // TODO Randomize??
+ mRawLastResetTime = now;
+ }
+ } else {
+ // TODO Do it properly.
+ while ((mRawLastResetTime + mResetInterval) <= now) {
+ mRawLastResetTime += mResetInterval;
+ }
+ }
+ if (prevLastResetTime != mRawLastResetTime) {
+ scheduleSaveBaseState();
+ }
+ }
+
+ /** Return the per-user state. */
+ @GuardedBy("mLock")
+ @NonNull
+ private UserShortcuts getUserShortcutsLocked(@UserIdInt int userId) {
+ UserShortcuts userPackages = mUsers.get(userId);
+ if (userPackages == null) {
+ userPackages = loadUserLocked(userId);
+ if (userPackages == null) {
+ userPackages = new UserShortcuts(userId);
+ }
+ mUsers.put(userId, userPackages);
+ }
+ return userPackages;
+ }
+
+ /** Return the per-user per-package state. */
+ @GuardedBy("mLock")
+ @NonNull
+ private PackageShortcuts getPackageShortcutsLocked(
+ @NonNull String packageName, @UserIdInt int userId) {
+ final UserShortcuts userPackages = getUserShortcutsLocked(userId);
+ PackageShortcuts shortcuts = userPackages.getPackages().get(packageName);
+ if (shortcuts == null) {
+ shortcuts = new PackageShortcuts(userId, packageName);
+ userPackages.getPackages().put(packageName, shortcuts);
+ }
+ return shortcuts;
+ }
+
+ // === 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);
+ }
+ }
+
+ @VisibleForTesting
+ static class FileOutputStreamWithPath extends FileOutputStream {
+ private final File mFile;
+
+ public FileOutputStreamWithPath(File file) throws FileNotFoundException {
+ super(file);
+ mFile = file;
+ }
+
+ public File getFile() {
+ return mFile;
+ }
+ }
+
+ /**
+ * Build the cached bitmap filename for a shortcut icon.
+ *
+ * The filename will be based on the ID, except certain characters will be escaped.
+ */
+ @VisibleForTesting
+ FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
+ throws IOException {
+ final File packagePath = new File(getUserBitmapFilePath(userId),
+ shortcut.getPackageName());
+ if (!packagePath.isDirectory()) {
+ packagePath.mkdirs();
+ if (!packagePath.isDirectory()) {
+ throw new IOException("Unable to create directory " + packagePath);
+ }
+ SELinux.restorecon(packagePath);
+ }
+
+ final String baseName = String.valueOf(injectCurrentTimeMillis());
+ for (int suffix = 0;; suffix++) {
+ final String filename = (suffix == 0 ? baseName : baseName + "_" + suffix) + ".png";
+ final File file = new File(packagePath, filename);
+ if (!file.exists()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Saving icon to " + file.getAbsolutePath());
+ }
+ return new FileOutputStreamWithPath(file);
+ }
+ }
+ }
+
+ void saveIconAndFixUpShortcut(@UserIdInt int userId, ShortcutInfo shortcut) {
+ if (shortcut.hasIconFile() || shortcut.hasIconResource()) {
+ return;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Clear icon info on the shortcut.
+ shortcut.setIconResourceId(0);
+ shortcut.setBitmapPath(null);
+
+ final Icon icon = shortcut.getIcon();
+ if (icon == null) {
+ return; // has no icon
+ }
+
+ Bitmap bitmap = null;
+ try {
+ switch (icon.getType()) {
+ case Icon.TYPE_RESOURCE: {
+ injectValidateIconResPackage(shortcut, icon);
+
+ shortcut.setIconResourceId(icon.getResId());
+ shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_RES);
+ return;
+ }
+ case Icon.TYPE_BITMAP: {
+ bitmap = icon.getBitmap();
+ break;
+ }
+ case Icon.TYPE_URI: {
+ final Uri uri = ContentProvider.maybeAddUserId(icon.getUri(), userId);
+
+ try (InputStream is = mContext.getContentResolver().openInputStream(uri)) {
+
+ bitmap = BitmapFactory.decodeStream(is);
+
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to load icon from " + uri);
+ return;
+ }
+ break;
+ }
+ default:
+ // This shouldn't happen because we've already validated the icon, but
+ // just in case.
+ throw ShortcutInfo.getInvalidIconException();
+ }
+ if (bitmap == null) {
+ Slog.e(TAG, "Null bitmap detected");
+ return;
+ }
+ // Shrink and write to the file.
+ File path = null;
+ try {
+ final FileOutputStreamWithPath out = openIconFileForWrite(userId, shortcut);
+ try {
+ path = out.getFile();
+
+ shrinkBitmap(bitmap, mMaxIconDimension)
+ .compress(mIconPersistFormat, mIconPersistQuality, out);
+
+ shortcut.setBitmapPath(out.getFile().getAbsolutePath());
+ shortcut.addFlags(ShortcutInfo.FLAG_HAS_ICON_FILE);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ } 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()) {
+ path.delete();
+ }
+ }
+ } finally {
+ if (bitmap != null) {
+ bitmap.recycle();
+ }
+ // Once saved, we won't use the original icon information, so null it out.
+ shortcut.clearIcon();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Unfortunately we can't do this check in unit tests because we fake creator package names,
+ // so override in unit tests.
+ // TODO CTS this case.
+ void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
+ if (!shortcut.getPackageName().equals(icon.getResPackage())) {
+ throw new IllegalArgumentException(
+ "Icon resource must reside in shortcut owner package");
+ }
+ }
+
+ @VisibleForTesting
+ static Bitmap shrinkBitmap(Bitmap in, int maxSize) {
+ // Original width/height.
+ final int ow = in.getWidth();
+ final int oh = in.getHeight();
+ if ((ow <= maxSize) && (oh <= maxSize)) {
+ if (DEBUG) {
+ Slog.d(TAG, String.format("Icon size %dx%d, no need to shrink", ow, oh));
+ }
+ return in;
+ }
+ final int longerDimension = Math.max(ow, oh);
+
+ // New width and height.
+ final int nw = ow * maxSize / longerDimension;
+ final int nh = oh * maxSize / longerDimension;
+ if (DEBUG) {
+ Slog.d(TAG, String.format("Icon size %dx%d, shrinking to %dx%d",
+ ow, oh, nw, nh));
+ }
+
+ final Bitmap scaledBitmap = Bitmap.createBitmap(nw, nh, Bitmap.Config.ARGB_8888);
+ final Canvas c = new Canvas(scaledBitmap);
+
+ final RectF dst = new RectF(0, 0, nw, nh);
+
+ c.drawBitmap(in, /*src=*/ null, dst, /* paint =*/ null);
+
+ in.recycle();
+
+ return scaledBitmap;
+ }
+
+ // === Caller validation ===
+
+ private boolean isCallerSystem() {
+ final int callingUid = injectBinderCallingUid();
+ return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
+ }
+
+ private boolean isCallerShell() {
+ final int callingUid = injectBinderCallingUid();
+ return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+ }
+
+ private void enforceSystemOrShell() {
+ Preconditions.checkState(isCallerSystem() || isCallerShell(),
+ "Caller must be system or shell");
+ }
+
+ private void enforceShell() {
+ Preconditions.checkState(isCallerShell(), "Caller must be shell");
+ }
+
+ private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+
+ if (isCallerSystem()) {
+ return; // no check
+ }
+
+ final int callingUid = injectBinderCallingUid();
+
+ // Otherwise, make sure the arguments are valid.
+ if (UserHandle.getUserId(callingUid) != userId) {
+ throw new SecurityException("Invalid user-ID");
+ }
+ if (injectGetPackageUid(packageName, userId) == injectBinderCallingUid()) {
+ return; // Caller is valid.
+ }
+ throw new SecurityException("Caller UID= doesn't own " + packageName);
+ }
+
+ // Test overrides it.
+ int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
+ try {
+
+ // TODO Is MATCH_UNINSTALLED_PACKAGES correct to get SD card app info?
+
+ return mContext.getPackageManager().getPackageUidAsUser(packageName,
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ } catch (NameNotFoundException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
+ */
+ void enforceMaxDynamicShortcuts(int numShortcuts) {
+ if (numShortcuts > mMaxDynamicShortcuts) {
+ throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
+ }
+ }
+
+ /**
+ * - Sends a notification to LauncherApps
+ * - Write to file
+ */
+ private void userPackageChanged(@NonNull String packageName, @UserIdInt int userId) {
+ notifyListeners(packageName, userId);
+ scheduleSaveUser(userId);
+ }
+
+ private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
+ final ArrayList<ShortcutChangeListener> copy;
+ final List<ShortcutInfo> shortcuts = new ArrayList<>();
+ synchronized (mLock) {
+ copy = new ArrayList<>(mListeners);
+
+ getPackageShortcutsLocked(packageName, userId)
+ .findAll(shortcuts, /* query =*/ null, ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+ }
+ for (int i = copy.size() - 1; i >= 0; i--) {
+ copy.get(i).onShortcutChanged(packageName, shortcuts, userId);
+ }
+ }
+
+ /**
+ * 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.
+ * - Clear flags.
+ *
+ * TODO Detailed unit tests
+ */
+ private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
+ Preconditions.checkNotNull(shortcut, "Null shortcut detected");
+ if (shortcut.getActivityComponent() != null) {
+ Preconditions.checkState(
+ shortcut.getPackageName().equals(
+ shortcut.getActivityComponent().getPackageName()),
+ "Activity package name mismatch");
+ }
+
+ if (!forUpdate) {
+ shortcut.enforceMandatoryFields();
+ }
+ if (shortcut.getIcon() != null) {
+ ShortcutInfo.validateIcon(shortcut.getIcon());
+ }
+
+ validateForXml(shortcut.getId());
+ validateForXml(shortcut.getTitle());
+ validatePersistableBundleForXml(shortcut.getIntentPersistableExtras());
+ validatePersistableBundleForXml(shortcut.getExtras());
+
+ shortcut.setFlags(0);
+ }
+
+ // KXmlSerializer is strict and doesn't allow certain characters, so we disallow those
+ // characters.
+
+ 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);
+ }
+ } else if (value instanceof PersistableBundle) {
+ validatePersistableBundleForXml((PersistableBundle) value);
+ }
+ }
+ }
+
+ private static void validateForXml(String s) {
+ if (TextUtils.isEmpty(s)) {
+ return;
+ }
+ 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 ===
+
+ @Override
+ public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+
+ final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+ final int size = newShortcuts.size();
+
+ synchronized (mLock) {
+ final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
+
+ // Throttling.
+ if (!ps.tryApiCall(this)) {
+ return false;
+ }
+ enforceMaxDynamicShortcuts(size);
+
+ // Validate the shortcuts.
+ for (int i = 0; i < size; i++) {
+ fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
+ }
+
+ // First, remove all un-pinned; dynamic shortcuts
+ ps.deleteAllDynamicShortcuts(this);
+
+ // 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);
+ newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+ ps.updateShortcutWithCapping(this, newShortcut);
+ }
+ }
+ userPackageChanged(packageName, userId);
+ return true;
+ }
+
+ @Override
+ public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+
+ final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
+ final int size = newShortcuts.size();
+
+ synchronized (mLock) {
+ final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
+
+ // Throttling.
+ if (!ps.tryApiCall(this)) {
+ return false;
+ }
+
+ 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);
+ }
+
+ target.copyNonNullFieldsFrom(source);
+
+ if (replacingIcon) {
+ saveIconAndFixUpShortcut(userId, target);
+ }
+ }
+ }
+ }
+ userPackageChanged(packageName, userId);
+
+ return true;
+ }
+
+ @Override
+ public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+
+ synchronized (mLock) {
+ final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
+
+ // Throttling.
+ if (!ps.tryApiCall(this)) {
+ return false;
+ }
+
+ // Validate the shortcut.
+ fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
+
+ // Add it.
+ newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+ ps.updateShortcutWithCapping(this, newShortcut);
+ }
+ userPackageChanged(packageName, userId);
+
+ return true;
+ }
+
+ @Override
+ public void deleteDynamicShortcut(String packageName, String shortcutId,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+ Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided");
+
+ synchronized (mLock) {
+ getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(this, shortcutId);
+ }
+ userPackageChanged(packageName, userId);
+ }
+
+ @Override
+ public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+
+ synchronized (mLock) {
+ getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(this);
+ }
+ userPackageChanged(packageName, userId);
+ }
+
+ @Override
+ public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+ synchronized (mLock) {
+ return getShortcutsWithQueryLocked(
+ packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
+ ShortcutInfo::isDynamic);
+ }
+ }
+
+ @Override
+ public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+ synchronized (mLock) {
+ return getShortcutsWithQueryLocked(
+ packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
+ ShortcutInfo::isPinned);
+ }
+ }
+
+ private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
+ @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
+
+ final ArrayList<ShortcutInfo> ret = new ArrayList<>();
+
+ getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
+
+ return new ParceledListSlice<>(ret);
+ }
+
+ @Override
+ public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId)
+ throws RemoteException {
+ verifyCaller(packageName, userId);
+
+ return mMaxDynamicShortcuts;
+ }
+
+ @Override
+ public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+
+ synchronized (mLock) {
+ return mMaxDailyUpdates
+ - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
+ }
+ }
+
+ @Override
+ public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+
+ synchronized (mLock) {
+ return getNextResetTimeLocked();
+ }
+ }
+
+ @Override
+ public int getIconMaxDimensions(String packageName, int userId) throws RemoteException {
+ synchronized (mLock) {
+ return mMaxIconDimension;
+ }
+ }
+
+ /**
+ * Reset all throttling, for developer options and command line. Only system/shell can call it.
+ */
+ @Override
+ public void resetThrottling() {
+ enforceSystemOrShell();
+
+ resetThrottlingInner(getCallingUserId());
+ }
+
+ void resetThrottlingInner(@UserIdInt int userId) {
+ synchronized (mLock) {
+ getUserShortcutsLocked(userId).resetThrottling();
+ }
+ scheduleSaveUser(userId);
+ Slog.i(TAG, "ShortcutManager: throttling counter reset");
+ }
+
+ // We override this method in unit tests to do a simpler check.
+ boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
+ return hasShortcutHostPermissionInner(callingPackage, userId);
+ }
+
+ // This method is extracted so we can directly call this method from unit tests,
+ // even when hasShortcutPermission() is overridden.
+ @VisibleForTesting
+ boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
+ synchronized (mLock) {
+ long start = 0;
+ if (DEBUG) {
+ start = System.currentTimeMillis();
+ }
+
+ final UserShortcuts user = getUserShortcutsLocked(userId);
+
+ final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
+
+ // Default launcher from package manager.
+ final ComponentName defaultLauncher = injectPackageManagerInternal()
+ .getHomeActivitiesAsUser(allHomeCandidates, userId);
+
+ ComponentName detected;
+ if (defaultLauncher != null) {
+ detected = defaultLauncher;
+ if (DEBUG) {
+ Slog.v(TAG, "Default launcher from PM: " + detected);
+ }
+ } else {
+ detected = user.getLauncherComponent();
+
+ // TODO: Make sure it's still enabled.
+ if (DEBUG) {
+ Slog.v(TAG, "Cached launcher: " + detected);
+ }
+ }
+
+ if (detected == null) {
+ // If we reach here, that means it's the first check since the user was created,
+ // and there's already multiple launchers and there's no default set.
+ // Find the system one with the highest priority.
+ // (We need to check the priority too because of FallbackHome in Settings.)
+ // If there's no system launcher yet, then no one can access shortcuts, until
+ // the user explicitly
+ final int size = allHomeCandidates.size();
+
+ int lastPriority = Integer.MIN_VALUE;
+ for (int i = 0; i < size; i++) {
+ final ResolveInfo ri = allHomeCandidates.get(i);
+ if (!ri.activityInfo.applicationInfo.isSystemApp()) {
+ continue;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
+ ri.activityInfo.getComponentName(), ri.priority));
+ }
+ if (ri.priority < lastPriority) {
+ continue;
+ }
+ detected = ri.activityInfo.getComponentName();
+ lastPriority = ri.priority;
+ }
+ }
+ if (DEBUG) {
+ long end = System.currentTimeMillis();
+ Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start));
+ }
+ 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.
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Entry point from {@link LauncherApps}.
+ */
+ private class LocalService extends ShortcutServiceInternal {
+ @Override
+ public List<ShortcutInfo> getShortcuts(
+ @NonNull String callingPackage, long changedSince,
+ @Nullable String packageName, @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;
+
+ synchronized (mLock) {
+ if (packageName != null) {
+ getShortcutsInnerLocked(packageName, changedSince, componentName, queryFlags,
+ userId, ret, cloneFlag);
+ } else {
+ final ArrayMap<String, PackageShortcuts> packages =
+ getUserShortcutsLocked(userId).getPackages();
+ for (int i = packages.size() - 1; i >= 0; i--) {
+ getShortcutsInnerLocked(
+ packages.keyAt(i),
+ changedSince, componentName, queryFlags, userId, ret, cloneFlag);
+ }
+ }
+ }
+ return ret;
+ }
+
+ private void getShortcutsInnerLocked(@Nullable String packageName,long changedSince,
+ @Nullable ComponentName componentName, int queryFlags,
+ int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
+ getPackageShortcutsLocked(packageName, userId).findAll(ret,
+ (ShortcutInfo si) -> {
+ if (si.getLastChangedTimestamp() < changedSince) {
+ return false;
+ }
+ if (componentName != null
+ && !componentName.equals(si.getActivityComponent())) {
+ 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;
+ }, cloneFlag);
+ }
+
+ @Override
+ public List<ShortcutInfo> getShortcutInfo(
+ @NonNull String callingPackage,
+ @NonNull String packageName, @Nullable List<String> ids, int userId) {
+ // Calling permission must be checked by LauncherAppsImpl.
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+
+ final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
+ final ArraySet<String> idSet = new ArraySet<>(ids);
+ synchronized (mLock) {
+ getPackageShortcutsLocked(packageName, userId).findAll(ret,
+ (ShortcutInfo si) -> idSet.contains(si.getId()),
+ ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+ }
+ return ret;
+ }
+
+ @Override
+ public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
+ @NonNull List<String> shortcutIds, int userId) {
+ // Calling permission must be checked by LauncherAppsImpl.
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+ Preconditions.checkNotNull(shortcutIds, "shortcutIds");
+
+ synchronized (mLock) {
+ getPackageShortcutsLocked(packageName, userId).replacePinned(
+ ShortcutService.this, callingPackage, shortcutIds);
+ }
+ userPackageChanged(packageName, userId);
+ }
+
+ @Override
+ public Intent createShortcutIntent(@NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ // Calling permission must be checked by LauncherAppsImpl.
+ Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
+ Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
+
+ synchronized (mLock) {
+ final ShortcutInfo fullShortcut =
+ getPackageShortcutsLocked(packageName, userId)
+ .findShortcutById(shortcutId);
+ return fullShortcut == null ? null : fullShortcut.getIntent();
+ }
+ }
+
+ @Override
+ public void addListener(@NonNull ShortcutChangeListener listener) {
+ synchronized (mLock) {
+ mListeners.add(Preconditions.checkNotNull(listener));
+ }
+ }
+
+ @Override
+ public int getShortcutIconResId(@NonNull String callingPackage,
+ @NonNull ShortcutInfo shortcut, int userId) {
+ Preconditions.checkNotNull(shortcut, "shortcut");
+
+ synchronized (mLock) {
+ final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
+ shortcut.getPackageName(), userId).findShortcutById(shortcut.getId());
+ return (shortcutInfo != null && shortcutInfo.hasIconResource())
+ ? shortcutInfo.getIconResourceId() : 0;
+ }
+ }
+
+ @Override
+ public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
+ @NonNull ShortcutInfo shortcutIn, int userId) {
+ Preconditions.checkNotNull(shortcutIn, "shortcut");
+
+ synchronized (mLock) {
+ final ShortcutInfo shortcutInfo = getPackageShortcutsLocked(
+ shortcutIn.getPackageName(), userId).findShortcutById(shortcutIn.getId());
+ if (shortcutInfo == null || !shortcutInfo.hasIconFile()) {
+ return null;
+ }
+ try {
+ if (shortcutInfo.getBitmapPath() == null) {
+ Slog.w(TAG, "null bitmap detected in getShortcutIconFd()");
+ return null;
+ }
+ return ParcelFileDescriptor.open(
+ new File(shortcutInfo.getBitmapPath()),
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "Icon file not found: " + shortcutInfo.getBitmapPath());
+ return null;
+ }
+ }
+ }
+
+ @Override
+ public boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
+ return ShortcutService.this.hasShortcutHostPermission(callingPackage, userId);
+ }
+ }
+
+ // === Dump ===
+
+ @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;
+ }
+ dumpInner(pw);
+ }
+
+ @VisibleForTesting
+ void dumpInner(PrintWriter pw) {
+ synchronized (mLock) {
+ final long now = injectCurrentTimeMillis();
+ pw.print("Now: [");
+ pw.print(now);
+ pw.print("] ");
+ pw.print(formatTime(now));
+
+ pw.print(" Raw last reset: [");
+ pw.print(mRawLastResetTime);
+ pw.print("] ");
+ pw.print(formatTime(mRawLastResetTime));
+
+ final long last = getLastResetTimeLocked();
+ pw.print(" Last reset: [");
+ pw.print(last);
+ pw.print("] ");
+ pw.print(formatTime(last));
+
+ final long next = getNextResetTimeLocked();
+ pw.print(" Next reset: [");
+ pw.print(next);
+ pw.print("] ");
+ pw.print(formatTime(next));
+ pw.println();
+
+ pw.print(" Max icon dim: ");
+ pw.print(mMaxIconDimension);
+ pw.print(" Icon format: ");
+ pw.print(mIconPersistFormat);
+ pw.print(" Icon quality: ");
+ pw.print(mIconPersistQuality);
+ pw.println();
+
+
+ for (int i = 0; i < mUsers.size(); i++) {
+ pw.println();
+ mUsers.valueAt(i).dump(this, pw, " ");
+ }
+ }
+ }
+
+ static String formatTime(long time) {
+ Time tobj = new Time();
+ tobj.set(time);
+ return tobj.format("%Y-%m-%d %H:%M:%S");
+ }
+
+ // === Shell support ===
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ResultReceiver resultReceiver) throws RemoteException {
+
+ enforceShell();
+
+ (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
+ }
+
+ static class CommandException extends Exception {
+ public CommandException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * Handle "adb shell cmd".
+ */
+ private class MyShellCommand extends ShellCommand {
+
+ private int mUserId = UserHandle.USER_SYSTEM;
+
+ private void parseOptions(boolean takeUser)
+ throws CommandException {
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--user":
+ if (takeUser) {
+ mUserId = UserHandle.parseUserArg(getNextArgRequired());
+ break;
+ }
+ // fallthrough
+ default:
+ throw new CommandException("Unknown option: " + opt);
+ }
+ }
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ switch (cmd) {
+ case "reset-package-throttling":
+ handleResetPackageThrottling();
+ break;
+ case "reset-throttling":
+ handleResetThrottling();
+ break;
+ case "override-config":
+ handleOverrideConfig();
+ break;
+ case "reset-config":
+ handleResetConfig();
+ break;
+ case "clear-default-launcher":
+ handleClearDefaultLauncher();
+ break;
+ case "get-default-launcher":
+ handleGetDefaultLauncher();
+ break;
+ case "refresh-default-launcher":
+ handleRefreshDefaultLauncher();
+ break;
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (CommandException e) {
+ pw.println("Error: " + e.getMessage());
+ return 1;
+ }
+ pw.println("Success");
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ 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");
+ pw.println(" Reset throttling for all packages and users");
+ pw.println();
+ pw.println("cmd shortcut override-config CONFIG");
+ pw.println(" Override the configuration for testing (will last until reboot)");
+ pw.println();
+ pw.println("cmd shortcut reset-config");
+ pw.println(" Reset the configuration set with \"update-config\"");
+ pw.println();
+ pw.println("cmd shortcut clear-default-launcher [--user USER_ID]");
+ 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();
+ }
+
+ private int handleResetThrottling() throws CommandException {
+ parseOptions(/* takeUser =*/ true);
+
+ resetThrottlingInner(mUserId);
+ return 0;
+ }
+
+ private void handleResetPackageThrottling() throws CommandException {
+ parseOptions(/* takeUser =*/ true);
+
+ final String packageName = getNextArgRequired();
+
+ synchronized (mLock) {
+ getPackageShortcutsLocked(packageName, mUserId).resetRateLimitingForCommandLine();
+ saveUserLocked(mUserId);
+ }
+ }
+
+ private void handleOverrideConfig() throws CommandException {
+ final String config = getNextArgRequired();
+
+ synchronized (mLock) {
+ if (!updateConfigurationLocked(config)) {
+ throw new CommandException("override-config failed. See logcat for details.");
+ }
+ }
+ }
+
+ private void handleResetConfig() {
+ synchronized (mLock) {
+ loadConfigurationLocked();
+ }
+ }
+
+ private void clearLauncher() {
+ synchronized (mLock) {
+ getUserShortcutsLocked(mUserId).setLauncherComponent(
+ ShortcutService.this, null);
+ }
+ }
+
+ private void showLauncher() {
+ synchronized (mLock) {
+ // This ensures to set the cached launcher. Package name doesn't matter.
+ hasShortcutHostPermissionInner("-", mUserId);
+
+ getOutPrintWriter().println("Launcher: "
+ + getUserShortcutsLocked(mUserId).getLauncherComponent());
+ }
+ }
+
+ private void handleClearDefaultLauncher() throws CommandException {
+ parseOptions(/* takeUser =*/ true);
+
+ clearLauncher();
+ }
+
+ private void handleGetDefaultLauncher() throws CommandException {
+ parseOptions(/* takeUser =*/ true);
+
+ showLauncher();
+ }
+
+ private void handleRefreshDefaultLauncher() throws CommandException {
+ parseOptions(/* takeUser =*/ true);
+
+ clearLauncher();
+ showLauncher();
+ }
+ }
+
+ // === Unit test support ===
+
+ // Injection point.
+ long injectCurrentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ // Injection point.
+ int injectBinderCallingUid() {
+ return getCallingUid();
+ }
+
+ final int getCallingUserId() {
+ return UserHandle.getUserId(injectBinderCallingUid());
+ }
+
+ File injectSystemDataPath() {
+ return Environment.getDataSystemDirectory();
+ }
+
+ File injectUserDataPath(@UserIdInt int userId) {
+ return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER);
+ }
+
+ @VisibleForTesting
+ boolean injectIsLowRamDevice() {
+ return ActivityManager.isLowRamDeviceStatic();
+ }
+
+ PackageManagerInternal injectPackageManagerInternal() {
+ return mPackageManagerInternal;
+ }
+
+ File getUserBitmapFilePath(@UserIdInt int userId) {
+ return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
+ }
+
+ @VisibleForTesting
+ SparseArray<UserShortcuts> getShortcutsForTest() {
+ return mUsers;
+ }
+
+ @VisibleForTesting
+ int getMaxDynamicShortcutsForTest() {
+ return mMaxDynamicShortcuts;
+ }
+
+ @VisibleForTesting
+ int getMaxDailyUpdatesForTest() {
+ return mMaxDailyUpdates;
+ }
+
+ @VisibleForTesting
+ long getResetIntervalForTest() {
+ return mResetInterval;
+ }
+
+ @VisibleForTesting
+ int getMaxIconDimensionForTest() {
+ return mMaxIconDimension;
+ }
+
+ @VisibleForTesting
+ CompressFormat getIconPersistFormatForTest() {
+ return mIconPersistFormat;
+ }
+
+ @VisibleForTesting
+ int getIconPersistQualityForTest() {
+ return mIconPersistQuality;
+ }
+
+ @VisibleForTesting
+ ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
+ synchronized (mLock) {
+ return getPackageShortcutsLocked(packageName, userId).findShortcutById(shortcutId);
+ }
+ }
+}
+
+/**
+ * Per-user information.
+ */
+class UserShortcuts {
+ private static final String TAG = ShortcutService.TAG;
+
+ @UserIdInt
+ final int mUserId;
+
+ private final ArrayMap<String, PackageShortcuts> mPackages = new ArrayMap<>();
+
+ private ComponentName mLauncherComponent;
+
+ public UserShortcuts(int userId) {
+ mUserId = userId;
+ }
+
+ public ArrayMap<String, PackageShortcuts> getPackages() {
+ return mPackages;
+ }
+
+ public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+ out.startTag(null, ShortcutService.TAG_USER);
+
+ ShortcutService.writeTagValue(out, ShortcutService.TAG_LAUNCHER,
+ mLauncherComponent);
+
+ for (int i = 0; i < mPackages.size(); i++) {
+ mPackages.valueAt(i).saveToXml(out);
+ }
+
+ out.endTag(null, ShortcutService.TAG_USER);
+ }
+
+ public static UserShortcuts loadFromXml(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+ final UserShortcuts ret = new UserShortcuts(userId);
+
+ 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();
+ switch (tag) {
+ case ShortcutService.TAG_LAUNCHER:
+ ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
+ parser, ShortcutService.ATTR_VALUE);
+ continue;
+ case ShortcutService.TAG_PACKAGE:
+ final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId);
+
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.getPackages().put(shortcuts.mPackageName, shortcuts);
+ continue;
+ }
+ throw ShortcutService.throwForInvalidTag(depth, tag);
+ }
+ return ret;
+ }
+
+ public ComponentName getLauncherComponent() {
+ return mLauncherComponent;
+ }
+
+ public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) {
+ if (Objects.equal(mLauncherComponent, launcherComponent)) {
+ return;
+ }
+ mLauncherComponent = launcherComponent;
+ s.scheduleSaveUser(mUserId);
+ }
+
+ public void resetThrottling() {
+ for (int i = mPackages.size() - 1; i >= 0; i--) {
+ mPackages.valueAt(i).resetThrottling();
+ }
+ }
+
+ public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.print(prefix);
+ pw.print("User: ");
+ pw.print(mUserId);
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("Default launcher: ");
+ pw.print(mLauncherComponent);
+ pw.println();
+
+ for (int i = 0; i < mPackages.size(); i++) {
+ mPackages.valueAt(i).dump(s, pw, prefix + " ");
+ }
+ }
+}
+
+/**
+ * All the information relevant to shortcuts from a single package (per-user).
+ */
+class PackageShortcuts {
+ private static final String TAG = ShortcutService.TAG;
+
+ @UserIdInt
+ final int mUserId;
+
+ @NonNull
+ final String mPackageName;
+
+ /**
+ * 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;
+
+ /**
+ * When {@link #mApiCallCount} was reset last time.
+ */
+ private long mLastResetTime;
+
+ PackageShortcuts(int userId, String packageName) {
+ mUserId = userId;
+ mPackageName = packageName;
+ }
+
+ @Nullable
+ public ShortcutInfo findShortcutById(String id) {
+ return mShortcuts.get(id);
+ }
+
+ private ShortcutInfo deleteShortcut(@NonNull ShortcutService s,
+ @NonNull String id) {
+ final ShortcutInfo shortcut = mShortcuts.remove(id);
+ if (shortcut != null) {
+ s.removeIcon(mUserId, shortcut);
+ shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
+ }
+ return shortcut;
+ }
+
+ void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
+ deleteShortcut(s, newShortcut.getId());
+ s.saveIconAndFixUpShortcut(mUserId, newShortcut);
+ mShortcuts.put(newShortcut.getId(), newShortcut);
+ }
+
+ /**
+ * Add a shortcut, or update one with the same ID, with taking over existing flags.
+ *
+ * It checks the max number of dynamic shortcuts.
+ */
+ public void updateShortcutWithCapping(@NonNull ShortcutService s,
+ @NonNull ShortcutInfo newShortcut) {
+ final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
+
+ int oldFlags = 0;
+ int newDynamicCount = mDynamicShortcutCount;
+
+ if (oldShortcut != null) {
+ oldFlags = oldShortcut.getFlags();
+ if (oldShortcut.isDynamic()) {
+ newDynamicCount--;
+ }
+ }
+ if (newShortcut.isDynamic()) {
+ newDynamicCount++;
+ }
+ // Make sure there's still room.
+ s.enforceMaxDynamicShortcuts(newDynamicCount);
+
+ // Okay, make it dynamic and add.
+ newShortcut.addFlags(oldFlags);
+
+ addShortcut(s, newShortcut);
+ mDynamicShortcutCount = newDynamicCount;
+ }
+
+ /**
+ * Remove all shortcuts that aren't pinned nor dynamic.
+ */
+ private void removeOrphans(@NonNull ShortcutService s) {
+ 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 (removeList == null) {
+ removeList = new ArrayList<>();
+ }
+ removeList.add(si.getId());
+ }
+ if (removeList != null) {
+ for (int i = removeList.size() - 1 ; i >= 0; i--) {
+ deleteShortcut(s, removeList.get(i));
+ }
+ }
+ }
+
+ public void deleteAllDynamicShortcuts(@NonNull ShortcutService s) {
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ }
+ removeOrphans(s);
+ mDynamicShortcutCount = 0;
+ }
+
+ public void deleteDynamicWithId(@NonNull ShortcutService s, @NonNull String shortcutId) {
+ final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
+
+ if (oldShortcut == null) {
+ return;
+ }
+ if (oldShortcut.isDynamic()) {
+ mDynamicShortcutCount--;
+ }
+ if (oldShortcut.isPinned()) {
+ oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ } else {
+ deleteShortcut(s, shortcutId);
+ }
+ }
+
+ public void replacePinned(@NonNull ShortcutService s, String launcherPackage,
+ List<String> shortcutIds) {
+
+ // TODO Should be per launcherPackage.
+
+ // First, un-pin all shortcuts
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_PINNED);
+ }
+
+ // Then pin ALL
+ for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+ final ShortcutInfo shortcut = mShortcuts.get(shortcutIds.get(i));
+ if (shortcut != null) {
+ shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
+ }
+ }
+
+ removeOrphans(s);
+ }
+
+ /**
+ * Number of calls that the caller has made, since the last reset.
+ */
+ public int getApiCallCount(@NonNull ShortcutService s) {
+ final long last = s.getLastResetTimeLocked();
+
+ final long now = s.injectCurrentTimeMillis();
+ if (ShortcutService.isClockValid(now) && mLastResetTime > now) {
+ // Clock rewound. // TODO Test it
+ mLastResetTime = now;
+ }
+
+ // If not reset yet, then reset.
+ if (mLastResetTime < last) {
+ mApiCallCount = 0;
+ mLastResetTime = last;
+ }
+ return mApiCallCount;
+ }
+
+ /**
+ * If the caller app hasn't been throttled yet, increment {@link #mApiCallCount}
+ * and return true. Otherwise just return false.
+ */
+ public boolean tryApiCall(@NonNull ShortcutService s) {
+ if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
+ return false;
+ }
+ mApiCallCount++;
+ return true;
+ }
+
+ public void resetRateLimitingForCommandLine() {
+ mApiCallCount = 0;
+ mLastResetTime = 0;
+ }
+
+ /**
+ * Find all shortcuts that match {@code query}.
+ */
+ public void findAll(@NonNull List<ShortcutInfo> result,
+ @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
+ for (int i = 0; i < mShortcuts.size(); i++) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (query == null || query.test(si)) {
+ result.add(si.clone(cloneFlag));
+ }
+ }
+ }
+
+ public void resetThrottling() {
+ mApiCallCount = 0;
+ }
+
+ public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.print(prefix);
+ pw.print("Package: ");
+ pw.print(mPackageName);
+ pw.println();
+
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("Calls: ");
+ pw.print(getApiCallCount(s));
+ pw.println();
+
+ // This should be after getApiCallCount(), which may update it.
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("Last reset: [");
+ pw.print(mLastResetTime);
+ pw.print("] ");
+ pw.print(s.formatTime(mLastResetTime));
+ pw.println();
+
+ pw.println(" Shortcuts:");
+ 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);
+ pw.print(" ");
+ pw.println(si.toInsecureString());
+ if (si.getBitmapPath() != null) {
+ final long len = new File(si.getBitmapPath()).length();
+ pw.print(" ");
+ pw.print("bitmap size=");
+ pw.println(len);
+
+ totalBitmapSize += len;
+ }
+ }
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print("Total bitmap size: ");
+ pw.print(totalBitmapSize);
+ pw.print(" (");
+ pw.print(Formatter.formatFileSize(s.mContext, totalBitmapSize));
+ pw.println(")");
+ }
+
+ public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException {
+ out.startTag(null, ShortcutService.TAG_PACKAGE);
+
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_NAME, mPackageName);
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_CALL_COUNT, mApiCallCount);
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_LAST_RESET, mLastResetTime);
+
+ final int size = mShortcuts.size();
+ for (int j = 0; j < size; j++) {
+ saveShortcut(out, mShortcuts.valueAt(j));
+ }
+
+ out.endTag(null, ShortcutService.TAG_PACKAGE);
+ }
+
+ private static void saveShortcut(XmlSerializer out, ShortcutInfo si)
+ throws IOException, XmlPullParserException {
+ out.startTag(null, ShortcutService.TAG_SHORTCUT);
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_ID, si.getId());
+ // writeAttr(out, "package", si.getPackageName()); // not needed
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_ACTIVITY, si.getActivityComponent());
+ // writeAttr(out, "icon", si.getIcon()); // We don't save it.
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_TITLE, si.getTitle());
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_INTENT, si.getIntentNoExtras());
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_WEIGHT, si.getWeight());
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_TIMESTAMP,
+ si.getLastChangedTimestamp());
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_FLAGS, si.getFlags());
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_ICON_RES, si.getIconResourceId());
+ ShortcutService.writeAttr(out, ShortcutService.ATTR_BITMAP_PATH, si.getBitmapPath());
+
+ ShortcutService.writeTagExtra(out, ShortcutService.TAG_INTENT_EXTRAS,
+ si.getIntentPersistableExtras());
+ ShortcutService.writeTagExtra(out, ShortcutService.TAG_EXTRAS, si.getExtras());
+
+ out.endTag(null, ShortcutService.TAG_SHORTCUT);
+ }
+
+ public static PackageShortcuts loadFromXml(XmlPullParser parser, int userId)
+ throws IOException, XmlPullParserException {
+
+ final String packageName = ShortcutService.parseStringAttribute(parser,
+ ShortcutService.ATTR_NAME);
+
+ final PackageShortcuts ret = new PackageShortcuts(userId, packageName);
+
+ ret.mDynamicShortcutCount =
+ ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_DYNAMIC_COUNT);
+ ret.mApiCallCount =
+ ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_CALL_COUNT);
+ ret.mLastResetTime =
+ ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_LAST_RESET);
+
+ 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();
+ switch (tag) {
+ case ShortcutService.TAG_SHORTCUT:
+ final ShortcutInfo si = parseShortcut(parser, packageName);
+
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.mShortcuts.put(si.getId(), si);
+ continue;
+ }
+ throw ShortcutService.throwForInvalidTag(depth, tag);
+ }
+ return ret;
+ }
+
+ private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName)
+ throws IOException, XmlPullParserException {
+ String id;
+ ComponentName activityComponent;
+ // Icon icon;
+ String title;
+ Intent intent;
+ PersistableBundle intentPersistableExtras = null;
+ int weight;
+ PersistableBundle extras = null;
+ long lastChangedTimestamp;
+ int flags;
+ int iconRes;
+ String bitmapPath;
+
+ id = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_ID);
+ activityComponent = ShortcutService.parseComponentNameAttribute(parser,
+ ShortcutService.ATTR_ACTIVITY);
+ title = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_TITLE);
+ intent = ShortcutService.parseIntentAttribute(parser, ShortcutService.ATTR_INTENT);
+ weight = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_WEIGHT);
+ lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
+ ShortcutService.ATTR_TIMESTAMP);
+ flags = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_FLAGS);
+ iconRes = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_ICON_RES);
+ bitmapPath = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_BITMAP_PATH);
+
+ 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 ShortcutService.TAG_INTENT_EXTRAS:
+ intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
+ continue;
+ case ShortcutService.TAG_EXTRAS:
+ extras = PersistableBundle.restoreFromXml(parser);
+ continue;
+ }
+ throw ShortcutService.throwForInvalidTag(depth, tag);
+ }
+ return new ShortcutInfo(
+ id, packageName, activityComponent, /* icon =*/ null, title, intent,
+ intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
+ iconRes, bitmapPath);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5490260..8206bda 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -50,6 +50,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.SELinux;
import android.os.ServiceManager;
import android.os.ShellCommand;
import android.os.UserHandle;
@@ -758,23 +759,28 @@
}
@Override
- public ParcelFileDescriptor getUserIcon(int userId) {
+ public ParcelFileDescriptor getUserIcon(int targetUserId) {
String iconPath;
synchronized (mPackagesLock) {
- UserInfo info = getUserInfoNoChecks(userId);
- if (info == null || info.partial) {
- Slog.w(LOG_TAG, "getUserIcon: unknown user #" + userId);
+ UserInfo targetUserInfo = getUserInfoNoChecks(targetUserId);
+ if (targetUserInfo == null || targetUserInfo.partial) {
+ Slog.w(LOG_TAG, "getUserIcon: unknown user #" + targetUserId);
return null;
}
- int callingGroupId = getUserInfoNoChecks(UserHandle.getCallingUserId()).profileGroupId;
- if (callingGroupId == UserInfo.NO_PROFILE_GROUP_ID
- || callingGroupId != info.profileGroupId) {
+
+ final int callingUserId = UserHandle.getCallingUserId();
+ final int callingGroupId = getUserInfoNoChecks(callingUserId).profileGroupId;
+ final int targetGroupId = targetUserInfo.profileGroupId;
+ final boolean sameGroup = (callingGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ && callingGroupId == targetGroupId);
+ if ((callingUserId != targetUserId) && !sameGroup) {
checkManageUsersPermission("get the icon of a user who is not related");
}
- if (info.iconPath == null) {
+
+ if (targetUserInfo.iconPath == null) {
return null;
}
- iconPath = info.iconPath;
+ iconPath = targetUserInfo.iconPath;
}
try {
@@ -1229,7 +1235,7 @@
}
FileOutputStream os;
if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(tmp))
- && tmp.renameTo(file)) {
+ && tmp.renameTo(file) && SELinux.restorecon(file)) {
info.iconPath = file.getAbsolutePath();
}
try {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6320413..0115a08 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -28,6 +28,8 @@
import static android.view.WindowManager.DOCKED_TOP;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
+import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION;
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
@@ -57,6 +59,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.CompatibilityInfo;
@@ -76,6 +79,7 @@
import android.media.RingtoneManager;
import android.media.session.MediaSessionLegacyHelper;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.FactoryTest;
@@ -187,6 +191,9 @@
static final int LONG_PRESS_POWER_SHUT_OFF = 2;
static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3;
+ static final int LONG_PRESS_BACK_NOTHING = 0;
+ static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1;
+
static final int MULTI_PRESS_POWER_NOTHING = 0;
static final int MULTI_PRESS_POWER_THEATER_MODE = 1;
static final int MULTI_PRESS_POWER_BRIGHTNESS_BOOST = 2;
@@ -207,6 +214,13 @@
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 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.
+ static final int NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED = 0;
+ // Nav bar is always translucent when the freeform stack is visible, otherwise always opaque.
+ static final int NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE = 1;
+
static final int APPLICATION_MEDIA_SUBLAYER = -2;
static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
static final int APPLICATION_PANEL_SUBLAYER = 1;
@@ -240,6 +254,12 @@
// app shows again. If that doesn't happen for 30s we drop the gesture.
private static final long PANIC_GESTURE_EXPIRATION = 30000;
+ private static final String SYSUI_PACKAGE = "com.android.systemui";
+ private static final String SYSUI_SCREENSHOT_SERVICE =
+ "com.android.systemui.screenshot.TakeScreenshotService";
+ private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
+ "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
+
/**
* Keyguard stuff
*/
@@ -374,6 +394,7 @@
// handler thread. We'll need to resolve this someday by teaching the input dispatcher
// to hold wakelocks during dispatch and eliminating the critical path.
volatile boolean mPowerKeyHandled;
+ volatile boolean mBackKeyHandled;
volatile boolean mBeganFromNonInteractive;
volatile int mPowerKeyPressCounter;
volatile boolean mEndCallKeyHandled;
@@ -426,6 +447,7 @@
int mLongPressOnPowerBehavior;
int mDoublePressOnPowerBehavior;
int mTriplePressOnPowerBehavior;
+ int mLongPressOnBackBehavior;
int mShortPressOnSleepBehavior;
int mShortPressWindowBehavior;
boolean mAwake;
@@ -535,6 +557,7 @@
boolean mForceStatusBar;
boolean mForceStatusBarFromKeyguard;
private boolean mForceStatusBarTransparent;
+ int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
boolean mHideLockScreen;
boolean mForcingShowNavBar;
int mForcingShowNavBarLayer;
@@ -681,6 +704,7 @@
private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15;
private static final int MSG_REQUEST_TRANSIENT_BARS = 16;
private static final int MSG_REQUEST_TV_PICTURE_IN_PICTURE = 17;
+ private static final int MSG_BACK_LONG_PRESS = 18;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -745,6 +769,9 @@
case MSG_REQUEST_TV_PICTURE_IN_PICTURE:
requestTvPictureInPictureInternal();
break;
+ case MSG_BACK_LONG_PRESS:
+ backLongPress();
+ break;
}
}
}
@@ -1091,6 +1118,13 @@
}
}
+ private void cancelPendingBackKeyAction() {
+ if (!mBackKeyHandled) {
+ mBackKeyHandled = true;
+ mHandler.removeMessages(MSG_BACK_LONG_PRESS);
+ }
+ }
+
private void powerPress(long eventTime, boolean interactive, int count) {
if (mScreenOnEarly && !mScreenOnFully) {
Slog.i(TAG, "Suppressed redundant power key press while "
@@ -1198,6 +1232,19 @@
}
}
+ private void backLongPress() {
+ mBackKeyHandled = true;
+
+ switch (mLongPressOnBackBehavior) {
+ case LONG_PRESS_BACK_NOTHING:
+ break;
+ case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST:
+ Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ break;
+ }
+ }
+
private void sleepPress(long eventTime) {
if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
@@ -1226,6 +1273,10 @@
return getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;
}
+ private boolean hasLongPressOnBackBehavior() {
+ return mLongPressOnBackBehavior != LONG_PRESS_BACK_NOTHING;
+ }
+
private void interceptScreenshotChord() {
if (mScreenshotChordEnabled
&& mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
@@ -1236,7 +1287,7 @@
+ SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
mScreenshotChordVolumeDownKeyConsumed = true;
cancelPendingPowerKeyAction();
-
+ mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
}
}
@@ -1266,12 +1317,20 @@
}
};
- private final Runnable mScreenshotRunnable = new Runnable() {
+ private class ScreenshotRunnable implements Runnable {
+ private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
+
+ public void setScreenshotType(int screenshotType) {
+ mScreenshotType = screenshotType;
+ }
+
@Override
public void run() {
- takeScreenshot();
+ takeScreenshot(mScreenshotType);
}
- };
+ }
+
+ private final ScreenshotRunnable mScreenshotRunnable = new ScreenshotRunnable();
@Override
public void showGlobalActions() {
@@ -1547,6 +1606,9 @@
mSupportLongPressPowerWhenNonInteractive = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_supportLongPressPowerWhenNonInteractive);
+ mLongPressOnBackBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnBackBehavior);
+
mShortPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnPowerBehavior);
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
@@ -1715,6 +1777,9 @@
if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
mShortPressWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
}
+
+ mNavBarOpacityMode = res.getInteger(
+ com.android.internal.R.integer.config_navBarOpacityMode);
}
@Override
@@ -2053,7 +2118,7 @@
// check if user has enabled this operation. SecurityException will be thrown if
// this app has not been allowed by the user
- final int mode = mAppOpsManager.checkOp(outAppOp[0], callingUid,
+ final int mode = mAppOpsManager.checkOpNoThrow(outAppOp[0], callingUid,
attrs.packageName);
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
@@ -2062,6 +2127,17 @@
// actually be hidden in WindowManagerService
return WindowManagerGlobal.ADD_OKAY;
case AppOpsManager.MODE_ERRORED:
+ try {
+ ApplicationInfo appInfo = mContext.getPackageManager()
+ .getApplicationInfo(attrs.packageName,
+ UserHandle.getUserId(callingUid));
+ // Don't crash legacy apps
+ if (appInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ return WindowManagerGlobal.ADD_OKAY;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ /* ignore */
+ }
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
default:
// in the default mode, we will make a decision here based on
@@ -2287,29 +2363,33 @@
case TYPE_NAVIGATION_BAR_PANEL:
// some panels (e.g. search) need to show on top of the navigation bar
return 22;
+ case TYPE_SCREENSHOT:
+ // screenshot selection layer shouldn't go above system error, but it should cover
+ // navigation bars at the very least.
+ return 23;
case TYPE_SYSTEM_ERROR:
// system-level error dialogs
- return 23;
+ return 24;
case TYPE_MAGNIFICATION_OVERLAY:
// used to highlight the magnified portion of a display
- return 24;
+ return 25;
case TYPE_DISPLAY_OVERLAY:
// used to simulate secondary display devices
- return 25;
+ return 26;
case TYPE_DRAG:
// the drag layer: input for drag-and-drop is associated with this window,
// which sits above all other focusable windows
- return 26;
+ return 27;
case TYPE_ACCESSIBILITY_OVERLAY:
// overlay put by accessibility services to intercept user interaction
- return 27;
- case TYPE_SECURE_SYSTEM_OVERLAY:
return 28;
- case TYPE_BOOT_PROGRESS:
+ case TYPE_SECURE_SYSTEM_OVERLAY:
return 29;
+ case TYPE_BOOT_PROGRESS:
+ return 30;
case TYPE_POINTER:
// the (mouse) pointer layer
- return 30;
+ return 31;
}
Log.e(TAG, "Unknown window type: " + type);
return 2;
@@ -3009,6 +3089,15 @@
}
}
}
+ } else if (keyCode == KeyEvent.KEYCODE_S && event.isMetaPressed()
+ && event.isCtrlPressed()) {
+ if (down && repeatCount == 0) {
+ int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION
+ : TAKE_SCREENSHOT_FULLSCREEN;
+ mScreenshotRunnable.setScreenshotType(type);
+ mHandler.post(mScreenshotRunnable);
+ return -1;
+ }
} else if (keyCode == KeyEvent.KEYCODE_SLASH && event.isMetaPressed()) {
if (down) {
if (repeatCount == 0) {
@@ -3056,6 +3145,7 @@
}
} else if (keyCode == KeyEvent.KEYCODE_SYSRQ) {
if (down && repeatCount == 0) {
+ mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
mHandler.post(mScreenshotRunnable);
}
return -1;
@@ -3343,8 +3433,8 @@
throws RemoteException {
synchronized (mLock) {
IShortcutService service = mShortcutKeyServices.get(shortcutCode);
- if (service != null && service.asBinder().isBinderAlive()) {
- throw new RemoteException("Key already exists.");
+ if (service != null && service.asBinder().pingBinder()) {
+ throw new RemoteException("Key already exists.");
}
mShortcutKeyServices.put(shortcutCode, shortcutService);
@@ -4385,9 +4475,11 @@
"Laying out navigation bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
} else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
- || attrs.type == TYPE_BOOT_PROGRESS)
+ || attrs.type == TYPE_BOOT_PROGRESS
+ || attrs.type == TYPE_SCREENSHOT)
&& ((fl & FLAG_FULLSCREEN) != 0)) {
- // Fullscreen secure system overlays get what they ask for.
+ // Fullscreen secure system overlays get what they ask for. Screenshot region
+ // selection overlay should also expand to full screen.
pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
@@ -4543,7 +4635,9 @@
}
// TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
- if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR) {
+ // Also, we don't allow windows in multi-window mode to extend out of the screen.
+ if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR
+ && !win.inMultiWindowMode()) {
df.left = df.top = -10000;
df.right = df.bottom = 10000;
if (attrs.type != TYPE_WALLPAPER) {
@@ -5136,21 +5230,22 @@
if (mScreenshotConnection != null) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
+ notifyScreenshotError();
}
}
}
};
// Assume this is called from the Handler thread.
- private void takeScreenshot() {
+ private void takeScreenshot(final int screenshotType) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
}
- ComponentName cn = new ComponentName("com.android.systemui",
- "com.android.systemui.screenshot.TakeScreenshotService");
- Intent intent = new Intent();
- intent.setComponent(cn);
+ final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
+ SYSUI_SCREENSHOT_SERVICE);
+ final Intent serviceIntent = new Intent();
+ serviceIntent.setComponent(serviceComponent);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -5159,7 +5254,7 @@
return;
}
Messenger messenger = new Messenger(service);
- Message msg = Message.obtain(null, 1);
+ Message msg = Message.obtain(null, screenshotType);
final ServiceConnection myConn = this;
Handler h = new Handler(mHandler.getLooper()) {
@Override
@@ -5185,17 +5280,35 @@
}
}
}
+
@Override
- public void onServiceDisconnected(ComponentName name) {}
+ public void onServiceDisconnected(ComponentName name) {
+ notifyScreenshotError();
+ }
};
- if (mContext.bindServiceAsUser(
- intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
+ if (mContext.bindServiceAsUser(serviceIntent, conn,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+ UserHandle.CURRENT)) {
mScreenshotConnection = conn;
mHandler.postDelayed(mScreenshotTimeout, 10000);
}
}
}
+ /**
+ * Notifies the screenshot service to show an error.
+ */
+ private void notifyScreenshotError() {
+ // If the service process is killed, then ask it to clean up after itself
+ final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE,
+ SYSUI_SCREENSHOT_ERROR_RECEIVER);
+ Intent errorIntent = new Intent();
+ errorIntent.setComponent(errorComponent);
+ errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(errorIntent, UserHandle.ALL);
+ }
+
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
@@ -5266,6 +5379,29 @@
// Handle special keys.
switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK: {
+ if (down) {
+ mBackKeyHandled = false;
+ if (hasLongPressOnBackBehavior()) {
+ Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg,
+ ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
+ }
+ } else {
+ boolean handled = mBackKeyHandled;
+
+ // Reset back key state
+ cancelPendingBackKeyAction();
+
+ // Don't pass back press to app if we've already handled it
+ if (handled) {
+ result &= ~ACTION_PASS_TO_USER;
+ }
+ }
+ break;
+ }
+
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
@@ -7050,7 +7186,7 @@
// is visible but also when we are resizing for the transitions when docked stack
// visibility changes.
mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
- final boolean forceOpaqueSystemBars = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
+ final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
// apply translucent bar vis flags
WindowState transWin = isStatusBarKeyguard() && !mHideLockScreen
@@ -7075,11 +7211,12 @@
}
if ((!areTranslucentBarsAllowed() && transWin != mStatusBar)
- || forceOpaqueSystemBars) {
- vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT
- | View.SYSTEM_UI_TRANSPARENT);
+ || forceOpaqueStatusBar) {
+ vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
}
+ vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);
+
if (mForceWindowDrawsStatusBarBackground) {
vis |= View.STATUS_BAR_TRANSPARENT;
vis &= ~View.STATUS_BAR_TRANSLUCENT;
@@ -7152,6 +7289,41 @@
return vis;
}
+ /**
+ * @return the current visibility flags with the nav-bar opacity related flags toggled based
+ * on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
+ */
+ private int configureNavBarOpacity(int visibility, boolean dockedStackVisible,
+ boolean freeformStackVisible, boolean isDockedDividerResizing) {
+ if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
+ if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) {
+ visibility = setNavBarOpaqueFlag(visibility);
+ }
+ } else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
+ if (isDockedDividerResizing) {
+ visibility = setNavBarOpaqueFlag(visibility);
+ } else if (freeformStackVisible) {
+ visibility = setNavBarTranslucentFlag(visibility);
+ } else {
+ visibility = setNavBarOpaqueFlag(visibility);
+ }
+ }
+
+ if (!areTranslucentBarsAllowed()) {
+ visibility &= ~View.NAVIGATION_BAR_TRANSLUCENT;
+ }
+ return visibility;
+ }
+
+ private int setNavBarOpaqueFlag(int visibility) {
+ return visibility &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT);
+ }
+
+ private int setNavBarTranslucentFlag(int visibility) {
+ visibility &= ~View.NAVIGATION_BAR_TRANSPARENT;
+ return visibility |= View.NAVIGATION_BAR_TRANSLUCENT;
+ }
+
private void clearClearableFlagsLw() {
int newVal = mResettingSystemUiFlags | View.SYSTEM_UI_CLEARABLE_FLAGS;
if (newVal != mResettingSystemUiFlags) {
@@ -7279,6 +7451,8 @@
pw.print(" mLidControlsScreenLock="); pw.println(mLidControlsScreenLock);
pw.print(" mLidControlsSleep="); pw.println(mLidControlsSleep);
pw.print(prefix);
+ pw.print(" mLongPressOnBackBehavior="); pw.println(mLongPressOnBackBehavior);
+ pw.print(prefix);
pw.print("mShortPressOnPowerBehavior="); pw.print(mShortPressOnPowerBehavior);
pw.print(" mLongPressOnPowerBehavior="); pw.println(mLongPressOnPowerBehavior);
pw.print(prefix);
diff --git a/services/core/java/com/android/server/policy/ShortcutManager.java b/services/core/java/com/android/server/policy/ShortcutManager.java
index 9284442..57ae523 100644
--- a/services/core/java/com/android/server/policy/ShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ShortcutManager.java
@@ -138,14 +138,16 @@
ComponentName componentName = new ComponentName(packageName, className);
try {
info = packageManager.getActivityInfo(componentName,
- PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e) {
String[] packages = packageManager.canonicalToCurrentPackageNames(
new String[] { packageName });
componentName = new ComponentName(packages[0], className);
try {
info = packageManager.getActivityInfo(componentName,
- PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+ PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e1) {
Log.w(TAG, "Unable to add bookmark: " + packageName
+ "/" + className, e);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6218c4e..91d8671 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -50,6 +50,7 @@
import android.os.WorkSource;
import android.provider.Settings;
import android.provider.Settings.Secure;
+import android.provider.Settings.SettingNotFoundException;
import android.service.dreams.DreamManagerInternal;
import android.util.EventLog;
import android.util.Slog;
@@ -57,6 +58,7 @@
import android.util.TimeUtils;
import android.view.Display;
import android.view.WindowManagerPolicy;
+
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
@@ -537,6 +539,8 @@
}
}
mBootCompletedRunnables = null;
+
+ incrementBootCount();
}
}
}
@@ -772,7 +776,7 @@
}
}
- void updateLowPowerModeLocked() {
+ private void updateLowPowerModeLocked() {
if (mIsPowered && mLowPowerModeSetting) {
if (DEBUG_SPEW) {
Slog.d(TAG, "updateLowPowerModeLocked: powered, turning setting off");
@@ -2912,6 +2916,20 @@
return suspendBlocker;
}
+ private void incrementBootCount() {
+ synchronized (mLock) {
+ int count;
+ try {
+ count = Settings.Global.getInt(
+ getContext().getContentResolver(), Settings.Global.BOOT_COUNT);
+ } catch (SettingNotFoundException e) {
+ count = 0;
+ }
+ Settings.Global.putInt(
+ getContext().getContentResolver(), Settings.Global.BOOT_COUNT, count + 1);
+ }
+ }
+
private static WorkSource copyWorkSource(WorkSource workSource) {
return workSource != null ? new WorkSource(workSource) : null;
}
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index 0046fbb..6bacdfd 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -410,7 +410,7 @@
activities =
mPm.queryIntentActivities(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags, mUserId);
+ flags, mUserId).getList();
} catch (RemoteException re) {
// Local call
}
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index e5c5b2bc..858f7c7 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -81,6 +81,7 @@
private boolean mBound;
private long mScheduledRestartUptimeMillis;
private long mMaximumTimeToLock; // from DevicePolicyManager
+ private boolean mPendingSuccessfulUnlock = false;
// Trust state
private boolean mTrusted;
@@ -234,6 +235,11 @@
setCallback(mCallback);
updateDevicePolicyFeatures();
+ if (mPendingSuccessfulUnlock) {
+ onUnlockAttempt(true);
+ mPendingSuccessfulUnlock = false;
+ }
+
if (mTrustManagerService.isDeviceLockedInner(mUserId)) {
onDeviceLocked();
} else {
@@ -302,7 +308,11 @@
*/
public void onUnlockAttempt(boolean successful) {
try {
- if (mTrustAgentService != null) mTrustAgentService.onUnlockAttempt(successful);
+ if (mTrustAgentService != null) {
+ mTrustAgentService.onUnlockAttempt(successful);
+ } else {
+ mPendingSuccessfulUnlock = successful;
+ }
} catch (RemoteException e) {
onError(e);
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index b54e866..984fb76 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -19,7 +19,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
import com.android.server.SystemService;
import org.xmlpull.v1.XmlPullParser;
@@ -104,7 +103,7 @@
private static final int MSG_SET_DEVICE_LOCKED = 10;
private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 11;
- public static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
+ private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>();
private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
@@ -136,13 +135,7 @@
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mLockPatternUtils = new LockPatternUtils(context);
-
- mStrongAuthTracker = new StrongAuthTracker(context) {
- @Override
- public void onStrongAuthRequiredChanged(int userId) {
- refreshAgentList(userId);
- }
- };
+ mStrongAuthTracker = new StrongAuthTracker(context);
}
@Override
@@ -231,24 +224,24 @@
TRUST_USUALLY_MANAGED_FLUSH_DELAY);
}
- void refreshAgentList(int userId) {
- if (DEBUG) Slog.d(TAG, "refreshAgentList()");
+ void refreshAgentList(int userIdOrAll) {
+ if (DEBUG) Slog.d(TAG, "refreshAgentList(" + userIdOrAll + ")");
if (!mTrustAgentsCanRun) {
return;
}
- if (userId != UserHandle.USER_ALL && userId < UserHandle.USER_SYSTEM) {
- Log.e(TAG, "refreshAgentList(userId=" + userId + "): Invalid user handle,"
+ if (userIdOrAll != UserHandle.USER_ALL && userIdOrAll < UserHandle.USER_SYSTEM) {
+ Log.e(TAG, "refreshAgentList(userId=" + userIdOrAll + "): Invalid user handle,"
+ " must be USER_ALL or a specific user.", new Throwable("here"));
- userId = UserHandle.USER_ALL;
+ userIdOrAll = UserHandle.USER_ALL;
}
PackageManager pm = mContext.getPackageManager();
List<UserInfo> userInfos;
- if (userId == UserHandle.USER_ALL) {
+ if (userIdOrAll == UserHandle.USER_ALL) {
userInfos = mUserManager.getUsers(true /* excludeDying */);
} else {
userInfos = new ArrayList<>();
- userInfos.add(mUserManager.getUserInfo(userId));
+ userInfos.add(mUserManager.getUserInfo(userIdOrAll));
}
LockPatternUtils lockPatternUtils = mLockPatternUtils;
@@ -261,7 +254,7 @@
if (!userInfo.supportsSwitchToByUser()) continue;
if (!mActivityManager.isUserRunning(userInfo.id)) continue;
if (!lockPatternUtils.isSecure(userInfo.id)) continue;
- if (!mStrongAuthTracker.isTrustAllowedForUser(userInfo.id)) continue;
+ if (!mStrongAuthTracker.canAgentsRunForUser(userInfo.id)) continue;
DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager();
int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id);
final boolean disableTrustAgents =
@@ -302,7 +295,7 @@
boolean trustMayHaveChanged = false;
for (int i = 0; i < obsoleteAgents.size(); i++) {
AgentInfo info = obsoleteAgents.valueAt(i);
- if (userId == UserHandle.USER_ALL || userId == info.userId) {
+ if (userIdOrAll == UserHandle.USER_ALL || userIdOrAll == info.userId) {
if (info.agent.isManagingTrust()) {
trustMayHaveChanged = true;
}
@@ -312,10 +305,10 @@
}
if (trustMayHaveChanged) {
- if (userId == UserHandle.USER_ALL) {
+ if (userIdOrAll == UserHandle.USER_ALL) {
updateTrustAll();
} else {
- updateTrust(userId, 0);
+ updateTrust(userIdOrAll, 0);
}
}
}
@@ -578,6 +571,10 @@
}
private void dispatchUnlockAttempt(boolean successful, int userId) {
+ if (successful) {
+ mStrongAuthTracker.allowTrustFromUnlock(userId);
+ }
+
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.userId == userId) {
@@ -608,6 +605,10 @@
}
private void dispatchOnTrustChanged(boolean enabled, int userId, int flags) {
+ if (DEBUG) {
+ Log.i(TAG, "onTrustChanged(" + enabled + ", " + userId + ", 0x"
+ + Integer.toHexString(flags) + ")");
+ }
if (!enabled) flags = 0;
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
@@ -623,6 +624,9 @@
}
private void dispatchOnTrustManagedChanged(boolean managed, int userId) {
+ if (DEBUG) {
+ Log.i(TAG, "onTrustManagedChanged(" + managed + ", " + userId + ")");
+ }
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
mTrustListeners.get(i).onTrustManagedChanged(managed, userId);
@@ -980,4 +984,61 @@
null /* scheduler */);
}
}
+
+ private class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+
+ SparseBooleanArray mStartFromSuccessfulUnlock = new SparseBooleanArray();
+
+ public StrongAuthTracker(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStrongAuthRequiredChanged(int userId) {
+ mStartFromSuccessfulUnlock.delete(userId);
+
+ if (DEBUG) {
+ Log.i(TAG, "onStrongAuthRequiredChanged(" + userId + ") ->"
+ + " trustAllowed=" + isTrustAllowedForUser(userId)
+ + " agentsCanRun=" + canAgentsRunForUser(userId));
+ }
+
+ refreshAgentList(userId);
+
+ // The list of active trust agents may not have changed, if there was a previous call
+ // to allowTrustFromUnlock, so we update the trust here too.
+ updateTrust(userId, 0 /* flags */);
+ }
+
+ boolean canAgentsRunForUser(int userId) {
+ return mStartFromSuccessfulUnlock.get(userId)
+ || super.isTrustAllowedForUser(userId);
+ }
+
+ /**
+ * Temporarily suppress strong auth requirements for {@param userId} until strong auth
+ * changes again. Must only be called when we know about a successful unlock already
+ * before the underlying StrongAuthTracker.
+ *
+ * Note that this only changes whether trust agents can be started, not the actual trusted
+ * value.
+ */
+ void allowTrustFromUnlock(int userId) {
+ if (userId < UserHandle.USER_SYSTEM) {
+ throw new IllegalArgumentException("userId must be a valid user: " + userId);
+ }
+ boolean previous = canAgentsRunForUser(userId);
+ mStartFromSuccessfulUnlock.put(userId, true);
+
+ if (DEBUG) {
+ Log.i(TAG, "allowTrustFromUnlock(" + userId + ") ->"
+ + " trustAllowed=" + isTrustAllowedForUser(userId)
+ + " agentsCanRun=" + canAgentsRunForUser(userId));
+ }
+
+ if (canAgentsRunForUser(userId) != previous) {
+ refreshAgentList(userId);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 49aaa4a..30442bc 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1292,6 +1292,7 @@
@Override
public void unblockContent(
IBinder sessionToken, String unblockedRating, int userId) {
+ ensureParentalControlsPermission();
final int callingUid = Binder.getCallingUid();
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
userId, "unblockContent");
diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java
new file mode 100644
index 0000000..a645701
--- /dev/null
+++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java
@@ -0,0 +1,220 @@
+/**
+ * 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.utils;
+
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/**
+ * Manages the lifecycle of an application-provided service bound from system server.
+ *
+ * @hide
+ */
+public class ManagedApplicationService {
+ private final String TAG = getClass().getSimpleName();
+
+ private final Context mContext;
+ private final int mUserId;
+ private final ComponentName mComponent;
+ private final int mClientLabel;
+ private final String mSettingsAction;
+ private final BinderChecker mChecker;
+
+ private final DeathRecipient mDeathRecipient = new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mBoundInterface = null;
+ }
+ }
+ };
+
+ private final Object mLock = new Object();
+
+ // State protected by mLock
+ private ServiceConnection mPendingConnection;
+ private ServiceConnection mConnection;
+ private IInterface mBoundInterface;
+
+
+ private ManagedApplicationService(final Context context, final ComponentName component,
+ final int userId, int clientLabel, String settingsAction,
+ BinderChecker binderChecker) {
+ mContext = context;
+ mComponent = component;
+ mUserId = userId;
+ mClientLabel = clientLabel;
+ mSettingsAction = settingsAction;
+ mChecker = binderChecker;
+ }
+
+ /**
+ * Implement to validate returned IBinder instance.
+ */
+ public interface BinderChecker {
+ IInterface asInterface(IBinder binder);
+ boolean checkType(IInterface service);
+ }
+
+ /**
+ * Create a new ManagedApplicationService object but do not yet bind to the user service.
+ *
+ * @param context a Context to use for binding the application service.
+ * @param component the {@link ComponentName} of the application service to bind.
+ * @param userId the user ID of user to bind the application service as.
+ * @param clientLabel the resource ID of a label displayed to the user indicating the
+ * binding service.
+ * @param settingsAction an action that can be used to open the Settings UI to enable/disable
+ * binding to these services.
+ * @param binderChecker an interface used to validate the returned binder object.
+ * @return a ManagedApplicationService instance.
+ */
+ public static ManagedApplicationService build(@NonNull final Context context,
+ @NonNull final ComponentName component, final int userId, @NonNull int clientLabel,
+ @NonNull String settingsAction, @NonNull BinderChecker binderChecker) {
+ return new ManagedApplicationService(context, component, userId, clientLabel,
+ settingsAction, binderChecker);
+ }
+
+ /**
+ * @return the user ID of the user that owns the bound service.
+ */
+ public int getUserId() {
+ return mUserId;
+ }
+
+ /**
+ * @return the component of the bound service.
+ */
+ public ComponentName getComponent() {
+ return mComponent;
+ }
+
+ /**
+ * Asynchronously unbind from the application service if the bound service component and user
+ * does not match the given signature.
+ *
+ * @param componentName the component that must match.
+ * @param userId the user ID that must match.
+ * @return {@code true} if not matching.
+ */
+ public boolean disconnectIfNotMatching(final ComponentName componentName, final int userId) {
+ if (matches(componentName, userId)) {
+ return false;
+ }
+ disconnect();
+ return true;
+ }
+
+ /**
+ * Asynchronously unbind from the application service if bound.
+ */
+ public void disconnect() {
+ synchronized (mLock) {
+ // Wipe out pending connections
+ mPendingConnection = null;
+
+ // Unbind existing connection, if it exists
+ if (mConnection != null) {
+ mContext.unbindService(mConnection);
+ mConnection = null;
+ }
+
+ mBoundInterface = null;
+ }
+ }
+
+ /**
+ * Asynchronously bind to the application service if not bound.
+ */
+ public void connect() {
+ synchronized (mLock) {
+ if (mConnection != null || mPendingConnection != null) {
+ // We're already connected or are trying to connect
+ return;
+ }
+
+ final PendingIntent pendingIntent = PendingIntent.getActivity(
+ mContext, 0, new Intent(mSettingsAction), 0);
+ final Intent intent = new Intent().setComponent(mComponent).
+ putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel).
+ putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
+
+ final ServiceConnection serviceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+ synchronized (mLock) {
+ if (mPendingConnection == this) {
+ // No longer pending, remove from pending connection
+ mPendingConnection = null;
+ mConnection = this;
+ } else {
+ // Service connection wasn't pending, must have been disconnected
+ mContext.unbindService(this);
+ }
+
+ try {
+ iBinder.linkToDeath(mDeathRecipient, 0);
+ mBoundInterface = mChecker.asInterface(iBinder);
+ if (!mChecker.checkType(mBoundInterface)) {
+ // Received an invalid binder, disconnect
+ mContext.unbindService(this);
+ mBoundInterface = null;
+ }
+ } catch (RemoteException e) {
+ // DOA
+ Slog.w(TAG, "Unable to bind service: " + intent, e);
+ mBoundInterface = null;
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName componentName) {
+ Slog.w(TAG, "Service disconnected: " + intent);
+ }
+ };
+
+ mPendingConnection = serviceConnection;
+
+ try {
+ if (!mContext.bindServiceAsUser(intent, serviceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ new UserHandle(mUserId))) {
+ Slog.w(TAG, "Unable to bind service: " + intent);
+ }
+ } catch (SecurityException e) {
+ Slog.w(TAG, "Unable to bind service: " + intent, e);
+ }
+ }
+ }
+
+ private boolean matches(final ComponentName component, final int userId) {
+ return Objects.equals(mComponent, component) && mUserId == userId;
+ }
+}
diff --git a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
new file mode 100644
index 0000000..1363fb9
--- /dev/null
+++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
@@ -0,0 +1,282 @@
+/**
+ * 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.vr;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.server.vr.SettingsObserver.SettingChangeListener;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Detects changes in packages, settings, and current users that may affect whether components
+ * implementing a given service can be run.
+ *
+ * @hide
+ */
+public class EnabledComponentsObserver implements SettingChangeListener {
+
+ private static final String TAG = EnabledComponentsObserver.class.getSimpleName();
+ private static final String ENABLED_SERVICES_SEPARATOR = ":";
+
+ public static final int NO_ERROR = 0;
+ public static final int DISABLED = -1;
+ public static final int NOT_INSTALLED = -2;
+
+ private final Object mLock;
+ private final Context mContext;
+ private final String mSettingName;
+ private final String mServiceName;
+ private final String mServicePermission;
+ private final SparseArray<ArraySet<ComponentName>> mInstalledSet = new SparseArray<>();
+ private final SparseArray<ArraySet<ComponentName>> mEnabledSet = new SparseArray<>();
+ private final Set<EnabledComponentChangeListener> mEnabledComponentListeners = new ArraySet<>();
+
+ /**
+ * Implement this to receive callbacks when relevant changes to the allowed components occur.
+ */
+ public interface EnabledComponentChangeListener {
+
+ /**
+ * Called when a change in the allowed components occurs.
+ */
+ void onEnabledComponentChanged();
+ }
+
+ private EnabledComponentsObserver(@NonNull Context context, @NonNull String settingName,
+ @NonNull String servicePermission, @NonNull String serviceName, @NonNull Object lock,
+ @NonNull Collection<EnabledComponentChangeListener> listeners) {
+ mLock = lock;
+ mContext = context;
+ mSettingName = settingName;
+ mServiceName = serviceName;
+ mServicePermission = servicePermission;
+ mEnabledComponentListeners.addAll(listeners);
+ }
+
+ /**
+ * Create a EnabledComponentObserver instance.
+ *
+ * @param context the context to query for changes.
+ * @param handler a handler to receive lifecycle events from system services on.
+ * @param settingName the name of a setting to monitor for a list of enabled components.
+ * @param looper a {@link Looper} to use for receiving package callbacks.
+ * @param servicePermission the permission required by the components to be bound.
+ * @param serviceName the intent action implemented by the tracked components.
+ * @param lock a lock object used to guard instance state in all callbacks and method calls.
+ * @return an EnableComponentObserver instance.
+ */
+ public static EnabledComponentsObserver build(@NonNull Context context,
+ @NonNull Handler handler, @NonNull String settingName, @NonNull Looper looper,
+ @NonNull String servicePermission, @NonNull String serviceName,
+ @NonNull final Object lock,
+ @NonNull Collection<EnabledComponentChangeListener> listeners) {
+
+ SettingsObserver s = SettingsObserver.build(context, handler, settingName);
+
+ final EnabledComponentsObserver o = new EnabledComponentsObserver(context, settingName,
+ servicePermission, serviceName, lock, listeners);
+
+ PackageMonitor packageMonitor = new PackageMonitor() {
+ @Override
+ public void onSomePackagesChanged() {
+ o.onPackagesChanged();
+
+ }
+
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ o.onPackagesChanged();
+
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ o.onPackagesChanged();
+
+ }
+
+ @Override
+ public boolean onHandleForceStop(Intent intent, String[] packages, int uid,
+ boolean doit) {
+ o.onPackagesChanged();
+
+ return super.onHandleForceStop(intent, packages, uid, doit);
+ }
+ };
+
+ packageMonitor.register(context, looper, UserHandle.ALL, true);
+
+ s.addListener(o);
+
+ return o;
+
+ }
+
+ public void onPackagesChanged() {
+ rebuildAll();
+ }
+
+ @Override
+ public void onSettingChanged() {
+ rebuildAll();
+ }
+
+ @Override
+ public void onSettingRestored(String prevValue, String newValue, int userId) {
+ rebuildAll();
+ }
+
+ public void onUsersChanged() {
+ rebuildAll();
+ }
+
+ /**
+ * Rebuild the sets of allowed components for each current user profile.
+ */
+ public void rebuildAll() {
+ synchronized (mLock) {
+ mInstalledSet.clear();
+ mEnabledSet.clear();
+ final int[] userIds = getCurrentProfileIds();
+ for (int i : userIds) {
+ ArraySet<ComponentName> implementingPackages = loadComponentNamesForUser(i);
+ ArraySet<ComponentName> packagesFromSettings =
+ loadComponentNamesFromSetting(mSettingName, i);
+ packagesFromSettings.retainAll(implementingPackages);
+
+ mInstalledSet.put(i, implementingPackages);
+ mEnabledSet.put(i, packagesFromSettings);
+
+ }
+ }
+ sendSettingChanged();
+ }
+
+ /**
+ * Check whether a given component is present and enabled for the given user.
+ *
+ * @param component the component to check.
+ * @param userId the user ID for the component to check.
+ * @return {@code true} if present and enabled.
+ */
+ public int isValid(ComponentName component, int userId) {
+ synchronized (mLock) {
+ ArraySet<ComponentName> installedComponents = mInstalledSet.get(userId);
+ if (installedComponents == null || !installedComponents.contains(component)) {
+ return NOT_INSTALLED;
+ }
+ ArraySet<ComponentName> validComponents = mEnabledSet.get(userId);
+ if (validComponents == null || !validComponents.contains(component)) {
+ return DISABLED;
+ }
+ return NO_ERROR;
+ }
+ }
+
+ private int[] getCurrentProfileIds() {
+ UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ if (userManager == null) {
+ return null;
+ }
+ int currentUserId = ActivityManager.getCurrentUser();
+ List<UserInfo> profiles = userManager.getProfiles(currentUserId);
+ if (profiles == null) {
+ return null;
+ }
+ final int s = profiles.size();
+ int[] userIds = new int[s];
+ int ctr = 0;
+ for (UserInfo info : profiles) {
+ userIds[ctr++] = info.id;
+ }
+ return userIds;
+ }
+
+ private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
+ ArraySet<ComponentName> installed = new ArraySet<>();
+ PackageManager pm = mContext.getPackageManager();
+ Intent queryIntent = new Intent(mServiceName);
+ List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
+ queryIntent,
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+ userId);
+ if (installedServices != null) {
+ for (int i = 0, count = installedServices.size(); i < count; i++) {
+ ResolveInfo resolveInfo = installedServices.get(i);
+ ServiceInfo info = resolveInfo.serviceInfo;
+
+ ComponentName component = new ComponentName(info.packageName, info.name);
+ if (!mServicePermission.equals(info.permission)) {
+ Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name
+ + ": it does not require the permission "
+ + mServicePermission);
+ continue;
+ }
+ installed.add(component);
+ }
+ }
+ return installed;
+ }
+
+ private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
+ int userId) {
+ final ContentResolver cr = mContext.getContentResolver();
+ String settingValue = Settings.Secure.getStringForUser(
+ cr,
+ settingName,
+ userId);
+ if (TextUtils.isEmpty(settingValue))
+ return new ArraySet<>();
+ String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR);
+ ArraySet<ComponentName> result = new ArraySet<>(restored.length);
+ for (int i = 0; i < restored.length; i++) {
+ ComponentName value = ComponentName.unflattenFromString(restored[i]);
+ if (null != value) {
+ result.add(value);
+ }
+ }
+ return result;
+ }
+
+ private void sendSettingChanged() {
+ for (EnabledComponentChangeListener l : mEnabledComponentListeners) {
+ l.onEnabledComponentChanged();
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/vr/SettingsObserver.java b/services/core/java/com/android/server/vr/SettingsObserver.java
new file mode 100644
index 0000000..ce76863
--- /dev/null
+++ b/services/core/java/com/android/server/vr/SettingsObserver.java
@@ -0,0 +1,145 @@
+/**
+ * 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.vr;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArraySet;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Detects changes in a given setting.
+ *
+ * @hide
+ */
+public class SettingsObserver {
+
+ private final String mSecureSettingName;
+ private final BroadcastReceiver mSettingRestorReceiver;
+ private final ContentObserver mContentObserver;
+ private final Set<SettingChangeListener> mSettingsListeners = new ArraySet<>();
+
+ /**
+ * Implement this to receive callbacks when the setting tracked by this observer changes.
+ */
+ public interface SettingChangeListener {
+
+ /**
+ * Called when the tracked setting has changed.
+ */
+ void onSettingChanged();
+
+
+ /**
+ * Called when the tracked setting has been restored for a particular user.
+ *
+ * @param prevValue the previous value of the setting.
+ * @param newValue the new value of the setting.
+ * @param userId the user ID for which this setting has been restored.
+ */
+ void onSettingRestored(String prevValue, String newValue, int userId);
+ }
+
+ private SettingsObserver(@NonNull final Context context, @NonNull final Handler handler,
+ @NonNull final Uri settingUri, @NonNull final String secureSettingName) {
+
+ mSecureSettingName = secureSettingName;
+ mSettingRestorReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_SETTING_RESTORED.equals(intent.getAction())) {
+ String element = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
+ if (Objects.equals(element, secureSettingName)) {
+ String prevValue = intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
+ String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
+ sendSettingRestored(prevValue, newValue, getSendingUserId());
+ }
+ }
+ }
+ };
+
+ mContentObserver = new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri == null || settingUri.equals(uri)) {
+ sendSettingChanged();
+ }
+ }
+ };
+
+ ContentResolver resolver = context.getContentResolver();
+ resolver.registerContentObserver(settingUri, false, mContentObserver,
+ UserHandle.USER_ALL);
+ }
+
+ /**
+ * Create a SettingsObserver instance.
+ *
+ * @param context the context to query for settings changes.
+ * @param handler the handler to use for a settings ContentObserver.
+ * @param settingName the setting to track.
+ * @return a SettingsObserver instance.
+ */
+ public static SettingsObserver build(@NonNull Context context, @NonNull Handler handler,
+ @NonNull String settingName) {
+ Uri settingUri = Settings.Secure.getUriFor(settingName);
+
+ return new SettingsObserver(context, handler, settingUri, settingName);
+ }
+
+ /**
+ * Add a listener for setting changes.
+ *
+ * @param listener a {@link SettingChangeListener} instance.
+ */
+ public void addListener(@NonNull SettingChangeListener listener) {
+ mSettingsListeners.add(listener);
+
+ }
+
+ /**
+ * Remove a listener for setting changes.
+ *
+ * @param listener a {@link SettingChangeListener} instance.
+ */
+ public void removeListener(@NonNull SettingChangeListener listener) {
+ mSettingsListeners.remove(listener);
+
+ }
+
+ private void sendSettingChanged() {
+ for (SettingChangeListener l : mSettingsListeners) {
+ l.onSettingChanged();
+ }
+ }
+
+ private void sendSettingRestored(final String prevValue, final String newValue, final int userId) {
+ for (SettingChangeListener l : mSettingsListeners) {
+ l.onSettingRestored(prevValue, newValue, userId);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 42db364..6b5523f 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,14 +15,22 @@
*/
package com.android.server.vr;
+import android.annotation.NonNull;
+import android.content.ComponentName;
+
/**
- * VR mode local system service interface.
+ * Service for accessing the VR mode manager.
*
* @hide Only for use within system server.
*/
public abstract class VrManagerInternal {
/**
+ * The error code returned on success.
+ */
+ public static final int NO_ERROR = 0;
+
+ /**
* Return current VR mode state.
*
* @return {@code true} if VR mode is enabled.
@@ -33,8 +41,11 @@
* Set the current VR mode state.
*
* @param enabled {@code true} to enable VR mode.
+ * @param packageName The package name of the requested VrListenerService to bind.
+ * @param userId the user requesting the VrListenerService component.
*/
- public abstract void setVrMode(boolean enabled);
+ public abstract void setVrMode(boolean enabled, @NonNull ComponentName packageName,
+ int userId);
/**
* Add a listener for VR mode state changes.
@@ -43,13 +54,23 @@
* </p>
* @param listener the listener instance to add.
*/
- public abstract void registerListener(VrStateListener listener);
+ public abstract void registerListener(@NonNull VrStateListener listener);
/**
* Remove the listener from the current set of listeners.
*
* @param listener the listener to remove.
*/
- public abstract void unregisterListener(VrStateListener listener);
+ public abstract void unregisterListener(@NonNull VrStateListener listener);
+
+ /**
+ * Return NO_ERROR if the given package is installed on the device and enabled as a
+ * VrListenerService for the given current user, or a negative error code indicating a failure.
+ *
+ * @param packageName the name of the package to check, or null to select the default package.
+ * @return NO_ERROR if the given package is installed and is enabled, or a negative error code
+ * given in {@link android.service.vr.VrModeException} on failure.
+ */
+ public abstract int hasVrPackage(@NonNull ComponentName packageName, int userId);
}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 7611527..d0ee6e0 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,26 +16,53 @@
package com.android.server.vr;
import android.app.AppOpsManager;
+import android.annotation.NonNull;
import android.content.Context;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Looper;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.vr.IVrListener;
+import android.service.vr.VrListenerService;
import android.util.ArraySet;
import android.util.Slog;
+import com.android.internal.R;
import com.android.server.SystemService;
+import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
+import com.android.server.utils.ManagedApplicationService;
+import com.android.server.utils.ManagedApplicationService.BinderChecker;
import java.util.ArrayList;
+import java.util.Set;
/**
- * Service tracking whether VR mode is active, and notifying listening system services of state
- * changes.
+ * Service tracking whether VR mode is active, and notifying listening services of state changes.
+ * <p/>
+ * Services running in system server may modify the state of VrManagerService via the interface in
+ * VrManagerInternal, and may register to receive callbacks when the system VR mode changes via the
+ * interface given in VrStateListener.
+ * <p/>
+ * Device vendors may choose to receive VR state changes by implementing the VR mode HAL, e.g.:
+ * hardware/libhardware/modules/vr
+ * <p/>
+ * In general applications may enable or disable VR mode by calling
+ * {@link android.app.Activity#setVrMode)}. An application may also implement a service to be run
+ * while in VR mode by implementing {@link android.service.vr.VrListenerService}.
*
- * {@hide}
+ * @see {@link android.service.vr.VrListenerService}
+ * @see {@link com.android.server.vr.VrManagerInternal}
+ * @see {@link com.android.server.vr.VrStateListener}
+ *
+ * @hide
*/
-public class VrManagerService extends SystemService {
+public class VrManagerService extends SystemService implements EnabledComponentChangeListener{
public static final String TAG = "VrManagerService";
@@ -46,9 +73,45 @@
private final IBinder mOverlayToken = new Binder();
+ // State protected by mLock
private boolean mVrModeEnabled = false;
- private ArraySet<VrStateListener> mListeners = new ArraySet<>();
+ private final Set<VrStateListener> mListeners = new ArraySet<>();
+ private EnabledComponentsObserver mComponentObserver;
+ private ManagedApplicationService mCurrentVrService;
+ private Context mContext;
+ private static final BinderChecker sBinderChecker = new BinderChecker() {
+ @Override
+ public IInterface asInterface(IBinder binder) {
+ return IVrListener.Stub.asInterface(binder);
+ }
+
+ @Override
+ public boolean checkType(IInterface service) {
+ return service instanceof IVrListener;
+ }
+ };
+
+ /**
+ * Called when a user, package, or setting changes that could affect whether or not the
+ * currently bound VrListenerService is changed.
+ */
+ @Override
+ public void onEnabledComponentChanged() {
+ synchronized (mLock) {
+ if (mCurrentVrService == null) {
+ return; // No active services
+ }
+
+ // There is an active service, update it if needed
+ updateCurrentVrServiceLocked(mVrModeEnabled, mCurrentVrService.getComponent(),
+ mCurrentVrService.getUserId());
+ }
+ }
+
+ /**
+ * Implementation of VrManagerInternal. Callable only from system services.
+ */
private final class LocalService extends VrManagerInternal {
@Override
public boolean isInVrMode() {
@@ -56,8 +119,8 @@
}
@Override
- public void setVrMode(boolean enabled) {
- VrManagerService.this.setVrMode(enabled);
+ public void setVrMode(boolean enabled, ComponentName packageName, int userId) {
+ VrManagerService.this.setVrMode(enabled, packageName, userId);
}
@Override
@@ -69,6 +132,11 @@
public void unregisterListener(VrStateListener listener) {
VrManagerService.this.removeListener(listener);
}
+
+ @Override
+ public int hasVrPackage(ComponentName packageName, int userId) {
+ return VrManagerService.this.hasVrPackage(packageName, userId);
+ }
}
public VrManagerService(Context context) {
@@ -79,11 +147,190 @@
public void onStart() {
synchronized(mLock) {
initializeNative();
+ mContext = getContext();
}
publishLocalService(VrManagerInternal.class, new LocalService());
}
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+ synchronized (mLock) {
+ Looper looper = Looper.getMainLooper();
+ Handler handler = new Handler(looper);
+ ArrayList<EnabledComponentChangeListener> listeners = new ArrayList<>();
+ listeners.add(this);
+ mComponentObserver = EnabledComponentsObserver.build(mContext, handler,
+ Settings.Secure.ENABLED_VR_LISTENERS, looper,
+ android.Manifest.permission.BIND_VR_LISTENER_SERVICE,
+ VrListenerService.SERVICE_INTERFACE, mLock, listeners);
+
+ mComponentObserver.rebuildAll();
+ }
+ }
+ }
+
+ @Override
+ public void onStartUser(int userHandle) {
+ synchronized (mLock) {
+ mComponentObserver.onUsersChanged();
+ }
+ }
+
+ @Override
+ public void onSwitchUser(int userHandle) {
+ synchronized (mLock) {
+ mComponentObserver.onUsersChanged();
+ }
+
+ }
+
+ @Override
+ public void onStopUser(int userHandle) {
+ synchronized (mLock) {
+ mComponentObserver.onUsersChanged();
+ }
+
+ }
+
+ @Override
+ public void onCleanupUser(int userHandle) {
+ synchronized (mLock) {
+ mComponentObserver.onUsersChanged();
+ }
+ }
+
+ private void updateOverlayStateLocked(ComponentName exemptedComponent) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+ if (appOpsManager != null) {
+ String[] exemptions = (exemptedComponent == null) ? new String[0] :
+ new String[] { exemptedComponent.getPackageName() };
+
+ appOpsManager.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+ mVrModeEnabled, mOverlayToken, exemptions);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Send VR mode changes (if the mode state has changed), and update the bound/unbound state of
+ * the currently selected VR listener service. If the component selected for the VR listener
+ * service has changed, unbind the previous listener and bind the new listener (if enabled).
+ * <p/>
+ * Note: Must be called while holding {@code mLock}.
+ *
+ * @param enabled new state for VR mode.
+ * @param component new component to be bound as a VR listener.
+ * @param userId user owning the component to be bound.
+ *
+ * @return {@code true} if the component/user combination specified is valid.
+ */
+ private boolean updateCurrentVrServiceLocked(boolean enabled,
+ @NonNull ComponentName component, int userId) {
+
+ boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
+ EnabledComponentsObserver.NO_ERROR);
+
+ // Always send mode change events.
+ changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null);
+
+ if (!enabled || !validUserComponent) {
+ // Unbind whatever is running
+ if (mCurrentVrService != null) {
+ Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
+ mCurrentVrService.getUserId());
+ mCurrentVrService.disconnect();
+ mCurrentVrService = null;
+ }
+ return validUserComponent;
+ }
+
+ if (mCurrentVrService != null) {
+ // Unbind any running service that doesn't match the component/user selection
+ if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
+ Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
+ mCurrentVrService.getUserId());
+ mCurrentVrService = VrManagerService.create(mContext, component, userId);
+ mCurrentVrService.connect();
+ Slog.i(TAG, "Connecting " + mCurrentVrService.getComponent() + " for user " +
+ mCurrentVrService.getUserId());
+ }
+ // The service with the correct component/user is bound
+ } else {
+ // Nothing was previously running, bind a new service
+ mCurrentVrService = VrManagerService.create(mContext, component, userId);
+ mCurrentVrService.connect();
+ Slog.i(TAG, "Connecting " + mCurrentVrService.getComponent() + " for user " +
+ mCurrentVrService.getUserId());
+ }
+
+ return validUserComponent;
+ }
+
+ /**
+ * Send VR mode change callbacks to HAL and system services if mode has actually changed.
+ * <p/>
+ * Note: Must be called while holding {@code mLock}.
+ *
+ * @param enabled new state of the VR mode.
+ * @param exemptedComponent a component to exempt from AppOps restrictions for overlays.
+ */
+ private void changeVrModeLocked(boolean enabled, ComponentName exemptedComponent) {
+ if (mVrModeEnabled != enabled) {
+ mVrModeEnabled = enabled;
+
+ // Log mode change event.
+ Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled"));
+ setVrModeNative(mVrModeEnabled);
+
+ updateOverlayStateLocked(exemptedComponent);
+ onVrModeChangedLocked();
+ }
+ }
+
+ /**
+ * Notify system services of VR mode change.
+ * <p/>
+ * Note: Must be called while holding {@code mLock}.
+ */
+ private void onVrModeChangedLocked() {
+ for (VrStateListener l : mListeners) {
+ l.onVrStateChanged(mVrModeEnabled);
+ }
+ }
+
+ /**
+ * Helper function for making ManagedApplicationService instances.
+ */
+ private static ManagedApplicationService create(@NonNull Context context,
+ @NonNull ComponentName component, int userId) {
+ return ManagedApplicationService.build(context, component, userId,
+ R.string.vr_listener_binding_label, Settings.ACTION_VR_LISTENER_SETTINGS,
+ sBinderChecker);
+ }
+
+ /*
+ * Implementation of VrManagerInternal calls. These are callable from system services.
+ */
+
+ private boolean setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
+ int userId) {
+ synchronized (mLock) {
+ return updateCurrentVrServiceLocked(enabled, targetPackageName, userId);
+ }
+ }
+
+ private boolean getVrMode() {
+ synchronized (mLock) {
+ return mVrModeEnabled;
+ }
+ }
+
private void addListener(VrStateListener listener) {
synchronized (mLock) {
mListeners.add(listener);
@@ -96,44 +343,9 @@
}
}
- private void setVrMode(boolean enabled) {
+ private int hasVrPackage(@NonNull ComponentName targetPackageName, int userId) {
synchronized (mLock) {
- if (mVrModeEnabled != enabled) {
- mVrModeEnabled = enabled;
- // Log mode change event.
- Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled"));
- setVrModeNative(mVrModeEnabled);
- updateOverlayStateLocked();
- onVrModeChangedLocked();
- }
- }
- }
-
- private void updateOverlayStateLocked() {
- final long identity = Binder.clearCallingIdentity();
- try {
- AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
- if (appOpsManager != null) {
- appOpsManager.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
- mVrModeEnabled, mOverlayToken);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private boolean getVrMode() {
- synchronized (mLock) {
- return mVrModeEnabled;
- }
- }
-
- /**
- * Notify system services of VR mode change.
- */
- private void onVrModeChangedLocked() {
- for (VrStateListener l : mListeners) {
- l.onVrStateChanged(mVrModeEnabled);
+ return mComponentObserver.isValid(targetPackageName, userId);
}
}
}
diff --git a/services/core/java/com/android/server/vr/VrStateListener.java b/services/core/java/com/android/server/vr/VrStateListener.java
index b8af4b2..b0603c80 100644
--- a/services/core/java/com/android/server/vr/VrStateListener.java
+++ b/services/core/java/com/android/server/vr/VrStateListener.java
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,9 @@
package com.android.server.vr;
/**
- * Listener for state changes in VrManagerService,
+ * Listener for state changes in VrManagerService.
+ *
+ * @hide Only for use within system server.
*/
public abstract class VrStateListener {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index a0a971a..ffdd89a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -441,12 +441,8 @@
}
// Called during initialization of a given user's wallpaper bookkeeping
- boolean ensureCropExists() {
- // if the crop file is not present, copy over the source image to use verbatim
- if (!cropFile.exists()) {
- return FileUtils.copyFile(wallpaperFile, cropFile);
- }
- return true;
+ boolean cropExists() {
+ return cropFile.exists();
}
}
@@ -734,7 +730,12 @@
public void systemRunning() {
if (DEBUG) Slog.v(TAG, "systemReady");
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
- if (!wallpaper.ensureCropExists()) {
+ // No crop file? Make sure we've finished the processing sequence if necessary
+ if (!wallpaper.cropExists()) {
+ generateCrop(wallpaper);
+ }
+ // Still nothing? Fall back to default.
+ if (!wallpaper.cropExists()) {
clearWallpaperLocked(false, FLAG_SET_SYSTEM, UserHandle.USER_SYSTEM, null);
}
switchWallpaper(wallpaper, null);
@@ -1317,7 +1318,7 @@
List<ResolveInfo> ris =
mIPackageManager.queryIntentServices(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- PackageManager.GET_META_DATA, serviceUserId);
+ PackageManager.GET_META_DATA, serviceUserId).getList();
for (int i=0; i<ris.size(); i++) {
ServiceInfo rsi = ris.get(i).serviceInfo;
if (rsi.name.equals(si.name) &&
@@ -1645,7 +1646,9 @@
if (wallpaper == null) {
wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
mWallpaperMap.put(userId, wallpaper);
- wallpaper.ensureCropExists();
+ if (!wallpaper.cropExists()) {
+ generateCrop(wallpaper);
+ }
}
boolean success = false;
try {
@@ -1809,7 +1812,8 @@
if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
+ " id=" + wallpaper.wallpaperId);
if (success) {
- bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
+ generateCrop(wallpaper); // based on the new image + metadata
+ bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, true, false,
wallpaper, null);
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 9c770e1..2db6b5d 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -190,7 +190,7 @@
userAddedFilter.addAction(Intent.ACTION_USER_ADDED);
getContext().registerReceiver(mWebViewUpdatedReceiver, userAddedFilter);
- publishBinderService("webviewupdate", new BinderService());
+ publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/);
}
private static boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index a81fba0..3a5dec9 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -171,17 +171,21 @@
transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
}
+ void setNullAnimation() {
+ animation = null;
+ usingTransferredAnimation = false;
+ }
+
public void clearAnimation() {
if (animation != null) {
- animation = null;
animating = true;
}
clearThumbnail();
+ setNullAnimation();
if (mAppToken.deferClearAllDrawn) {
mAppToken.allDrawn = false;
mAppToken.deferClearAllDrawn = false;
}
- usingTransferredAnimation = false;
}
public boolean isAnimating() {
@@ -190,7 +194,8 @@
public void clearThumbnail() {
if (thumbnail != null) {
- thumbnail.destroy();
+ thumbnail.hide();
+ mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail);
thumbnail = null;
}
deferThumbnailDestruction = false;
@@ -201,9 +206,9 @@
if (animation != null) {
toAppAnimator.animation = animation;
- animation = null;
toAppAnimator.animating = animating;
toAppAnimator.animLayerAdjustment = animLayerAdjustment;
+ setNullAnimation();
animLayerAdjustment = 0;
toAppAnimator.updateLayers();
updateLayers();
@@ -310,7 +315,7 @@
if (mProlongAnimation == PROLONG_ANIMATION_AT_END) {
hasMoreFrames = true;
} else {
- animation = null;
+ setNullAnimation();
clearThumbnail();
if (DEBUG_ANIM) Slog.v(TAG, "Finished animation in " + mAppToken + " @ "
+ currentTime);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ecfbc0a..4ec297e 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -129,6 +129,7 @@
boolean mAlwaysFocusable;
boolean mAppStopped;
+ int mPendingRelaunchCount;
ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
@@ -321,23 +322,6 @@
}
}
- void setWindowsExiting(boolean exiting) {
- for (int i = allAppWindows.size() - 1; i >= 0; i--) {
- WindowState win = allAppWindows.get(i);
- // If the app already requested to remove its window, we don't modify
- // its exiting state. Otherwise the stale window won't get removed on
- // exit and could cause focus to be given to the wrong window.
- if (!(win.mRemoveOnExit && win.mAnimatingExit)) {
- win.mAnimatingExit = exiting;
- }
- // If we're no longer exiting, remove the window from destroying list
- if (!win.mAnimatingExit && win.mDestroying) {
- win.mDestroying = false;
- service.mDestroySurface.remove(win);
- }
- }
- }
-
// 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.
@@ -354,6 +338,11 @@
continue;
}
+ if (DEBUG_ADD_REMOVE) Slog.e(TAG_WM, "win=" + win
+ + " destroySurfaces: mAppStopped=" + mAppStopped
+ + " win.mWindowRemovalAllowed=" + win.mWindowRemovalAllowed
+ + " win.mRemoveOnExit=" + win.mRemoveOnExit);
+
win.destroyOrSaveSurface();
if (win.mRemoveOnExit) {
win.mAnimatingExit = false;
@@ -372,15 +361,19 @@
}
}
- // The application has stopped, so 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();
+ /**
+ * If the application has stopped it is okay to destroy any surfaces which were keeping alive
+ * in case they were still being used.
+ */
+ void notifyAppStopped(boolean stopped) {
+ if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: stopped=" + stopped + " " + this);
+ mAppStopped = stopped;
- // Remove any starting window that was added for this app if they are still around.
- mTask.mService.scheduleRemoveStartingWindowLocked(this);
+ if (stopped) {
+ destroySurfaces();
+ // Remove any starting window that was added for this app if they are still around.
+ mTask.mService.scheduleRemoveStartingWindowLocked(this);
+ }
}
/**
@@ -522,6 +515,26 @@
}
}
+ boolean isRelaunching() {
+ return mPendingRelaunchCount > 0;
+ }
+
+ void startRelaunching() {
+ if (canFreezeBounds()) {
+ freezeBounds();
+ }
+ mPendingRelaunchCount++;
+ }
+
+ void finishRelaunching() {
+ if (canFreezeBounds()) {
+ unfreezeBounds();
+ }
+ if (mPendingRelaunchCount > 0) {
+ mPendingRelaunchCount--;
+ }
+ }
+
void addWindow(WindowState w) {
for (int i = allAppWindows.size() - 1; i >= 0; i--) {
WindowState candidate = allAppWindows.get(i);
@@ -570,20 +583,26 @@
}
}
+ private boolean canFreezeBounds() {
+ // For freeform windows, we can't freeze the bounds at the moment because this would make
+ // the resizing unresponsive.
+ return mTask != null && !mTask.inFreeformWorkspace();
+ }
+
/**
* Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
* freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
* if they change in the meantime. If the bounds are already frozen, the bounds will be frozen
* with a queue.
*/
- void freezeBounds() {
+ private void freezeBounds() {
mFrozenBounds.offer(new Rect(mTask.mPreparedFrozenBounds));
}
/**
* Unfreezes the previously frozen bounds. See {@link #freezeBounds}.
*/
- void unfreezeBounds() {
+ private void unfreezeBounds() {
mFrozenBounds.remove();
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState win = windows.get(i);
@@ -649,7 +668,10 @@
pw.print(" startingMoved="); pw.println(startingMoved);
}
if (!mFrozenBounds.isEmpty()) {
- pw.print(prefix); pw.print("mFrozenBounds="); pw.print(mFrozenBounds);
+ pw.print(prefix); pw.print("mFrozenBounds="); pw.println(mFrozenBounds);
+ }
+ if (mPendingRelaunchCount != 0) {
+ pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
}
}
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index fc5d8ce..95be233 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -84,10 +84,13 @@
/** The user of this dim layer. */
private final DimLayerUser mUser;
- DimLayer(WindowManagerService service, DimLayerUser user, int displayId) {
+ private final String mName;
+
+ DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
mUser = user;
mDisplayId = displayId;
mService = service;
+ mName = name;
if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
}
@@ -100,7 +103,7 @@
16, 16, PixelFormat.OPAQUE,
SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
} else {
- mDimSurface = new SurfaceControl(service.mFxSession, TAG,
+ mDimSurface = new SurfaceControl(service.mFxSession, mName,
16, 16, PixelFormat.OPAQUE,
SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
}
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 6d1cec4..97d0ae0 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -10,6 +10,8 @@
import android.util.Slog;
import android.util.TypedValue;
+import com.android.server.wm.DimLayer.DimLayerUser;
+
import java.io.PrintWriter;
/**
@@ -18,7 +20,8 @@
* as well as other use cases (such as dimming above a dead window).
*/
class DimLayerController {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayerController" : TAG_WM;
+ private static final String TAG_LOCAL = "DimLayerController";
+ private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
/** Amount of time in milliseconds to animate the dim surface from one value to another,
* when no window animation is driving it. */
@@ -63,7 +66,8 @@
newDimLayer = state.dimLayer;
} else {
// Create new full screen dim layer.
- newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
+ newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
+ getDimLayerTag(dimLayerUser));
}
dimLayerUser.getDimBounds(mTmpBounds);
newDimLayer.setBounds(mTmpBounds);
@@ -73,7 +77,8 @@
}
} else {
newDimLayer = (state.dimLayer == null || previousFullscreen)
- ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
+ ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
+ getDimLayerTag(dimLayerUser))
: state.dimLayer;
dimLayerUser.getDimBounds(mTmpBounds);
newDimLayer.setBounds(mTmpBounds);
@@ -81,6 +86,10 @@
state.dimLayer = newDimLayer;
}
+ private static String getDimLayerTag(DimLayerUser dimLayerUser) {
+ return TAG_LOCAL + "/" + dimLayerUser.toShortString();
+ }
+
private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
+ dimLayerUser.toShortString());
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 73cea52..5212211 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -29,6 +29,7 @@
import android.app.ActivityManager.StackId;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.Region.Op;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.Display;
@@ -426,6 +427,10 @@
win.getTouchableRegion(mTmpRegion);
mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
}
+ if (getDockedStackVisibleForUserLocked() != null) {
+ mDividerControllerLocked.getTouchRegion(mTmpRect);
+ mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
+ }
if (mTapDetector != null) {
mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion, mNonResizeableRegion);
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b6aa3f2..36e8bbb 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,6 +16,17 @@
package com.android.server.wm;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
+import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
import android.content.Context;
import android.graphics.Rect;
import android.os.RemoteCallbackList;
@@ -30,19 +41,6 @@
import com.android.server.wm.DimLayer.DimLayerUser;
-import java.util.ArrayList;
-
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
-import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
/**
* Keeps information about the docked stack divider.
*/
@@ -97,6 +95,7 @@
private long mAnimationDuration;
private final Interpolator mMinimizedDockInterpolator;
private float mMaximizeMeetFraction;
+ private final Rect mTouchRegion = new Rect();
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -106,7 +105,8 @@
com.android.internal.R.dimen.docked_stack_divider_thickness);
mDividerInsets = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_insets);
- mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
+ mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
+ "DockedStackDim");
mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
context, android.R.interpolator.fast_out_slow_in);
}
@@ -130,6 +130,15 @@
}
}
+ void setTouchRegion(Rect touchRegion) {
+ mTouchRegion.set(touchRegion);
+ }
+
+ void getTouchRegion(Rect outRegion) {
+ outRegion.set(mTouchRegion);
+ outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+ }
+
private void resetDragResizingChangeReported() {
final WindowList windowList = mDisplayContent.getWindowList();
for (int i = windowList.size() - 1; i >= 0; i--) {
@@ -247,8 +256,9 @@
void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
SurfaceControl.openTransaction();
- TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
- boolean visibleAndValid = visible && stack != null;
+ final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
+ final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
+ boolean visibleAndValid = visible && stack != null && dockedStack != null;
if (visibleAndValid) {
stack.getDimBounds(mTmpRect);
if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index e42658e..b702180 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -24,6 +24,7 @@
import android.app.ActivityManagerNative;
import android.graphics.Rect;
+import android.os.Debug;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
@@ -36,6 +37,7 @@
import com.android.server.input.InputManagerService;
import com.android.server.input.InputWindowHandle;
+import java.io.PrintWriter;
import java.util.Arrays;
final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
@@ -47,6 +49,9 @@
// When true, prevents input dispatch from proceeding until set to false again.
private boolean mInputDispatchFrozen;
+ // The reason the input is currently frozen or null if the input isn't frozen.
+ private String mInputFreezeReason = null;
+
// When true, input dispatch proceeds normally. Otherwise all events are dropped.
// Initially false, so that input does not get dispatched until boot is finished at
// which point the ActivityManager will enable dispatching.
@@ -474,12 +479,16 @@
}
public void freezeInputDispatchingLw() {
- if (! mInputDispatchFrozen) {
+ if (!mInputDispatchFrozen) {
if (DEBUG_INPUT) {
Slog.v(TAG_WM, "Freezing input dispatching");
}
mInputDispatchFrozen = true;
+
+ if (DEBUG_INPUT || true) {
+ mInputFreezeReason = Debug.getCallers(6);
+ }
updateInputDispatchModeLw();
}
}
@@ -491,6 +500,7 @@
}
mInputDispatchFrozen = false;
+ mInputFreezeReason = null;
updateInputDispatchModeLw();
}
}
@@ -509,4 +519,10 @@
private void updateInputDispatchModeLw() {
mService.mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
}
+
+ void dump(PrintWriter pw, String prefix) {
+ if (mInputFreezeReason != null) {
+ pw.println(prefix + "mInputFreezeReason=" + mInputFreezeReason);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index f7035c5..92701de 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -60,7 +60,8 @@
import java.lang.annotation.RetentionPolicy;
class TaskPositioner implements DimLayer.DimLayerUser {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskPositioner" : TAG_WM;
+ private static final String TAG_LOCAL = "TaskPositioner";
+ private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
// The margin the pointer position has to be within the side of the screen to be
// considered at the side of the screen.
@@ -287,7 +288,7 @@
}
mService.pauseRotationLocked();
- mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
+ mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 86327f7..60b2e4a 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -148,9 +148,7 @@
boolean setBounds(
Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds,
SparseArray<Rect> taskTempInsetBounds) {
- if (!setBounds(stackBounds)) {
- return false;
- }
+ setBounds(stackBounds);
// Update bounds of containing tasks.
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -585,7 +583,8 @@
}
mDisplayContent = displayContent;
- mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
+ mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
+ "animation background stackId=" + mStackId);
Rect bounds = null;
final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
@@ -607,13 +606,6 @@
}
updateDisplayInfo(bounds);
-
- if (mStackId == DOCKED_STACK_ID) {
- // Attaching a docked stack to the display affects the size of all other static
- // stacks since the docked stack occupies a dedicated region on screen.
- // Resize existing static stacks so they are pushed to the side of the docked stack.
- resizeNonDockedStacks(!FULLSCREEN, mBounds);
- }
}
void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibility) {
@@ -722,36 +714,6 @@
DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
}
- /** Resizes all non-docked stacks in the system to either fullscreen or the appropriate size
- * based on the presence of a docked stack.
- * @param fullscreen If true the stacks will be resized to fullscreen, else they will be
- * resized to the appropriate size based on the presence of a docked stack.
- * @param dockedBounds Bounds of the docked stack.
- */
- private void resizeNonDockedStacks(boolean fullscreen, Rect dockedBounds) {
- // Not using mTmpRect because we are posting the object in a message.
- final Rect bounds = new Rect();
- mDisplayContent.getLogicalDisplayRect(bounds);
- if (!fullscreen) {
- final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
- == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
- getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
- mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
- }
-
- final int count = mService.mStackIdToStack.size();
- for (int i = 0; i < count; i++) {
- final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
- final int otherStackId = otherStack.mStackId;
- if (StackId.isResizeableByDockedStack(otherStackId)
- && !otherStack.mBounds.equals(bounds)) {
- mService.mH.sendMessage(
- mService.mH.obtainMessage(RESIZE_STACK, otherStackId,
- 1 /*allowResizeInDockedMode*/, fullscreen ? null : bounds));
- }
- }
- }
-
void resetDockedStackToMiddle() {
if (mStackId != DOCKED_STACK_ID) {
throw new IllegalStateException("Not a docked stack=" + this);
@@ -785,12 +747,6 @@
mService.mWindowPlacerLocked.requestTraversal();
}
- if (mStackId == DOCKED_STACK_ID) {
- // Docked stack was detached from the display, so we no longer need to restrict the
- // region of the screen other static stacks occupy. Go ahead and make them fullscreen.
- resizeNonDockedStacks(FULLSCREEN, null);
- }
-
close();
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 2e424d0..e44b0f3 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -508,10 +508,11 @@
replacing = replacing || w.mWillReplaceWindow;
- // If the app is executing an animation because the keyguard is going away,
- // keep the wallpaper during the animation so it doesn't flicker out.
+ // 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.mKeyguardGoingAwayAnimation);
+ || (w.mAppToken != null && w.mWinAnimator.mKeyguardGoingAwayWithWallpaper);
if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w);
result.setWallpaperTarget(w, i);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 85bddee..f243761 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -22,6 +22,9 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
+import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
+import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
@@ -98,8 +101,7 @@
boolean mInitialized = false;
boolean mKeyguardGoingAway;
- boolean mKeyguardGoingAwayToNotificationShade;
- boolean mKeyguardGoingAwayDisableWindowAnimations;
+ int mKeyguardGoingAwayFlags;
/** Use one animation for all entering activities after keyguard is dismissed. */
Animation mPostKeyguardExitAnimation;
@@ -243,6 +245,13 @@
final WindowList windows = mService.getWindowListLocked(displayId);
+ final boolean keyguardGoingAwayToShade =
+ (mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0;
+ final boolean keyguardGoingAwayNoAnimation =
+ (mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0;
+ final boolean keyguardGoingAwayWithWallpaper =
+ (mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0;
+
if (mKeyguardGoingAway) {
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState win = windows.get(i);
@@ -261,6 +270,8 @@
winAnimator.mAnimationIsEntrance = false;
winAnimator.mAnimationStartTime = -1;
winAnimator.mKeyguardGoingAwayAnimation = true;
+ winAnimator.mKeyguardGoingAwayWithWallpaper
+ = keyguardGoingAwayWithWallpaper;
}
} else {
if (DEBUG_KEYGUARD) Slog.d(TAG,
@@ -392,10 +403,13 @@
if (DEBUG_KEYGUARD) Slog.v(TAG,
"Applying existing Keyguard exit animation to new window: win="
+ win);
- Animation a = mPolicy.createForceHideEnterAnimation(
- false, mKeyguardGoingAwayToNotificationShade);
+
+ Animation a = mPolicy.createForceHideEnterAnimation(false,
+ keyguardGoingAwayToShade);
winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime());
winAnimator.mKeyguardGoingAwayAnimation = true;
+ winAnimator.mKeyguardGoingAwayWithWallpaper
+ = keyguardGoingAwayWithWallpaper;
}
final WindowState currentFocus = mService.mCurrentFocus;
if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
@@ -463,18 +477,20 @@
// being force-hidden, apply the appropriate animation to them if animations are not
// disabled.
if (unForceHiding != null) {
- if (!mKeyguardGoingAwayDisableWindowAnimations) {
+ if (!keyguardGoingAwayNoAnimation) {
boolean first = true;
for (int i=unForceHiding.size()-1; i>=0; i--) {
final WindowStateAnimator winAnimator = unForceHiding.get(i);
Animation a = mPolicy.createForceHideEnterAnimation(
wallpaperInUnForceHiding && !startingInUnForceHiding,
- mKeyguardGoingAwayToNotificationShade);
+ keyguardGoingAwayToShade);
if (a != null) {
if (DEBUG_KEYGUARD) Slog.v(TAG,
"Starting keyguard exit animation on window " + winAnimator.mWin);
winAnimator.setAnimation(a);
winAnimator.mKeyguardGoingAwayAnimation = true;
+ winAnimator.mKeyguardGoingAwayWithWallpaper
+ = keyguardGoingAwayWithWallpaper;
if (first) {
mPostKeyguardExitAnimation = a;
mPostKeyguardExitAnimation.setStartTime(mCurrentTime);
@@ -489,11 +505,10 @@
// Wallpaper is going away in un-force-hide motion, animate it as well.
- if (!wallpaperInUnForceHiding && wallpaper != null
- && !mKeyguardGoingAwayDisableWindowAnimations) {
+ if (!wallpaperInUnForceHiding && wallpaper != null && !keyguardGoingAwayNoAnimation) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: wallpaper animating away");
Animation a = mPolicy.createForceHideWallpaperExitAnimation(
- mKeyguardGoingAwayToNotificationShade);
+ keyguardGoingAwayToShade);
if (a != null) {
wallpaper.mWinAnimator.setAnimation(a);
}
@@ -771,6 +786,7 @@
}
mService.destroyPreservedSurfaceLocked();
+ mService.mWindowPlacerLocked.destroyPendingSurfaces();
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index ccba88d..0979cd3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -35,7 +35,7 @@
static final boolean DEBUG_RESIZE = false;
static final boolean DEBUG = false;
- static final boolean DEBUG_ADD_REMOVE = true;
+ static final boolean DEBUG_ADD_REMOVE = false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
static final boolean DEBUG_ANIM = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7c2a8e3..e60e163 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,6 +24,9 @@
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IActivityManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -118,6 +121,7 @@
import android.view.inputmethod.InputMethodManagerInternal;
import android.widget.Toast;
+import com.android.internal.R;
import com.android.internal.app.IAssistScreenshotReceiver;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IShortcutService;
@@ -2340,6 +2344,7 @@
mTokenMap.remove(token.token);
} else if (atoken != null) {
atoken.firstWindowDrawn = false;
+ atoken.allDrawn = false;
}
}
@@ -2621,7 +2626,8 @@
}
// Odd choice but less odd than embedding in copyFrom()
- if ((attrs.flags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY) != 0) {
+ if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
+ != 0) {
attrs.x = win.mAttrs.x;
attrs.y = win.mAttrs.y;
attrs.width = win.mAttrs.width;
@@ -2820,10 +2826,12 @@
if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = isDefaultDisplay;
win.mAnimatingExit = true;
+ win.mWinAnimator.mAnimating = true;
} else if (win.mWinAnimator.isAnimating()) {
// Currently in a hide animation... turn this into
// an exit.
win.mAnimatingExit = true;
+ win.mWinAnimator.mAnimating = true;
} else if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
// If the wallpaper is currently behind this
// window, we need to change both of them inside
@@ -4080,7 +4088,7 @@
if (transit != AppTransition.TRANSIT_UNSET) {
if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
- wtoken.mAppAnimator.animation = null;
+ wtoken.mAppAnimator.setNullAnimation();
}
if (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) {
delayed = runningAppAnimation = true;
@@ -4190,14 +4198,14 @@
void updateTokenInPlaceLocked(AppWindowToken wtoken, int transit) {
if (transit != AppTransition.TRANSIT_UNSET) {
if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
- wtoken.mAppAnimator.animation = null;
+ wtoken.mAppAnimator.setNullAnimation();
}
applyAnimationLocked(wtoken, null, transit, false, false);
}
}
@Override
- public void notifyAppStopped(IBinder token) {
+ public void notifyAppStopped(IBinder token, boolean stopped) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"notifyAppStopped()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -4210,7 +4218,7 @@
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
return;
}
- wtoken.notifyAppStopped();
+ wtoken.notifyAppStopped(stopped);
}
}
@@ -4247,8 +4255,31 @@
wtoken.appDied = false;
wtoken.removeAllWindows();
} else if (visible) {
+ if (DEBUG_ADD_REMOVE) Slog.v(
+ TAG_WM, "No longer Stopped: " + wtoken);
wtoken.mAppStopped = false;
- wtoken.setWindowsExiting(false);
+ mOpeningApps.add(wtoken);
+ wtoken.startingMoved = false;
+
+ // If the token is currently hidden (should be the
+ // common case), then we need to set up to wait for
+ // its windows to be ready.
+ if (wtoken.hidden) {
+ wtoken.allDrawn = false;
+ wtoken.deferClearAllDrawn = false;
+ wtoken.waitingToShow = true;
+
+ if (wtoken.clientHidden) {
+ // In the case where we are making an app visible
+ // but holding off for a transition, we still need
+ // to tell the client to make its windows visible so
+ // they get drawn. Otherwise, we will wait on
+ // performing the transition until all windows have
+ // been drawn, they never will be, and we are sad.
+ wtoken.clientHidden = false;
+ wtoken.sendAppVisibilityToClients();
+ }
+ }
}
// If we are preparing an app transition, then delay changing
@@ -4258,6 +4289,11 @@
// animation is going on (in this case an application transition). If the animation
// was transferred from another application/animator, no dummy animator should be
// created since an animation is already in progress.
+ if (wtoken.mAppAnimator.usingTransferredAnimation
+ && wtoken.mAppAnimator.animation == null) {
+ Slog.wtf(TAG_WM, "Will NOT set dummy animation on: " + wtoken
+ + ", using null transfered animation!");
+ }
if (!wtoken.mAppAnimator.usingTransferredAnimation &&
(!wtoken.startingDisplayed || mSkipAppTransitionAnimation)) {
if (DEBUG_APP_TRANSITIONS) Slog.v(
@@ -4266,31 +4302,8 @@
}
wtoken.inPendingTransaction = true;
if (visible) {
- mOpeningApps.add(wtoken);
- wtoken.startingMoved = false;
wtoken.mEnteringAnimation = true;
-
- // If the token is currently hidden (should be the
- // common case), then we need to set up to wait for
- // its windows to be ready.
- if (wtoken.hidden) {
- wtoken.allDrawn = false;
- wtoken.deferClearAllDrawn = false;
- wtoken.waitingToShow = true;
-
- if (wtoken.clientHidden) {
- // In the case where we are making an app visible
- // but holding off for a transition, we still need
- // to tell the client to make its windows visible so
- // they get drawn. Otherwise, we will wait on
- // performing the transition until all windows have
- // been drawn, they never will be, and we are sad.
- wtoken.clientHidden = false;
- wtoken.sendAppVisibilityToClients();
- }
- }
} else {
- wtoken.setWindowsExiting(true);
mClosingApps.add(wtoken);
wtoken.mEnteringAnimation = false;
}
@@ -5166,18 +5179,16 @@
}
@Override
- public void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade) {
+ public void keyguardGoingAway(int flags) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
- if (DEBUG_KEYGUARD) Slog.d(TAG_WM, "keyguardGoingAway: disableWinAnim="
- + disableWindowAnimations + " kgToNotifShade=" + keyguardGoingToNotificationShade);
+ if (DEBUG_KEYGUARD) Slog.d(TAG_WM,
+ "keyguardGoingAway: flags=0x" + Integer.toHexString(flags));
synchronized (mWindowMap) {
mAnimator.mKeyguardGoingAway = true;
- mAnimator.mKeyguardGoingAwayToNotificationShade = keyguardGoingToNotificationShade;
- mAnimator.mKeyguardGoingAwayDisableWindowAnimations = disableWindowAnimations;
+ mAnimator.mKeyguardGoingAwayFlags = flags;
mWindowPlacerLocked.requestTraversal();
}
}
@@ -7466,6 +7477,35 @@
return mCurrentFocus;
}
+ private void showAuditSafeModeNotification() {
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(Intent.ACTION_VIEW,
+ Uri.parse("https://support.google.com/nexus/answer/2852139")), 0);
+
+ String title = mContext.getString(R.string.audit_safemode_notification);
+
+ Notification notification = new Notification.Builder(mContext)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(title)
+ .setLocalOnly(true)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(mContext.getString(R.string.audit_safemode_notification_details))
+ .setContentIntent(pendingIntent)
+ .build();
+
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ notificationManager.notifyAsUser(null, R.string.audit_safemode_notification, notification,
+ UserHandle.ALL);
+ }
+
public boolean detectSafeMode() {
if (!mInputMonitor.waitForInputDevicesReady(
INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
@@ -7498,6 +7538,7 @@
if (auditSafeMode >= buildDate) {
mSafeMode = true;
+ showAuditSafeModeNotification();
} else {
SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, "");
SystemProperties.set(ShutdownThread.AUDIT_SAFEMODE_PROPERTY, "");
@@ -9634,8 +9675,8 @@
public void notifyAppRelaunching(IBinder token) {
synchronized (mWindowMap) {
AppWindowToken appWindow = findAppWindowToken(token);
- if (canFreezeBounds(appWindow)) {
- appWindow.freezeBounds();
+ if (appWindow != null) {
+ appWindow.startRelaunching();
}
}
}
@@ -9643,20 +9684,12 @@
public void notifyAppRelaunchingFinished(IBinder token) {
synchronized (mWindowMap) {
AppWindowToken appWindow = findAppWindowToken(token);
- if (canFreezeBounds(appWindow)) {
- appWindow.unfreezeBounds();
+ if (appWindow != null) {
+ appWindow.finishRelaunching();
}
}
}
- private boolean canFreezeBounds(AppWindowToken appWindow) {
-
- // For freeform windows, we can't freeze the bounds at the moment because this would make
- // the resizing unresponsive.
- return appWindow != null && appWindow.mTask != null
- && !appWindow.mTask.inFreeformWorkspace();
- }
-
@Override
public int getDockedDividerInsetsLw() {
return getDefaultDisplayContentLocked().getDockedDividerController().getContentInsets();
@@ -9878,6 +9911,9 @@
pw.print(mLastFinishedFreezeSource);
}
pw.println();
+
+ mInputMonitor.dump(pw, " ");
+
if (dumpAll) {
pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer);
pw.print(" mScreenRect="); pw.println(mScreenRect.toShortString());
@@ -10409,6 +10445,15 @@
}
@Override
+ public void setDockedStackDividerTouchRegion(Rect touchRegion) {
+ synchronized (mWindowMap) {
+ getDefaultDisplayContentLocked().getDockedDividerController()
+ .setTouchRegion(touchRegion);
+ setFocusTaskRegionLocked();
+ }
+ }
+
+ @Override
public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
synchronized (mWindowMap) {
getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer(
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4ad021e..cd771ba 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -101,6 +101,10 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
class WindowList extends ArrayList<WindowState> {
+ WindowList() {}
+ WindowList(WindowList windowList) {
+ super(windowList);
+ }
}
/**
@@ -638,7 +642,7 @@
mHaveFrame = true;
final Task task = getTask();
- final boolean fullscreenTask = task == null || task.isFullscreen();
+ final boolean fullscreenTask = !inMultiWindowMode();
final boolean windowsAreFloating = task != null && task.isFloating();
if (fullscreenTask || (isChildWindow()
@@ -666,8 +670,8 @@
}
if (windowsAreFloating) {
- // In floating modes (e.g. freeform, pinned) we have only to set the rectangle
- // if it wasn't set already. No need to intersect it with the (visible)
+ // In floating modes (e.g. freeform, pinned) we have only to set the rectangle
+ // if it wasn't set already. No need to intersect it with the (visible)
// "content frame" since it is allowed to be outside the visible desktop.
if (mContainingFrame.isEmpty()) {
mContainingFrame.set(cf);
@@ -776,9 +780,9 @@
Math.min(mStableFrame.bottom, frame.bottom));
}
- if (!windowsAreFloating) {
- // Windows from floating tasks (e.g. freeform, pinned) may be positioned outside
- // of the display frame, but that is not a reason to provide them with overscan insets.
+ if (fullscreenTask && !windowsAreFloating) {
+ // Windows that are not fullscreen can be positioned outside of the display frame,
+ // but that is not a reason to provide them with overscan insets.
mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0),
Math.max(mOverscanFrame.top - frame.top, 0),
Math.max(frame.right - mOverscanFrame.right, 0),
@@ -2211,6 +2215,12 @@
return task != null && task.inFreeformWorkspace();
}
+ @Override
+ public boolean inMultiWindowMode() {
+ final Task task = getTask();
+ return task != null && !task.isFullscreen();
+ }
+
boolean isDragResizeChanged() {
return mDragResizing != computeDragResizing();
}
@@ -2253,7 +2263,7 @@
// background.
return (mDisplayContent.mDividerControllerLocked.isResizing()
|| mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
- !task.inFreeformWorkspace() && isVisibleLw();
+ !task.inFreeformWorkspace() && !isGoneForLayoutLw();
}
@@ -2502,9 +2512,8 @@
final int pw = mContainingFrame.width();
final int ph = mContainingFrame.height();
final Task task = getTask();
- final boolean nonFullscreenTask = task != null && !task.isFullscreen();
- final boolean fitToDisplay = task != null &&
- !task.isFloating();
+ final boolean nonFullscreenTask = inMultiWindowMode();
+ final boolean fitToDisplay = task != null && !task.isFloating();
float x, y;
int w,h;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c1ff96e..6ce1a64 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -160,6 +160,7 @@
boolean mEnteringAnimation;
boolean mKeyguardGoingAwayAnimation;
+ boolean mKeyguardGoingAwayWithWallpaper;
/** The pixel format of the underlying SurfaceControl */
int mSurfaceFormat;
@@ -250,6 +251,7 @@
mAnimation.cancel();
mAnimation = null;
mKeyguardGoingAwayAnimation = false;
+ mKeyguardGoingAwayWithWallpaper = false;
}
}
@@ -381,6 +383,7 @@
mAnimating = false;
mKeyguardGoingAwayAnimation = false;
+ mKeyguardGoingAwayWithWallpaper = false;
mLocalAnimating = false;
if (mAnimation != null) {
mAnimation.cancel();
@@ -434,9 +437,12 @@
+ " remove=" + mWin.mRemoveOnExit
+ " windowAnimating=" + isWindowAnimating());
- final int N = mWin.mChildWindows.size();
- for (int i=0; i<N; i++) {
- mWin.mChildWindows.get(i).mWinAnimator.finishExit();
+ if (!mWin.mChildWindows.isEmpty()) {
+ // Copying to a different list as multiple children can be removed.
+ final WindowList childWindows = new WindowList(mWin.mChildWindows);
+ for (int i = childWindows.size() - 1; i >= 0; i--) {
+ childWindows.get(i).mWinAnimator.finishExit();
+ }
}
if (mEnteringAnimation) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index e3955fe..5ad771f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -46,7 +46,6 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Debug;
-import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -125,6 +124,8 @@
}
private final LayerAndToken mTmpLayerAndToken = new LayerAndToken();
+ private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
public WindowSurfacePlacer(WindowManagerService service) {
mService = service;
mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
@@ -542,6 +543,7 @@
mService.enableScreenIfNeededLocked();
mService.scheduleAnimationLocked();
+ mService.mWindowPlacerLocked.destroyPendingSurfaces();
if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
"performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
@@ -1156,7 +1158,7 @@
if (!appAnimator.usingTransferredAnimation) {
appAnimator.clearThumbnail();
- appAnimator.animation = null;
+ appAnimator.setNullAnimation();
}
wtoken.inPendingTransaction = false;
@@ -1229,7 +1231,7 @@
final AppWindowAnimator appAnimator = wtoken.mAppAnimator;
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
appAnimator.clearThumbnail();
- appAnimator.animation = null;
+ appAnimator.setNullAnimation();
wtoken.inPendingTransaction = false;
mService.setTokenVisibilityLocked(wtoken, animLp, false, transit, false,
voiceInteraction);
@@ -1278,7 +1280,12 @@
"Check opening app=" + wtoken + ": allDrawn="
+ wtoken.allDrawn + " startingDisplayed="
+ wtoken.startingDisplayed + " startingMoved="
- + wtoken.startingMoved);
+ + wtoken.startingMoved + " isRelaunching()="
+ + wtoken.isRelaunching());
+
+ if (wtoken.isRelaunching()) {
+ return false;
+ }
final boolean drawnBeforeRestoring = wtoken.allDrawn;
wtoken.restoreSavedSurfaces();
@@ -1487,7 +1494,7 @@
if (DEBUG_APP_TRANSITIONS)
Slog.v(TAG, "Now animating app in place " + wtoken);
appAnimator.clearThumbnail();
- appAnimator.animation = null;
+ appAnimator.setNullAnimation();
mService.updateTokenInPlaceLocked(wtoken, transit);
wtoken.updateReportedVisibilityLocked();
@@ -1620,6 +1627,25 @@
}
}
+ /**
+ * Puts the {@param surface} into a pending list to be destroyed after the current transaction
+ * has been committed.
+ */
+ void destroyAfterTransaction(SurfaceControl surface) {
+ mPendingDestroyingSurfaces.add(surface);
+ }
+
+ /**
+ * Destroys any surfaces that have been put into the pending list with
+ * {@link #destroyAfterTransaction}.
+ */
+ void destroyPendingSurfaces() {
+ for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+ mPendingDestroyingSurfaces.get(i).destroy();
+ }
+ mPendingDestroyingSurfaces.clear();
+ }
+
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mTraversalScheduled="); pw.println(mTraversalScheduled);
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index aa14fff..e2c71a1 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1087,8 +1087,25 @@
const char *const JavaMethodHelper<bool>::signature_ = "(Z)V";
#define SET(setter, value) object.callSetter("set" # setter, (value))
-#define SET_IF(flag, setter, value) \
- if (flags & (flag)) object.callSetter("set" # setter, (value))
+
+// If you want to check if a flag is not set, use SET_IF_NOT(FLAG, setter,
+// value) to do that. SET_IF(!FLAG, setter, value) won't compile.
+//
+// This macros generates compilation error if the provided 'flag' is not a
+// single token. For example, 'GNSS_CLOCK_HAS_BIAS' can be accepted, but
+// '!GNSS_CLOCK_HAS_DRIFT' will fail to compile.
+#define SET_IF(flag, setter, value) do { \
+ if (flags & flag) { \
+ JavaObject& name_check_##flag = object; \
+ name_check_##flag.callSetter("set" # setter, (value)); \
+ } \
+ } while (false)
+#define SET_IF_NOT(flag, setter, value) do { \
+ if (!(flags & flag)) { \
+ JavaObject& name_check_##flag = object; \
+ name_check_##flag.callSetter("set" # setter, (value)); \
+ } \
+ } while (false)
static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) {
static uint32_t discontinuity_count_to_handle_old_lock_type = 0;
@@ -1205,9 +1222,14 @@
SET_IF(GNSS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
CarrierPhaseUncertainty,
measurement->carrier_phase_uncertainty);
- SET(MultipathIndicator, measurement->multipath_indicator);
+ SET(MultipathIndicator,
+ static_cast<int32_t>(measurement->multipath_indicator));
SET_IF(GNSS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
+ SET_IF_NOT(GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE,
+ PseudorangeRateCorrected,
+ true);
+
return object.get();
}
@@ -1244,7 +1266,8 @@
SET_IF(GNSS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY,
CarrierPhaseUncertainty,
measurement->carrier_phase_uncertainty);
- SET(MultipathIndicator, measurement->multipath_indicator);
+ SET(MultipathIndicator,
+ static_cast<int32_t>(measurement->multipath_indicator));
SET_IF(GNSS_MEASUREMENT_HAS_SNR, SnrInDb, measurement->snr_db);
return object.get();
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 6c640ba..c4316f6 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -400,7 +400,7 @@
connection.mThread->shutdown();
}
connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
- connection.mThread->run();
+ connection.mThread->run("BufferProducerThread");
}
}
connection.mSurface = surface;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1490109..a01002b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -31,8 +31,10 @@
import android.Manifest.permission;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accounts.AccountManager;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -82,6 +84,7 @@
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
@@ -151,6 +154,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.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@@ -280,6 +285,20 @@
private static final int PROFILE_KEYGUARD_FEATURES =
PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY;
+ private static final int CODE_OK = 0;
+ private static final int CODE_HAS_DEVICE_OWNER = 1;
+ private static final int CODE_USER_HAS_PROFILE_OWNER = 2;
+ private static final int CODE_USER_NOT_RUNNING = 3;
+ private static final int CODE_USER_SETUP_COMPLETED = 4;
+ private static final int CODE_NONSYSTEM_USER_EXISTS = 5;
+ private static final int CODE_ACCOUNTS_NOT_EMPTY = 6;
+ private static final int CODE_NOT_SYSTEM_USER = 7;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ CODE_OK, CODE_HAS_DEVICE_OWNER, CODE_USER_HAS_PROFILE_OWNER, CODE_USER_NOT_RUNNING,
+ CODE_USER_SETUP_COMPLETED, CODE_NOT_SYSTEM_USER })
+ private @interface DeviceOwnerPreConditionCode {}
+
private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000;
final Context mContext;
@@ -308,7 +327,7 @@
* Whether or not device admin feature is supported. If it isn't return defaults for all
* public methods.
*/
- private boolean mHasFeature;
+ boolean mHasFeature;
private final SecurityLogMonitor mSecurityLogMonitor;
@@ -328,7 +347,7 @@
@Override
public void onReceive(Context context, Intent intent) {
- if (RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction())
+ if (DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction())
&& mRemoteBugreportServiceIsActive.get()) {
onBugreportFinished(intent);
}
@@ -342,10 +361,9 @@
String action = intent.getAction();
mInjector.getNotificationManager().cancel(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID);
- if (RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
+ if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
onBugreportSharingAccepted();
- } else if (RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_DECLINED
- .equals(action)) {
+ } else if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) {
onBugreportSharingDeclined();
}
mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
@@ -437,15 +455,14 @@
&& userHandle == mOwners.getDeviceOwnerUserId()
&& getDeviceOwnerRemoteBugreportUri() != null) {
IntentFilter filterConsent = new IntentFilter();
- filterConsent.addAction(
- RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_DECLINED);
- filterConsent.addAction(
- RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED);
+ filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
+ filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
- mInjector.getNotificationManager().notify(LOG_TAG,
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
- RemoteBugreportUtils.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED));
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
+ UserHandle.ALL);
}
if (Intent.ACTION_BOOT_COMPLETED.equals(action)
|| ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
@@ -1363,6 +1380,22 @@
return new LockPatternUtils(mContext);
}
+ boolean storageManagerIsFileBasedEncryptionEnabled() {
+ return StorageManager.isFileEncryptedNativeOnly();
+ }
+
+ boolean storageManagerIsNonDefaultBlockEncrypted() {
+ return StorageManager.isNonDefaultBlockEncrypted();
+ }
+
+ boolean storageManagerIsEncrypted() {
+ return StorageManager.isEncrypted();
+ }
+
+ boolean storageManagerIsEncryptable() {
+ return StorageManager.isEncryptable();
+ }
+
Looper getMyLooper() {
return Looper.myLooper();
}
@@ -1622,23 +1655,23 @@
}
// Still at the first stage of CryptKeeper double bounce, mOwners.hasDeviceOwner is
// always false at this point.
- if ("encrypted".equals(mInjector.systemPropertiesGet("ro.crypto.state"))
- && "trigger_restart_min_framework".equals(
- mInjector.systemPropertiesGet("vold.decrypt"))){
+ if (StorageManager.inCryptKeeperBounce()) {
return;
}
if (!TextUtils.isEmpty(mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT))) {
- Slog.wtf(LOG_TAG, "Trying to set ro.device_owner, but it has already been set?");
+ Slog.w(LOG_TAG, "Trying to set ro.device_owner, but it has already been set?");
} else {
if (mOwners.hasDeviceOwner()) {
mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "true");
+ Slog.i(LOG_TAG, "Set ro.device_owner property to true");
disableDeviceLoggingIfNotCompliant();
if (mInjector.securityLogGetLoggingEnabledProperty()) {
mSecurityLogMonitor.start();
}
} else {
mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "false");
+ Slog.i(LOG_TAG, "Set ro.device_owner property to false");
}
}
}
@@ -2624,7 +2657,7 @@
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
- } catch (RuntimeException e) {
+ } catch (RuntimeException | AssertionError e) {
Log.e(LOG_TAG, "Could not connect to KeyChain service", e);
}
if (!hasCert) {
@@ -2853,13 +2886,9 @@
@Override
public boolean isSeparateProfileChallengeAllowed(int userHandle) {
ComponentName profileOwner = getProfileOwner(userHandle);
- try {
- // Profile challenge is supported on N or newer release.
- return profileOwner != null &&
- getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M;
- } catch (RemoteException e) {
- return false;
- }
+ // Profile challenge is supported on N or newer release.
+ return profileOwner != null &&
+ getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M;
}
@Override
@@ -3628,7 +3657,7 @@
if (count == 0 ||
count > admin.maximumFailedPasswordsForWipe ||
(count == admin.maximumFailedPasswordsForWipe &&
- mUserManager.getUserInfo(userId).isPrimary())) {
+ getUserInfo(userId).isPrimary())) {
count = admin.maximumFailedPasswordsForWipe;
strictestAdmin = admin;
}
@@ -3636,6 +3665,15 @@
return strictestAdmin;
}
+ private UserInfo getUserInfo(@UserIdInt int userId) {
+ final long token = mInjector.binderClearCallingIdentity();
+ try {
+ return mUserManager.getUserInfo(userId);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(token);
+ }
+ }
+
@Override
public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
if (!mHasFeature) {
@@ -4214,15 +4252,12 @@
int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- try {
- if (getTargetSdk(who.getPackageName(), userHandle) >= Build.VERSION_CODES.N) {
- if (installerPackage != null &&
- !isPackageInstalledForUser(installerPackage, userHandle)) {
- throw new IllegalArgumentException("Package " + installerPackage
- + " is not installed on the current user");
- }
+ if (getTargetSdk(who.getPackageName(), userHandle) >= Build.VERSION_CODES.N) {
+ if (installerPackage != null &&
+ !isPackageInstalledForUser(installerPackage, userHandle)) {
+ throw new IllegalArgumentException("Package " + installerPackage
+ + " is not installed on the current user");
}
- } catch (RemoteException e) {
}
DevicePolicyData policy = getUserData(userHandle);
policy.mDelegatedCertInstallerPackage = installerPackage;
@@ -4813,12 +4848,23 @@
* Get the current encryption status of the device.
*/
@Override
- public int getStorageEncryptionStatus(int userHandle) {
+ public int getStorageEncryptionStatus(@Nullable String callerPackage, int userHandle) {
if (!mHasFeature) {
// Ok to return current status.
}
enforceFullCrossUsersPermission(userHandle);
- return getEncryptionStatus();
+
+ // It's not critical here, but let's make sure the package name is correct, in case
+ // we start using it for different purposes.
+ ensureCallerPackage(callerPackage);
+
+ final int rawStatus = getEncryptionStatus();
+ if ((rawStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER)
+ && (callerPackage != null)
+ && (getTargetSdk(callerPackage, userHandle) <= VERSION_CODES.M)) {
+ return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
+ }
+ return rawStatus;
}
/**
@@ -4836,21 +4882,18 @@
* Hook to low-levels: Reporting the current status of encryption.
* @return A value such as {@link DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED},
* {@link DevicePolicyManager#ENCRYPTION_STATUS_INACTIVE},
- * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY}, or
+ * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY},
+ * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_PER_USER}, or
* {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}.
*/
private int getEncryptionStatus() {
- String status = mInjector.systemPropertiesGet("ro.crypto.state", "unsupported");
- if ("encrypted".equalsIgnoreCase(status)) {
- final long token = mInjector.binderClearCallingIdentity();
- try {
- return LockPatternUtils.isDeviceEncrypted()
- ? DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
- : DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY;
- } finally {
- mInjector.binderRestoreCallingIdentity(token);
- }
- } else if ("unencrypted".equalsIgnoreCase(status)) {
+ if (mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
+ return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
+ } else if (mInjector.storageManagerIsNonDefaultBlockEncrypted()) {
+ return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
+ } else if (mInjector.storageManagerIsEncrypted()) {
+ return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY;
+ } else if (mInjector.storageManagerIsEncryptable()) {
return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
} else {
return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
@@ -4863,7 +4906,6 @@
private void setEncryptionRequested(boolean encrypt) {
}
-
/**
* Set whether the screen capture is disabled for the user managed by the specified admin.
*/
@@ -5068,9 +5110,9 @@
mRemoteBugreportServiceIsActive.set(true);
mRemoteBugreportSharingAccepted.set(false);
registerRemoteBugreportReceivers();
- mInjector.getNotificationManager().notify(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
- RemoteBugreportUtils.NOTIFICATION_BUGREPORT_STARTED));
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
mHandler.postDelayed(mRemoteBugreportTimeoutRunnable,
RemoteBugreportUtils.REMOTE_BUGREPORT_TIMEOUT_MILLIS);
return true;
@@ -5104,7 +5146,7 @@
private void registerRemoteBugreportReceivers() {
try {
IntentFilter filterFinished = new IntentFilter(
- RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_DISPATCH,
+ DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH,
RemoteBugreportUtils.BUGREPORT_MIMETYPE);
mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
} catch (IntentFilter.MalformedMimeTypeException e) {
@@ -5112,8 +5154,8 @@
Slog.w(LOG_TAG, "Failed to set type " + RemoteBugreportUtils.BUGREPORT_MIMETYPE, e);
}
IntentFilter filterConsent = new IntentFilter();
- filterConsent.addAction(RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_DECLINED);
- filterConsent.addAction(RemoteBugreportUtils.ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED);
+ filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
+ filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
}
@@ -5126,16 +5168,17 @@
bugreportUriString = bugreportUri.toString();
}
String bugreportHash = intent.getStringExtra(
- RemoteBugreportUtils.EXTRA_REMOTE_BUGREPORT_HASH);
+ DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH);
if (mRemoteBugreportSharingAccepted.get()) {
shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
mInjector.getNotificationManager().cancel(LOG_TAG,
RemoteBugreportUtils.NOTIFICATION_ID);
} else {
setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash);
- mInjector.getNotificationManager().notify(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
- RemoteBugreportUtils.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED));
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
+ UserHandle.ALL);
}
mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
}
@@ -5166,9 +5209,10 @@
if (bugreportUriString != null) {
shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
} else if (mRemoteBugreportServiceIsActive.get()) {
- mInjector.getNotificationManager().notify(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
+ mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
RemoteBugreportUtils.buildNotification(mContext,
- RemoteBugreportUtils.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED));
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED),
+ UserHandle.ALL);
}
}
@@ -5897,7 +5941,7 @@
* - adb if there are not accounts.
*/
private void enforceCanSetProfileOwnerLocked(int userHandle) {
- UserInfo info = mUserManager.getUserInfo(userHandle);
+ UserInfo info = getUserInfo(userHandle);
if (info == null) {
// User doesn't exist.
throw new IllegalArgumentException(
@@ -5934,52 +5978,40 @@
/**
* The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
* permission.
- * The device owner can only be set before the setup phase of the primary user has completed,
- * except for adb if no accounts or additional users are present on the device.
*/
private void enforceCanSetDeviceOwnerLocked(int userId) {
- if (mOwners.hasDeviceOwner()) {
- throw new IllegalStateException("Trying to set the device owner, but device owner "
- + "is already set.");
- }
- if (mOwners.hasProfileOwner(userId)) {
- throw new IllegalStateException("Trying to set the device owner, but the user already "
- + "has a profile owner.");
- }
- if (!mUserManager.isUserRunning(new UserHandle(userId))) {
- throw new IllegalStateException("User not running: " + userId);
- }
-
int callingUid = mInjector.binderGetCallingUid();
- if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
- if (!hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
- return;
- }
- // STOPSHIP Do proper check in split user mode
- if (!mInjector.userManagerIsSplitSystemUser()) {
- if (mUserManager.getUserCount() > 1) {
- throw new IllegalStateException(
- "Not allowed to set the device owner because there "
- + "are already several users on the device");
- }
- if (AccountManager.get(mContext).getAccounts().length > 0) {
- throw new IllegalStateException(
- "Not allowed to set the device owner because there "
- + "are already some accounts on the device");
- }
- }
- return;
+ boolean isAdb = callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
+ if (!isAdb) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
}
- // STOPSHIP check the caller UID with userId
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
- // STOPSHIP Do proper check in split user mode
- if (!mInjector.userManagerIsSplitSystemUser()) {
- if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
- throw new IllegalStateException("Cannot set the device owner if the device is "
- + "already set-up");
- }
+ final int code = checkSetDeviceOwnerPreCondition(userId, isAdb);
+ switch (code) {
+ case CODE_OK:
+ return;
+ case CODE_HAS_DEVICE_OWNER:
+ throw new IllegalStateException(
+ "Trying to set the device owner, but device owner is already set.");
+ case CODE_USER_HAS_PROFILE_OWNER:
+ throw new IllegalStateException("Trying to set the device owner, but the user "
+ + "already has a profile owner.");
+ case CODE_USER_NOT_RUNNING:
+ throw new IllegalStateException("User not running: " + userId);
+ case CODE_NOT_SYSTEM_USER:
+ throw new IllegalStateException("User is not system user");
+ case CODE_USER_SETUP_COMPLETED:
+ throw new IllegalStateException(
+ "Cannot set the device owner if the device is already set-up");
+ case CODE_NONSYSTEM_USER_EXISTS:
+ throw new IllegalStateException("Not allowed to set the device owner because there "
+ + "are already several users on the device");
+ case CODE_ACCOUNTS_NOT_EMPTY:
+ throw new IllegalStateException("Not allowed to set the device owner because there "
+ + "are already some accounts on the device");
+ default:
+ throw new IllegalStateException("Unknown @DeviceOwnerPreConditionCode " + code);
}
}
@@ -6031,6 +6063,23 @@
}
}
+ private void ensureCallerPackage(@Nullable String packageName) {
+ if (packageName == null) {
+ Preconditions.checkState(isCallerWithSystemUid(),
+ "Only caller can omit package name");
+ } else {
+ final int callingUid = mInjector.binderGetCallingUid();
+ final int userId = mInjector.userHandleGetCallingUserId();
+ try {
+ final ApplicationInfo ai = mIPackageManager.getApplicationInfo(
+ packageName, 0, userId);
+ Preconditions.checkState(ai.uid == callingUid, "Unmatching package name");
+ } catch (RemoteException e) {
+ // Shouldn't happen
+ }
+ }
+ }
+
private boolean isCallerWithSystemUid() {
return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
}
@@ -6061,12 +6110,7 @@
}
private boolean isManagedProfile(int userHandle) {
- long ident = mInjector.binderClearCallingIdentity();
- try {
- return mUserManager.getUserInfo(userHandle).isManagedProfile();
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
+ return getUserInfo(userHandle).isManagedProfile();
}
private void enableIfNecessary(String packageName, int userId) {
@@ -6121,6 +6165,27 @@
pw.println(" ");
pw.print(" mPasswordOwner="); pw.println(policy.mPasswordOwner);
}
+ pw.println();
+ pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
+ }
+ }
+
+ private String getEncryptionStatusName(int encryptionStatus) {
+ switch (encryptionStatus) {
+ case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE:
+ return "inactive";
+ case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY:
+ return "block default key";
+ case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
+ return "block";
+ case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER:
+ return "per-user";
+ case DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED:
+ return "unsupported";
+ case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING:
+ return "activating";
+ default:
+ return "unknown";
}
}
@@ -6405,7 +6470,7 @@
try {
// If we have an enabled packages list for a managed profile the packages
// we should check are installed for the parent user.
- UserInfo user = mUserManager.getUserInfo(userIdToCheck);
+ UserInfo user = getUserInfo(userIdToCheck);
if (user.isManagedProfile()) {
userIdToCheck = user.profileGroupId;
}
@@ -6451,7 +6516,7 @@
List<AccessibilityServiceInfo> enabledServices = null;
long id = mInjector.binderClearCallingIdentity();
try {
- UserInfo user = mUserManager.getUserInfo(userId);
+ UserInfo user = getUserInfo(userId);
if (user.isManagedProfile()) {
userId = user.profileGroupId;
}
@@ -6533,7 +6598,7 @@
if (result != null) {
long id = mInjector.binderClearCallingIdentity();
try {
- UserInfo user = mUserManager.getUserInfo(userId);
+ UserInfo user = getUserInfo(userId);
if (user.isManagedProfile()) {
userId = user.profileGroupId;
}
@@ -6587,7 +6652,7 @@
long token = mInjector.binderClearCallingIdentity();
try {
UserInfo currentUser;
- UserInfo callingUser = mUserManager.getUserInfo(callingUserId);
+ UserInfo callingUser = getUserInfo(callingUserId);
try {
currentUser = mInjector.getIActivityManager().getCurrentUser();
} catch (RemoteException e) {
@@ -7126,7 +7191,7 @@
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
- parentUserId);
+ parentUserId).getList();
if (VERBOSE_LOG) {
Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
@@ -7810,6 +7875,26 @@
listener.onCrossProfileWidgetProvidersChanged(userId, packages);
}
}
+
+ @Override
+ public boolean hasDeviceOwnerOrProfileOwner(String packageName, int userId) {
+ if (!mHasFeature || packageName == null) {
+ return false;
+ }
+ if (userId < 0) {
+ throw new UnsupportedOperationException("userId should be >= 0");
+ }
+ synchronized (DevicePolicyManagerService.this) {
+ if (packageName.equals(mOwners.getProfileOwnerPackage(userId))) {
+ return true;
+ }
+ if (userId == mOwners.getDeviceOwnerUserId()
+ && packageName.equals(mOwners.getDeviceOwnerPackageName())) {
+ return true;
+ }
+ }
+ return false;
+ }
}
/**
@@ -8036,6 +8121,10 @@
@Override
public boolean isProvisioningAllowed(String action) {
+ if (!mHasFeature) {
+ return false;
+ }
+
final int callingUserId = mInjector.userHandleGetCallingUserId();
if (DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE.equals(action)) {
if (!hasFeatureManagedUsers()) {
@@ -8100,23 +8189,55 @@
throw new IllegalArgumentException("Unknown provisioning action " + action);
}
- private boolean isDeviceOwnerProvisioningAllowed(int callingUserId) {
- synchronized (this) {
- if (mOwners.hasDeviceOwner()) {
- return false;
+ /*
+ * The device owner can only be set before the setup phase of the primary user has completed,
+ * except for adb command if no accounts or additional users are present on the device.
+ */
+ private synchronized @DeviceOwnerPreConditionCode int checkSetDeviceOwnerPreCondition(
+ int deviceOwnerUserId, boolean isAdb) {
+ if (mOwners.hasDeviceOwner()) {
+ return CODE_HAS_DEVICE_OWNER;
+ }
+ if (mOwners.hasProfileOwner(deviceOwnerUserId)) {
+ return CODE_USER_HAS_PROFILE_OWNER;
+ }
+ if (!mUserManager.isUserRunning(new UserHandle(deviceOwnerUserId))) {
+ return CODE_USER_NOT_RUNNING;
+ }
+ if (isAdb) {
+ // if shell command runs after user setup completed check device status. Otherwise, OK.
+ if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
+ if (mUserManager.getUserCount() > 1) {
+ return CODE_NONSYSTEM_USER_EXISTS;
+ }
+ if (AccountManager.get(mContext).getAccounts().length > 0) {
+ return CODE_ACCOUNTS_NOT_EMPTY;
+ }
+ } else {
+ // STOPSHIP Do proper check in split user mode
+ }
}
+ return CODE_OK;
+ } else {
+ if (!mInjector.userManagerIsSplitSystemUser()) {
+ // In non-split user mode, DO has to be user 0
+ if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
+ return CODE_NOT_SYSTEM_USER;
+ }
+ // In non-split user mode, only provision DO before setup wizard completes
+ if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
+ return CODE_USER_SETUP_COMPLETED;
+ }
+ } else {
+ // STOPSHIP Do proper check in split user mode
+ }
+ return CODE_OK;
}
- if (getProfileOwner(callingUserId) != null) {
- return false;
- }
- if (mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0) {
- return false;
- }
- if (callingUserId != UserHandle.USER_SYSTEM) {
- // Device owner provisioning can only be initiated from system user.
- return false;
- }
- return true;
+ }
+
+ private boolean isDeviceOwnerProvisioningAllowed(int deviceOwnerUserId) {
+ return CODE_OK == checkSetDeviceOwnerPreCondition(deviceOwnerUserId, /* isAdb */ false);
}
private boolean hasFeatureManagedUsers() {
@@ -8128,10 +8249,10 @@
}
@Override
- public String getWifiMacAddress() {
+ public String getWifiMacAddress(ComponentName admin) {
// Make sure caller has DO.
synchronized (this) {
- getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
}
final long ident = mInjector.binderClearCallingIdentity();
@@ -8150,11 +8271,16 @@
* Returns the target sdk version number that the given packageName was built for
* in the given user.
*/
- private int getTargetSdk(String packageName, int userId) throws RemoteException {
- final ApplicationInfo ai = mIPackageManager
- .getApplicationInfo(packageName, 0, userId);
- final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
- return targetSdkVersion;
+ private int getTargetSdk(String packageName, int userId) {
+ final ApplicationInfo ai;
+ try {
+ ai = mIPackageManager.getApplicationInfo(packageName, 0, userId);
+ final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
+ return targetSdkVersion;
+ } catch (RemoteException e) {
+ // Shouldn't happen
+ return 0;
+ }
}
@Override
@@ -8163,13 +8289,7 @@
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
}
final int callingUserId = mInjector.userHandleGetCallingUserId();
- final UserInfo user;
- long ident = mInjector.binderClearCallingIdentity();
- try {
- user = mUserManager.getUserInfo(callingUserId);
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
+ final UserInfo user = getUserInfo(callingUserId);
return user != null && user.isManagedProfile();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 68fd0f6..b316cbd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -494,7 +494,6 @@
abstract void writeInner(XmlSerializer out) throws IOException;
abstract boolean readInner(XmlPullParser parser, int depth, String tag);
-
}
private class DeviceOwnerReadWriter extends FileReadWriter {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
index 117ba15..6d42dc9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
@@ -19,8 +19,12 @@
import android.annotation.IntDef;
import android.app.Notification;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.text.format.DateUtils;
import com.android.internal.R;
@@ -37,79 +41,62 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- NOTIFICATION_BUGREPORT_STARTED,
- NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
- NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED,
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
+ DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
})
@interface RemoteBugreportNotificationType {}
- static final int NOTIFICATION_BUGREPORT_STARTED = 1;
- static final int NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED = 2;
- static final int NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED = 3;
static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
static final String CTL_STOP = "ctl.stop";
static final String REMOTE_BUGREPORT_SERVICE = "bugreportremote";
- static final String ACTION_REMOTE_BUGREPORT_DISPATCH =
- "android.intent.action.REMOTE_BUGREPORT_DISPATCH";
- static final String ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED =
- "com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED";
- static final String ACTION_REMOTE_BUGREPORT_SHARING_DECLINED =
- "com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED";
- static final String EXTRA_REMOTE_BUGREPORT_HASH = "android.intent.extra.REMOTE_BUGREPORT_HASH";
-
static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
static Notification buildNotification(Context context,
@RemoteBugreportNotificationType int type) {
+ Intent dialogIntent = new Intent(Settings.ACTION_SHOW_REMOTE_BUGREPORT_DIALOG);
+ dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ dialogIntent.putExtra(DevicePolicyManager.EXTRA_BUGREPORT_NOTIFICATION_TYPE, type);
+ PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(context, type,
+ dialogIntent, 0, null, UserHandle.CURRENT);
+
Notification.Builder builder = new Notification.Builder(context)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setOngoing(true)
.setLocalOnly(true)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setContentIntent(pendingDialogIntent)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color));
- if (type == NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
+ if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
builder.setContentTitle(context.getString(
- R.string.sharing_remote_bugreport_notification_title))
- .setContentText(context.getString(
- R.string.sharing_remote_bugreport_notification_message))
- .setPriority(Notification.PRIORITY_HIGH)
- .setProgress(0, 0, true)
- .setStyle(new Notification.BigTextStyle().bigText(context.getString(
- R.string.sharing_remote_bugreport_notification_message)));
- } else {
+ R.string.sharing_remote_bugreport_notification_title))
+ .setProgress(0, 0, true);
+ } else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED) {
+ builder.setContentTitle(context.getString(
+ R.string.taking_remote_bugreport_notification_title))
+ .setProgress(0, 0, true);
+ } else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
- new Intent(ACTION_REMOTE_BUGREPORT_SHARING_ACCEPTED),
+ new Intent(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED),
PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(context,
- NOTIFICATION_ID, new Intent(ACTION_REMOTE_BUGREPORT_SHARING_DECLINED),
+ NOTIFICATION_ID, new Intent(
+ DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED),
PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(new Notification.Action.Builder(null /* icon */, context.getString(
- R.string.share_remote_bugreport_notification_decline),
- pendingIntentDecline).build())
+ R.string.decline_remote_bugreport_action), pendingIntentDecline).build())
.addAction(new Notification.Action.Builder(null /* icon */, context.getString(
- R.string.share_remote_bugreport_notification_accept),
- pendingIntentAccept).build())
+ R.string.share_remote_bugreport_action), pendingIntentAccept).build())
.setContentTitle(context.getString(
- R.string.share_remote_bugreport_notification_title));
-
- if (type == NOTIFICATION_BUGREPORT_STARTED) {
- builder.setContentText(context.getString(
- R.string.share_remote_bugreport_notification_message))
- .setStyle(new Notification.BigTextStyle().bigText(context.getString(
- R.string.share_remote_bugreport_notification_message)))
- .setProgress(0, 0, true)
- .setPriority(Notification.PRIORITY_MAX)
- .setVibrate(new long[0]);
- } else if (type == NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
- builder.setContentText(context.getString(
- R.string.share_finished_remote_bugreport_notification_message))
- .setStyle(new Notification.BigTextStyle().bigText(context.getString(
- R.string.share_finished_remote_bugreport_notification_message)))
- .setPriority(Notification.PRIORITY_HIGH);
- }
+ R.string.share_remote_bugreport_notification_title))
+ .setContentText(context.getString(
+ R.string.share_remote_bugreport_notification_message_finished))
+ .setStyle(new Notification.BigTextStyle().bigText(context.getString(
+ R.string.share_remote_bugreport_notification_message_finished)));
}
return builder.build();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9aa2b94..e7daaa1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -83,6 +83,7 @@
import com.android.server.pm.LauncherAppsService;
import com.android.server.pm.OtaDexoptService;
import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
@@ -431,6 +432,24 @@
mPackageManager = mSystemContext.getPackageManager();
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename
+ // A/B artifacts after boot, before anything else might touch/need them.
+ // Note: this isn't needed during decryption (we don't have /data anyways).
+ if (!mOnlyCore) {
+ boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
+ false);
+ if (!disableOtaDexopt) {
+ traceBeginAndSlog("StartOtaDexOptService");
+ try {
+ OtaDexoptService.main(mSystemContext, mPackageManagerService);
+ } catch (Throwable e) {
+ reportWtf("starting OtaDexOptService", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ }
+ }
+ }
+
traceBeginAndSlog("StartUserManagerService");
ServiceManager.addService(Context.USER_SERVICE, UserManagerService.getInstance());
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -617,7 +636,6 @@
WallpaperManagerService wallpaper = null;
LocationManagerService location = null;
CountryDetectorService countryDetector = null;
- TextServicesManagerService tsms = null;
ILockSettings lockSettings = null;
AssetAtlasService atlas = null;
MediaRouterService mediaRouter = null;
@@ -744,14 +762,7 @@
}
if (!disableNonCoreServices) {
- traceBeginAndSlog("StartTextServicesManagerService");
- try {
- tsms = new TextServicesManagerService(context);
- ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
- } catch (Throwable e) {
- reportWtf("starting Text Service Manager Service", e);
- }
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
}
if (!disableNetwork) {
@@ -1124,20 +1135,9 @@
reportWtf("starting BackgroundDexOptService", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-
- // Manages A/B OTA dexopting.
- boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
- false);
- if (!disableOtaDexopt) {
- traceBeginAndSlog("StartOtaDexOptService");
- try {
- OtaDexoptService.main(mSystemContext, mPackageManagerService);
- } catch (Throwable e) {
- reportWtf("starting BackgroundDexOptService", e);
- }
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- }
}
+ // LauncherAppsService uses ShortcutService.
+ mSystemServiceManager.startService(ShortcutService.Lifecycle.class);
mSystemServiceManager.startService(LauncherAppsService.class);
}
@@ -1251,7 +1251,6 @@
final CountryDetectorService countryDetectorF = countryDetector;
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService;
- final TextServicesManagerService textServiceManagerServiceF = tsms;
final StatusBarManagerService statusBarF = statusBar;
final AssetAtlasService atlasF = atlas;
final InputManagerService inputManagerF = inputManager;
@@ -1367,12 +1366,6 @@
reportWtf("Notifying CommonTimeManagementService running", e);
}
try {
- if (textServiceManagerServiceF != null)
- textServiceManagerServiceF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying TextServicesManagerService running", e);
- }
- try {
if (atlasF != null) atlasF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying AssetAtlasService running", e);
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 4f99bff..e2562cd 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -84,10 +84,10 @@
public class DhcpClient extends StateMachine {
private static final String TAG = "DhcpClient";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean STATE_DBG = false;
private static final boolean MSG_DBG = false;
- private static final boolean PACKET_DBG = true;
+ private static final boolean PACKET_DBG = false;
// Timers and timeouts.
private static final int SECONDS = 1000;
@@ -342,14 +342,14 @@
@Override
public void run() {
- maybeLog("Receive thread started");
+ if (DBG) Log.d(TAG, "Receive thread started");
while (!stopped) {
int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
try {
length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
DhcpPacket packet = null;
packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
- maybeLog("Received packet: " + packet);
+ if (DBG) Log.d(TAG, "Received packet: " + packet);
sendMessage(CMD_RECEIVED_PACKET, packet);
} catch (IOException|ErrnoException e) {
if (!stopped) {
@@ -362,7 +362,7 @@
}
}
}
- maybeLog("Receive thread stopped");
+ if (DBG) Log.d(TAG, "Receive thread stopped");
}
}
@@ -373,12 +373,12 @@
private boolean transmitPacket(ByteBuffer buf, String description, Inet4Address to) {
try {
if (to.equals(INADDR_BROADCAST)) {
- maybeLog("Broadcasting " + description);
+ if (DBG) Log.d(TAG, "Broadcasting " + description);
Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
} else {
// It's safe to call getpeername here, because we only send unicast packets if we
// have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter.
- maybeLog("Unicasting " + description + " to " + Os.getpeername(mUdpSock));
+ if (DBG) Log.d(TAG, "Unicasting " + description + " to " + Os.getpeername(mUdpSock));
Os.write(mUdpSock, buf);
}
} catch(ErrnoException|IOException e) {
@@ -454,10 +454,6 @@
mController.sendMessage(CMD_ON_QUIT);
}
- private void maybeLog(String msg) {
- if (DBG) Log.d(TAG, msg);
- }
-
abstract class LoggingState extends State {
public void enter() {
if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
@@ -592,7 +588,7 @@
transitionTo(mStoppedState);
return HANDLED;
case CMD_ONESHOT_TIMEOUT:
- maybeLog("Timed out");
+ if (DBG) Log.d(TAG, "Timed out");
notifyFailure();
return HANDLED;
default:
@@ -790,7 +786,7 @@
@Override
public void exit() {
- maybeLog("Clearing IP address");
+ if (DBG) Log.d(TAG, "Clearing IP address");
setIpAddress(new LinkAddress("0.0.0.0/0"));
}
}
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 6a255e5..e27f69e 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -819,7 +819,11 @@
// server-to-server packets, e.g. for relays.
if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) &&
!isPacketServerToServer(udpSrcPort, udpDstPort)) {
- return null;
+ // This should almost never happen because we use SO_ATTACH_FILTER on the packet
+ // socket to drop packets that don't have the right source ports. However, it's
+ // possible that a packet arrives between when the socket is bound and when the
+ // filter is set. http://b/26696823 .
+ throw new ParseException("Unexpected UDP ports %d->%d", udpSrcPort, udpDstPort);
}
}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index afae956..c7c5015 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -63,7 +63,7 @@
* @hide
*/
public class IpManager extends StateMachine {
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
// For message logging.
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 88155f7..5b4fd50 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -132,7 +132,7 @@
*/
public class IpReachabilityMonitor {
private static final String TAG = "IpReachabilityMonitor";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
public interface Callback {
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index e6f4177..8cfc4a3 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -41,13 +41,16 @@
import android.print.IPrintDocumentAdapter;
import android.print.IPrintJobStateChangeListener;
import android.print.IPrintManager;
+import android.print.IPrintServicesChangeListener;
import android.print.IPrinterDiscoveryObserver;
import android.print.PrintAttributes;
import android.print.PrintJobId;
import android.print.PrintJobInfo;
+import android.print.PrintManager;
import android.print.PrinterId;
import android.printservice.PrintServiceInfo;
import android.provider.Settings;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.content.PackageMonitor;
@@ -66,6 +69,8 @@
* PrintManager implementation is contained within.
*/
public final class PrintManagerService extends SystemService {
+ private static final String LOG_TAG = "PrintManagerService";
+
private final PrintManagerImpl mPrintManagerImpl;
public PrintManagerService(Context context) {
@@ -253,7 +258,10 @@
}
@Override
- public List<PrintServiceInfo> getEnabledPrintServices(int userId) {
+ public List<PrintServiceInfo> getPrintServices(int selectionFlags, int userId) {
+ Preconditions.checkFlagsArgument(selectionFlags,
+ PrintManager.DISABLED_SERVICES | PrintManager.ENABLED_SERVICES);
+
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
final UserState userState;
synchronized (mLock) {
@@ -262,34 +270,44 @@
return null;
}
userState = getOrCreateUserStateLocked(resolvedUserId);
-
- // The user state might be updated via the same observer-set as the caller of this
- // interface. If the caller is called back first the user state is not yet updated
- // and the user gets and inconsistent view. Hence force an update.
- userState.updateIfNeededLocked();
}
final long identity = Binder.clearCallingIdentity();
try {
- return userState.getEnabledPrintServices();
+ return userState.getPrintServices(selectionFlags);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public List<PrintServiceInfo> getInstalledPrintServices(int userId) {
+ public void setPrintServiceEnabled(ComponentName service, boolean isEnabled, int userId) {
final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final int appId = UserHandle.getAppId(Binder.getCallingUid());
+
+ try {
+ if (appId != Process.SYSTEM_UID && appId != UserHandle.getAppId(
+ mContext.getPackageManager().getPackageUidAsUser(
+ PrintManager.PRINT_SPOOLER_PACKAGE_NAME, resolvedUserId))) {
+ throw new SecurityException("Only system and print spooler can call this");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Could not verify caller", e);
+ return;
+ }
+
+ service = Preconditions.checkNotNull(service);
+
final UserState userState;
synchronized (mLock) {
- // Only the current group members can get installed services.
+ // Only the current group members can enable / disable services.
if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
- return null;
+ return;
}
userState = getOrCreateUserStateLocked(resolvedUserId);
}
final long identity = Binder.clearCallingIdentity();
try {
- return userState.getInstalledPrintServices();
+ userState.setPrintServiceEnabled(service, isEnabled);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -496,6 +514,50 @@
}
@Override
+ public void addPrintServicesChangeListener(IPrintServicesChangeListener listener,
+ int userId) throws RemoteException {
+ listener = Preconditions.checkNotNull(listener);
+
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ synchronized (mLock) {
+ // Only the current group members can add a print services listener.
+ if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
+ return;
+ }
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ userState.addPrintServicesChangeListener(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void removePrintServicesChangeListener(IPrintServicesChangeListener listener,
+ int userId) {
+ listener = Preconditions.checkNotNull(listener);
+
+ final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+ final UserState userState;
+ synchronized (mLock) {
+ // Only the current group members can remove a print job listener.
+ if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
+ return;
+ }
+ userState = getOrCreateUserStateLocked(resolvedUserId);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ userState.removePrintServicesChangeListener(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
fd = Preconditions.checkNotNull(fd);
pw = Preconditions.checkNotNull(pw);
@@ -550,43 +612,79 @@
private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
- private void updateServices(String packageName) {
- if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
- synchronized (mLock) {
- // A background user/profile's print jobs are running but there is
- // no UI shown. Hence, if the packages of such a user change we need
- // to handle it as the change may affect ongoing print jobs.
- boolean servicesChanged = false;
- UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ /**
+ * Checks if the package contains a print service.
+ *
+ * @param packageName The name of the package
+ *
+ * @return true iff the package contains a print service
+ */
+ private boolean hasPrintService(String packageName) {
+ Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
+ intent.setPackage(packageName);
- List<PrintServiceInfo> installedServices = userState
- .getInstalledPrintServices();
- final int numInstalledServices = installedServices.size();
- for (int i = 0; i < numInstalledServices; i++) {
- if (installedServices.get(i).getResolveInfo().serviceInfo.packageName
- .equals(packageName)) {
- servicesChanged = true;
- break;
- }
- }
- if (servicesChanged) {
- userState.updateIfNeededLocked();
+ List<ResolveInfo> installedServices = mContext.getPackageManager()
+ .queryIntentServicesAsUser(intent,
+ GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING,
+ getChangingUserId());
+
+ return installedServices != null && !installedServices.isEmpty();
+ }
+
+ /**
+ * Checks if there is a print service currently registered for this package.
+ *
+ * @param userState The userstate for the current user
+ * @param packageName The name of the package
+ *
+ * @return true iff the package contained (and might still contain) a print service
+ */
+ private boolean hadPrintService(@NonNull UserState userState, String packageName) {
+ List<PrintServiceInfo> installedServices = userState
+ .getPrintServices(PrintManager.ALL_SERVICES);
+
+ if (installedServices == null) {
+ return false;
+ }
+
+ final int numInstalledServices = installedServices.size();
+ for (int i = 0; i < numInstalledServices; i++) {
+ if (installedServices.get(i).getResolveInfo().serviceInfo.packageName
+ .equals(packageName)) {
+ return true;
}
}
+
+ return false;
}
@Override
public void onPackageModified(String packageName) {
if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
- updateServices(packageName);
- getOrCreateUserStateLocked(getChangingUserId()).prunePrintServices();
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+
+ synchronized (mLock) {
+ if (hadPrintService(userState, packageName)
+ || hasPrintService(packageName)) {
+ userState.updateIfNeededLocked();
+ }
+ }
+
+ userState.prunePrintServices();
}
@Override
public void onPackageRemoved(String packageName, int uid) {
if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
- updateServices(packageName);
- getOrCreateUserStateLocked(getChangingUserId()).prunePrintServices();
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+
+ synchronized (mLock) {
+ if (hadPrintService(userState, packageName)) {
+ userState.updateIfNeededLocked();
+ }
+ }
+
+ userState.prunePrintServices();
}
@Override
@@ -601,7 +699,7 @@
boolean stoppedSomePackages = false;
List<PrintServiceInfo> enabledServices = userState
- .getEnabledPrintServices();
+ .getPrintServices(PrintManager.ENABLED_SERVICES);
if (enabledServices == null) {
return false;
}
@@ -630,21 +728,11 @@
@Override
public void onPackageAdded(String packageName, int uid) {
if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
-
- // A background user/profile's print jobs are running but there is
- // no UI shown. Hence, if the packages of such a user change we need
- // to handle it as the change may affect ongoing print jobs.
- Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
- intent.setPackage(packageName);
-
- List<ResolveInfo> installedServices = mContext.getPackageManager()
- .queryIntentServicesAsUser(intent,
- GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING,
- getChangingUserId());
-
- if (installedServices != null) {
- UserState userState = getOrCreateUserStateLocked(getChangingUserId());
- userState.updateIfNeededLocked();
+ synchronized (mLock) {
+ if (hasPrintService(packageName)) {
+ UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+ userState.updateIfNeededLocked();
+ }
}
}
};
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index d179b95..e1d8c6c 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -36,6 +36,7 @@
import android.print.IPrintSpoolerClient;
import android.print.PrintJobId;
import android.print.PrintJobInfo;
+import android.print.PrintManager;
import android.print.PrinterId;
import android.printservice.PrintService;
import android.util.Slog;
@@ -115,8 +116,8 @@
mCallbacks = callbacks;
mClient = new PrintSpoolerClient(this);
mIntent = new Intent();
- mIntent.setComponent(new ComponentName("com.android.printspooler",
- "com.android.printspooler.model.PrintSpoolerService"));
+ mIntent.setComponent(new ComponentName(PrintManager.PRINT_SPOOLER_PACKAGE_NAME,
+ PrintManager.PRINT_SPOOLER_PACKAGE_NAME + ".model.PrintSpoolerService"));
}
public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state,
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index fcf2fc8..f2f555b 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -44,6 +44,7 @@
import android.os.UserHandle;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintJobStateChangeListener;
+import android.print.IPrintServicesChangeListener;
import android.print.IPrinterDiscoveryObserver;
import android.print.PrintAttributes;
import android.print.PrintJobId;
@@ -121,6 +122,8 @@
private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords;
+ private List<PrintServicesChangeListenerRecord> mPrintServicesChangeListenerRecords;
+
private boolean mDestroyed;
public UserState(Context context, int userId, Object lock) {
@@ -342,29 +345,63 @@
mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null);
}
- public @Nullable List<PrintServiceInfo> getEnabledPrintServices() {
+ public @Nullable List<PrintServiceInfo> getPrintServices(int selectionFlags) {
synchronized (mLock) {
- List<PrintServiceInfo> enabledServices = null;
+ List<PrintServiceInfo> selectedServices = null;
final int installedServiceCount = mInstalledServices.size();
for (int i = 0; i < installedServiceCount; i++) {
PrintServiceInfo installedService = mInstalledServices.get(i);
+
ComponentName componentName = new ComponentName(
installedService.getResolveInfo().serviceInfo.packageName,
installedService.getResolveInfo().serviceInfo.name);
- if (mActiveServices.containsKey(componentName)) {
- if (enabledServices == null) {
- enabledServices = new ArrayList<PrintServiceInfo>();
+
+ // Update isEnabled under the same lock the final returned list is created
+ installedService.setIsEnabled(mActiveServices.containsKey(componentName));
+
+ if (installedService.isEnabled()) {
+ if ((selectionFlags & PrintManager.ENABLED_SERVICES) == 0) {
+ continue;
}
- enabledServices.add(installedService);
+ } else {
+ if ((selectionFlags & PrintManager.DISABLED_SERVICES) == 0) {
+ continue;
+ }
}
+
+ if (selectedServices == null) {
+ selectedServices = new ArrayList<>();
+ }
+ selectedServices.add(installedService);
}
- return enabledServices;
+ return selectedServices;
}
}
- public List<PrintServiceInfo> getInstalledPrintServices() {
+ public void setPrintServiceEnabled(@NonNull ComponentName serviceName, boolean isEnabled) {
synchronized (mLock) {
- return mInstalledServices;
+ boolean isChanged = false;
+ if (isEnabled) {
+ isChanged = mDisabledServices.remove(serviceName);
+ } else {
+ // Make sure to only disable services that are currently installed
+ final int numServices = mInstalledServices.size();
+ for (int i = 0; i < numServices; i++) {
+ PrintServiceInfo service = mInstalledServices.get(i);
+
+ if (service.getComponentName().equals(serviceName)) {
+ mDisabledServices.add(serviceName);
+ isChanged = true;
+ break;
+ }
+ }
+ }
+
+ if (isChanged) {
+ writeDisabledPrintServicesLocked(mDisabledServices);
+
+ onConfigurationChangedLocked();
+ }
}
}
@@ -523,6 +560,44 @@
}
}
+ public void addPrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mPrintServicesChangeListenerRecords == null) {
+ mPrintServicesChangeListenerRecords = new ArrayList<>();
+ }
+ mPrintServicesChangeListenerRecords.add(
+ new PrintServicesChangeListenerRecord(listener) {
+ @Override
+ public void onBinderDied() {
+ mPrintServicesChangeListenerRecords.remove(this);
+ }
+ });
+ }
+ }
+
+ public void removePrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener) {
+ synchronized (mLock) {
+ throwIfDestroyedLocked();
+ if (mPrintServicesChangeListenerRecords == null) {
+ return;
+ }
+ final int recordCount = mPrintServicesChangeListenerRecords.size();
+ for (int i = 0; i < recordCount; i++) {
+ PrintServicesChangeListenerRecord record =
+ mPrintServicesChangeListenerRecords.get(i);
+ if (record.listener.asBinder().equals(listener.asBinder())) {
+ mPrintServicesChangeListenerRecords.remove(i);
+ break;
+ }
+ }
+ if (mPrintServicesChangeListenerRecords.isEmpty()) {
+ mPrintServicesChangeListenerRecords = null;
+ }
+ }
+ }
+
@Override
public void onPrintJobStateChanged(PrintJobInfo printJob) {
mPrintJobForAppCache.onPrintJobStateChanged(printJob);
@@ -530,6 +605,10 @@
printJob.getAppId(), 0, printJob.getId()).sendToTarget();
}
+ public void onPrintServicesChanged() {
+ mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_CHANGED).sendToTarget();
+ }
+
@Override
public void onPrintersAdded(List<PrinterInfo> printers) {
synchronized (mLock) {
@@ -894,6 +973,8 @@
iterator.remove();
}
}
+
+ onPrintServicesChanged();
}
private void addServiceLocked(RemotePrintService service) {
@@ -978,8 +1059,29 @@
}
}
+ private void handleDispatchPrintServicesChanged() {
+ final List<PrintServicesChangeListenerRecord> records;
+ synchronized (mLock) {
+ if (mPrintServicesChangeListenerRecords == null) {
+ return;
+ }
+ records = new ArrayList<>(mPrintServicesChangeListenerRecords);
+ }
+ final int recordCount = records.size();
+ for (int i = 0; i < recordCount; i++) {
+ PrintServicesChangeListenerRecord record = records.get(i);
+
+ try {
+ record.listener.onPrintServicesChanged();;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error notifying for print services change", re);
+ }
+ }
+ }
+
private final class UserStateHandler extends Handler {
public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1;
+ public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2;
public UserStateHandler(Looper looper) {
super(looper, null, false);
@@ -987,10 +1089,17 @@
@Override
public void handleMessage(Message message) {
- if (message.what == MSG_DISPATCH_PRINT_JOB_STATE_CHANGED) {
- PrintJobId printJobId = (PrintJobId) message.obj;
- final int appId = message.arg1;
- handleDispatchPrintJobStateChanged(printJobId, appId);
+ switch (message.what) {
+ case MSG_DISPATCH_PRINT_JOB_STATE_CHANGED:
+ PrintJobId printJobId = (PrintJobId) message.obj;
+ final int appId = message.arg1;
+ handleDispatchPrintJobStateChanged(printJobId, appId);
+ break;
+ case MSG_DISPATCH_PRINT_SERVICES_CHANGED:
+ handleDispatchPrintServicesChanged();
+ break;
+ default:
+ // not reached
}
}
}
@@ -1015,6 +1124,23 @@
public abstract void onBinderDied();
}
+ private abstract class PrintServicesChangeListenerRecord implements DeathRecipient {
+ @NonNull final IPrintServicesChangeListener listener;
+
+ public PrintServicesChangeListenerRecord(@NonNull IPrintServicesChangeListener listener) throws RemoteException {
+ this.listener = listener;
+ listener.asBinder().linkToDeath(this, 0);
+ }
+
+ @Override
+ public void binderDied() {
+ listener.asBinder().unlinkToDeath(this, 0);
+ onBinderDied();
+ }
+
+ public abstract void onBinderDied();
+ }
+
private class PrinterDiscoverySessionMediator {
private final ArrayMap<PrinterId, PrinterInfo> mPrinters =
new ArrayMap<PrinterId, PrinterInfo>();
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 3ae1072..23f186c 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -106,6 +106,10 @@
<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" />
</application>
<instrumentation
diff --git a/services/tests/servicestests/res/drawable-nodpi/black_1024x4096.png b/services/tests/servicestests/res/drawable-nodpi/black_1024x4096.png
new file mode 100644
index 0000000..f700326
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/black_1024x4096.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable-nodpi/black_16x64.png b/services/tests/servicestests/res/drawable-nodpi/black_16x64.png
new file mode 100644
index 0000000..315763e
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/black_16x64.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable-nodpi/black_32x32.png b/services/tests/servicestests/res/drawable-nodpi/black_32x32.png
new file mode 100644
index 0000000..8958f6b
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/black_32x32.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable-nodpi/black_4096x1024.png b/services/tests/servicestests/res/drawable-nodpi/black_4096x1024.png
new file mode 100644
index 0000000..f675030
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/black_4096x1024.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable-nodpi/black_4096x4096.png b/services/tests/servicestests/res/drawable-nodpi/black_4096x4096.png
new file mode 100644
index 0000000..999d858
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/black_4096x4096.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable-nodpi/black_512x512.png b/services/tests/servicestests/res/drawable-nodpi/black_512x512.png
new file mode 100644
index 0000000..40d1c2c
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/black_512x512.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable-nodpi/black_64x16.png b/services/tests/servicestests/res/drawable-nodpi/black_64x16.png
new file mode 100644
index 0000000..5883015
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/black_64x16.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable-nodpi/black_64x64.png b/services/tests/servicestests/res/drawable-nodpi/black_64x64.png
new file mode 100644
index 0000000..71cfafc
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/black_64x64.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable-nodpi/icon1.png b/services/tests/servicestests/res/drawable-nodpi/icon1.png
new file mode 100644
index 0000000..64eb294
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/icon1.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable-nodpi/icon2.png b/services/tests/servicestests/res/drawable-nodpi/icon2.png
new file mode 100644
index 0000000..75024841
--- /dev/null
+++ b/services/tests/servicestests/res/drawable-nodpi/icon2.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable/icon1.png b/services/tests/servicestests/res/drawable/icon1.png
new file mode 100644
index 0000000..64eb294
--- /dev/null
+++ b/services/tests/servicestests/res/drawable/icon1.png
Binary files differ
diff --git a/services/tests/servicestests/res/drawable/icon2.png b/services/tests/servicestests/res/drawable/icon2.png
new file mode 100644
index 0000000..75024841
--- /dev/null
+++ b/services/tests/servicestests/res/drawable/icon2.png
Binary files differ
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index 2a967e6..c322ab8 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -558,6 +558,39 @@
}
@SmallTest
+ public void testUdpInvalidDstPort() throws Exception {
+ final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ // Ethernet header.
+ "9cd917000000001c2e0000000800" +
+ // IP header.
+ "45a00148000040003d115087d18194fb0a0f7af2" +
+ // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
+ // NOTE: The destination port is a non-DHCP port.
+ "0043aaaa01341268" +
+ // BOOTP header.
+ "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
+ // MAC address.
+ "9cd91700000000000000000000000000" +
+ // Server name.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Options.
+ "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
+ "d18180060f0777766d2e6564751c040a0fffffff000000"
+ ).toCharArray(), false));
+
+ try {
+ DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+ fail("Packet with invalid dst port did not throw ParseException");
+ } catch (ParseException expected) {}
+ }
+
+ @SmallTest
public void testMultipleRouters() throws Exception {
final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
// Ethernet header.
diff --git a/services/tests/servicestests/src/com/android/server/DropBoxTest.java b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
index 055ce76..7f28d44 100644
--- a/services/tests/servicestests/src/com/android/server/DropBoxTest.java
+++ b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
@@ -54,7 +54,7 @@
public void testAddText() throws Exception {
File dir = getEmptyDir("testAddText");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
long before = System.currentTimeMillis();
Thread.sleep(5);
@@ -90,7 +90,7 @@
public void testAddData() throws Exception {
File dir = getEmptyDir("testAddData");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
long before = System.currentTimeMillis();
dropbox.addData("DropBoxTest", "TEST".getBytes(), 0);
@@ -135,7 +135,7 @@
gz3.close();
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
dropbox.addFile("DropBoxTest", f0, DropBoxManager.IS_TEXT);
dropbox.addFile("DropBoxTest", f1, DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED);
@@ -201,7 +201,7 @@
new FileOutputStream(new File(dir, "DropBoxTest@" + (before + 100002) + ".lost")).close();
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
// Until a write, the timestamps are taken at face value
DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before);
@@ -252,7 +252,7 @@
public void testIsTagEnabled() throws Exception {
File dir = getEmptyDir("testIsTagEnabled");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
long before = System.currentTimeMillis();
dropbox.addText("DropBoxTest", "TEST-ENABLED");
@@ -285,7 +285,7 @@
public void testGetNextEntry() throws Exception {
File dir = getEmptyDir("testGetNextEntry");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
long before = System.currentTimeMillis();
dropbox.addText("DropBoxTest.A", "A0");
@@ -347,7 +347,7 @@
final int overhead = 64;
long before = System.currentTimeMillis();
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead);
addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead);
@@ -441,7 +441,7 @@
// Write one normal entry and another so big that it is instantly tombstoned
long before = System.currentTimeMillis();
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
dropbox.addText("DropBoxTest", "TEST");
addRandomEntry(dropbox, "DropBoxTest", blockSize * 20);
@@ -472,7 +472,7 @@
File dir = getEmptyDir("testFileCountLimits");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
dropbox.addText("DropBoxTest", "TEST0");
dropbox.addText("DropBoxTest", "TEST1");
dropbox.addText("DropBoxTest", "TEST2");
@@ -525,7 +525,7 @@
File dir = new File(getEmptyDir("testCreateDropBoxManagerWith"), "InvalidDirectory");
new FileOutputStream(dir).close(); // Create an empty file
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
- DropBoxManager dropbox = new DropBoxManager(service.getServiceStub());
+ DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
dropbox.addText("DropBoxTest", "should be ignored");
dropbox.addData("DropBoxTest", "should be ignored".getBytes(), 0);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index db2a9ad..aaec1e9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -28,6 +28,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
+import android.os.storage.StorageManager;
import android.view.IWindowManager;
import java.io.File;
@@ -163,6 +164,26 @@
}
@Override
+ boolean storageManagerIsFileBasedEncryptionEnabled() {
+ return context.storageManager.isFileBasedEncryptionEnabled();
+ }
+
+ @Override
+ boolean storageManagerIsNonDefaultBlockEncrypted() {
+ return context.storageManager.isNonDefaultBlockEncrypted();
+ }
+
+ @Override
+ boolean storageManagerIsEncrypted() {
+ return context.storageManager.isEncrypted();
+ }
+
+ @Override
+ boolean storageManagerIsEncryptable() {
+ return context.storageManager.isEncryptable();
+ }
+
+ @Override
String getDevicePolicyFilePathForSystemUser() {
return context.systemUserDataDir.getAbsolutePath() + "/";
}
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 212b37c..e897e3d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -969,6 +969,8 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
+
// Make sure the admin packge is installed to each user.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_SYSTEM_USER_UID);
@@ -1008,6 +1010,7 @@
* finds the right component from a package name upon migration.
*/
public void testDeviceOwnerMigration() throws Exception {
+ when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
checkDeviceOwnerWithMultipleDeviceAdmins();
// Overwrite the device owner setting and clears the clas name.
@@ -1441,10 +1444,10 @@
// Test 1. Caller doesn't have DO or DA.
try {
- dpm.getWifiMacAddress();
+ dpm.getWifiMacAddress(admin1);
fail();
} catch (SecurityException e) {
- MoreAsserts.assertContainsRegex("No active admin owned", e.getMessage());
+ MoreAsserts.assertContainsRegex("No active admin", e.getMessage());
}
// DO needs to be an DA.
@@ -1453,19 +1456,19 @@
// Test 2. Caller has DA, but not DO.
try {
- dpm.getWifiMacAddress();
+ dpm.getWifiMacAddress(admin1);
fail();
} catch (SecurityException e) {
- MoreAsserts.assertContainsRegex("No active admin owned", e.getMessage());
+ MoreAsserts.assertContainsRegex("does not own the device", e.getMessage());
}
// Test 3. Caller has PO, but not DO.
assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM));
try {
- dpm.getWifiMacAddress();
+ dpm.getWifiMacAddress(admin1);
fail();
} catch (SecurityException e) {
- MoreAsserts.assertContainsRegex("No active admin owned", e.getMessage());
+ MoreAsserts.assertContainsRegex("does not own the device", e.getMessage());
}
// Remove PO.
@@ -1475,17 +1478,17 @@
assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM));
// 4-1. But no WifiInfo.
- assertNull(dpm.getWifiMacAddress());
+ assertNull(dpm.getWifiMacAddress(admin1));
// 4-2. Returns WifiInfo, but with the default MAC.
when(mContext.wifiManager.getConnectionInfo()).thenReturn(new WifiInfo());
- assertNull(dpm.getWifiMacAddress());
+ assertNull(dpm.getWifiMacAddress(admin1));
// 4-3. With a real MAC address.
final WifiInfo wi = new WifiInfo();
wi.setMacAddress("11:22:33:44:55:66");
when(mContext.wifiManager.getConnectionInfo()).thenReturn(wi);
- assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress());
+ assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1));
}
public void testRebootCanOnlyBeCalledByDeviceOwner() throws Exception {
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 ef8e420..b05309a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -39,6 +39,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
+import android.os.storage.StorageManager;
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.view.IWindowManager;
@@ -211,6 +212,24 @@
}
}
+ public static class StorageManagerForMock {
+ public boolean isFileBasedEncryptionEnabled() {
+ return false;
+ }
+
+ public boolean isNonDefaultBlockEncrypted() {
+ return false;
+ }
+
+ public boolean isEncrypted() {
+ return false;
+ }
+
+ public boolean isEncryptable() {
+ return false;
+ }
+ }
+
public final Context realTestContext;
/**
@@ -239,6 +258,7 @@
public final IBackupManager ibackupManager;
public final IAudioService iaudioService;
public final LockPatternUtils lockPatternUtils;
+ public final StorageManagerForMock storageManager;
public final WifiManager wifiManager;
public final SettingsForMock settings;
public final MockContentResolver contentResolver;
@@ -272,6 +292,7 @@
ibackupManager = mock(IBackupManager.class);
iaudioService = mock(IAudioService.class);
lockPatternUtils = mock(LockPatternUtils.class);
+ storageManager = mock(StorageManagerForMock.class);
wifiManager = mock(WifiManager.class);
settings = mock(SettingsForMock.class);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
index b9e9aa9..82c6b6d 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
@@ -25,7 +25,7 @@
import static org.mockito.Mockito.when;
import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.ROAMING_DEFAULT;
+import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
@@ -447,7 +447,7 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -455,7 +455,7 @@
// Delta
uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -487,7 +487,7 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -495,7 +495,7 @@
// Delta
uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -527,7 +527,7 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -535,7 +535,7 @@
// Delta
uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -567,7 +567,7 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -575,7 +575,7 @@
// Delta
uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -607,7 +607,7 @@
// Baseline
NetworkStats xtSnapshot = null;
NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
@@ -615,7 +615,7 @@
// Delta
uidSnapshot = new NetworkStats(TEST_START+ 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
- .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, ROAMING_NO,
BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
mStatsObservers.updateStats(
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
index 4f6c7b9..74c1984 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsServiceTest.java
@@ -23,8 +23,8 @@
import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.ROAMING_ALL;
-import static android.net.NetworkStats.ROAMING_DEFAULT;
-import static android.net.NetworkStats.ROAMING_ROAMING;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.ROAMING_YES;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
@@ -321,8 +321,8 @@
// verify service recorded history
assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
- assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, ROAMING_DEFAULT, 512L, 4L, 256L, 2L, 4);
- assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, ROAMING_DEFAULT, 512L, 4L, 256L, 2L,
+ assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, ROAMING_NO, 512L, 4L, 256L, 2L, 4);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, ROAMING_NO, 512L, 4L, 256L, 2L,
6);
assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
verifyAndReset();
@@ -357,8 +357,8 @@
// after systemReady(), we should have historical stats loaded again
assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
assertUidTotal(sTemplateWifi, UID_RED, 1024L, 8L, 512L, 4L, 10);
- assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, ROAMING_DEFAULT, 512L, 4L, 256L, 2L, 4);
- assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, ROAMING_DEFAULT, 512L, 4L, 256L, 2L,
+ assertUidTotal(sTemplateWifi, UID_RED, SET_DEFAULT, ROAMING_NO, 512L, 4L, 256L, 2L, 4);
+ assertUidTotal(sTemplateWifi, UID_RED, SET_FOREGROUND, ROAMING_NO, 512L, 4L, 256L, 2L,
6);
assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 1L, 128L, 1L, 0);
verifyAndReset();
@@ -711,11 +711,11 @@
NetworkStats stats = mSession.getSummaryForAllUid(
sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(3, stats.size());
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 50L, 5L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO, 50L, 5L,
50L, 5L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 10L, 1L, 10L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_NO, 10L, 1L, 10L,
1L, 1);
- assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 2048L, 16L,
+ assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, ROAMING_NO, 2048L, 16L,
1024L, 8L, 0);
// now verify that recent history only contains one uid
@@ -723,7 +723,7 @@
stats = mSession.getSummaryForAllUid(
sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true);
assertEquals(1, stats.size());
- assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 1024L, 8L,
+ assertValues(stats, IFACE_ALL, UID_BLUE, SET_DEFAULT, TAG_NONE, ROAMING_NO, 1024L, 8L,
512L, 4L, 0);
verifyAndReset();
@@ -787,13 +787,13 @@
final NetworkStats stats = mSession.getSummaryForAllUid(
sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(4, stats.size());
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 2L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 2L,
128L, 2L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 1L, 64L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_NO, 64L, 1L, 64L,
1L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, ROAMING_DEFAULT, 32L, 2L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, TAG_NONE, ROAMING_NO, 32L, 2L,
32L, 2L, 1);
- assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, ROAMING_DEFAULT, 1L, 1L, 1L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_FOREGROUND, 0xFAAD, ROAMING_NO, 1L, 1L, 1L,
1L, 1);
verifyAndReset();
@@ -818,13 +818,13 @@
expectCurrentTime();
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
- // Note that all traffic from NetworkManagementService is tagged as ROAMING_DEFAULT, because
+ // Note that all traffic from NetworkManagementService is tagged as ROAMING_NO, because
// roaming isn't tracked at that layer. We layer it on top by inspecting the iface
// properties.
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 2L,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 2L,
128L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 1L, 64L,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_NO, 64L, 1L, 64L,
1L, 0L));
expectNetworkStatsPoll();
@@ -838,9 +838,9 @@
final NetworkStats stats = mSession.getSummaryForAllUid(
sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(2, stats.size());
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 128L, 2L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_YES, 128L, 2L,
128L, 2L, 0);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_ROAMING, 64L, 1L, 64L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_YES, 64L, 1L, 64L,
1L, 0);
verifyAndReset();
@@ -1073,9 +1073,9 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, 128L, 2L,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO, 128L, 2L,
128L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT, 64L, 1L, 64L,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_NO, 64L, 1L, 64L,
1L, 0L));
expectNetworkStatsPoll();
@@ -1089,9 +1089,9 @@
NetworkStats stats = mSession.getSummaryForAllUid(
sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(2, stats.size());
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING, 128L, 2L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_YES, 128L, 2L,
128L, 2L, 0);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_ROAMING, 64L, 1L, 64L,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_YES, 64L, 1L, 64L,
1L, 0);
verifyAndReset();
@@ -1106,9 +1106,9 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_NO,
128000000L, 2L, 128000000L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_DEFAULT,
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_NO,
64000000L, 1L, 64000000L, 1L, 0L));
expectNetworkStatsPoll();
@@ -1122,9 +1122,9 @@
stats = mSession.getSummaryForAllUid(
sTemplateImsi1, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertEquals(2, stats.size());
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_ROAMING,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, TAG_NONE, ROAMING_YES,
128000000L, 2L, 128000000L, 2L, 0);
- assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_ROAMING,
+ assertValues(stats, IFACE_ALL, UID_RED, SET_DEFAULT, 0xF00D, ROAMING_YES,
64000000L, 1L, 64000000L, 1L, 0);
verifyAndReset();
@@ -1180,7 +1180,7 @@
// verify summary API
final NetworkStats stats = mSession.getSummaryForNetwork(template, start, end);
- assertValues(stats, IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_DEFAULT, rxBytes,
+ assertValues(stats, IFACE_ALL, UID_ALL, SET_DEFAULT, TAG_NONE, ROAMING_NO, rxBytes,
rxPackets, txBytes, txPackets, operations);
}
@@ -1312,11 +1312,11 @@
}
List<Integer> roamings = new ArrayList<>();
- if (roaming == ROAMING_DEFAULT || roaming == ROAMING_ALL) {
- roamings.add(ROAMING_DEFAULT);
+ if (roaming == ROAMING_NO || roaming == ROAMING_ALL) {
+ roamings.add(ROAMING_NO);
}
- if (roaming == ROAMING_ROAMING || roaming == ROAMING_ALL) {
- roamings.add(ROAMING_ROAMING);
+ if (roaming == ROAMING_YES || roaming == ROAMING_ALL) {
+ roamings.add(ROAMING_YES);
}
for (int s : sets) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java
new file mode 100644
index 0000000..eb16a1d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java
@@ -0,0 +1,44 @@
+
+/*
+ * 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.content.pm.ShortcutInfo;
+import android.test.AndroidTestCase;
+
+import com.android.server.testutis.TestUtils;
+
+/**
+ * Tests for {@link ShortcutInfo}.
+
+ m FrameworksServicesTests &&
+ adb install \
+ -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.ShortcutInfoTest \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+
+ */
+public class ShortcutInfoTest extends AndroidTestCase {
+
+ public void testNoId() {
+ TestUtils.assertExpectException(
+ IllegalArgumentException.class,
+ "ID must be provided",
+ () -> new ShortcutInfo.Builder(mContext).build());
+ }
+
+ // TODO Add more tests.
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
new file mode 100644
index 0000000..f978d84
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -0,0 +1,1970 @@
+/*
+ * 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 org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ILauncherApps;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.content.pm.ShortcutServiceInternal;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
+import android.os.BaseBundle;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContext;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import com.android.frameworks.servicestests.R;
+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.ShortcutService.ConfigConstants;
+import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
+
+import libcore.io.IoUtils;
+
+import org.junit.Assert;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 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.ShortcutManagerTest \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+
+ * TODO: Add checks with assertAllNotHaveIcon()
+ *
+ * TODO: separate, detailed tests for ShortcutInfo (CTS?) *
+ *
+ * TODO: Cross-user test (do in CTS?)
+ *
+ */
+@SmallTest
+public class ShortcutManagerTest extends AndroidTestCase {
+ private static final String TAG = "ShortcutManagerTest";
+
+ /**
+ * Whether to enable dump or not. Should be only true when debugging to avoid bugs where
+ * dump affecting the behavior.
+ */
+ private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH true
+
+ private class BaseContext extends MockContext {
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case Context.USER_SERVICE:
+ return mMockUserManager;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mMockPackageManager;
+ }
+
+ @Override
+ public Resources getResources() {
+ return ShortcutManagerTest.this.getContext().getResources();
+ }
+ }
+
+ /** Context used in the client side */
+ private class ClientContext extends BaseContext {
+ @Override
+ public String getPackageName() {
+ return mInjectedClientPackage;
+ }
+ }
+
+ /** Context used in the service side */
+ private final class ServiceContext extends BaseContext {
+ }
+
+ /** ShortcutService with injection override methods. */
+ private final class ShortcutServiceTestable extends ShortcutService {
+ public ShortcutServiceTestable(Context context) {
+ super(context);
+
+ }
+
+ @Override
+ String injectShortcutManagerConstants() {
+ return ConfigConstants.KEY_RESET_INTERVAL_SEC + "=" + (INTERVAL / 1000) + ","
+ + ConfigConstants.KEY_MAX_SHORTCUTS + "=" + MAX_SHORTCUTS + ","
+ + ConfigConstants.KEY_MAX_DAILY_UPDATES + "=" + MAX_DAILY_UPDATES + ","
+ + 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
+ int injectDipToPixel(int dip) {
+ return dip;
+ }
+
+ @Override
+ long injectCurrentTimeMillis() {
+ return mInjectedCurrentTimeLillis;
+ }
+
+ @Override
+ int injectBinderCallingUid() {
+ return mInjectedCallingUid;
+ }
+
+ @Override
+ int injectGetPackageUid(String packageName, int userId) {
+ Integer uid = mInjectedPackageUidMap.get(packageName);
+ return UserHandle.getUid(getCallingUserId(), (uid != null ? uid : 0));
+ }
+
+ @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 mInjectdIsLowRamDevice;
+ }
+
+ @Override
+ PackageManagerInternal injectPackageManagerInternal() {
+ return mMockPackageManagerInternal;
+ }
+
+ @Override
+ boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
+ // Sort of hack; do a simpler check.
+ return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage);
+ }
+ }
+
+ /** ShortcutManager with injection override methods. */
+ private class ShortcutManagerTestable extends ShortcutManager {
+ public ShortcutManagerTestable(Context context, ShortcutServiceTestable service) {
+ super(context, service);
+ }
+
+ @Override
+ protected int injectMyUserId() {
+ return UserHandle.getUserId(mInjectedCallingUid);
+ }
+ }
+
+ private class LauncherAppImplTestable extends LauncherAppsImpl {
+ public LauncherAppImplTestable(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void ensureInUserProfiles(UserHandle userToCheck, String message) {
+ // SKIP
+ }
+
+ @Override
+ public void verifyCallingPackage(String callingPackage) {
+ // SKIP
+ }
+ }
+
+ private 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 {
+ }
+
+ private ServiceContext mServiceContext;
+ private ClientContext mClientContext;
+
+ private ShortcutServiceTestable mService;
+ private ShortcutManagerTestable mManager;
+ private ShortcutServiceInternal mInternal;
+
+ private LauncherAppImplTestable mLauncherAppImpl;
+ private LauncherAppsTestable mLauncherApps;
+
+ private File mInjectedFilePathRoot;
+
+ private long mInjectedCurrentTimeLillis;
+
+ private boolean mInjectdIsLowRamDevice;
+
+ private int mInjectedCallingUid;
+ private String mInjectedClientPackage;
+
+ private Map<String, Integer> mInjectedPackageUidMap;
+
+ private PackageManager mMockPackageManager;
+ private PackageManagerInternal mMockPackageManagerInternal;
+ private UserManager mMockUserManager;
+
+ private static final String CALLING_PACKAGE_1 = "com.android.test.1";
+ private static final int CALLING_UID_1 = 10001;
+
+ private static final String CALLING_PACKAGE_2 = "com.android.test.2";
+ private static final int CALLING_UID_2 = 10002;
+
+ private static final String CALLING_PACKAGE_3 = "com.android.test.3";
+ private static final int CALLING_UID_3 = 10003;
+
+ private static final String LAUNCHER_1 = "com.android.launcher.1";
+ private static final int LAUNCHER_UID_1 = 10011;
+
+ private static final String LAUNCHER_2 = "com.android.launcher.2";
+ private static final int LAUNCHER_UID_2 = 10012;
+
+ private static final int USER_10 = 10;
+ private static final int USER_11 = 11;
+
+ private static final long START_TIME = 1234560000000L;
+
+ private static final long INTERVAL = 10000;
+
+ private static final int MAX_SHORTCUTS = 10;
+
+ private static final int MAX_DAILY_UPDATES = 3;
+
+ private static final int MAX_ICON_DIMENSION = 128;
+
+ private static final int MAX_ICON_DIMENSION_LOWRAM = 32;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mServiceContext = new ServiceContext();
+ mClientContext = new ClientContext();
+
+ mMockPackageManager = mock(PackageManager.class);
+ mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+ mMockUserManager = mock(UserManager.class);
+
+ // Prepare injection values.
+
+ mInjectedCurrentTimeLillis = START_TIME;
+
+ mInjectedPackageUidMap = new HashMap<>();
+ mInjectedPackageUidMap.put(CALLING_PACKAGE_1, CALLING_UID_1);
+ mInjectedPackageUidMap.put(CALLING_PACKAGE_2, CALLING_UID_2);
+ mInjectedPackageUidMap.put(CALLING_PACKAGE_3, CALLING_UID_3);
+ mInjectedPackageUidMap.put(LAUNCHER_1, LAUNCHER_UID_1);
+ mInjectedPackageUidMap.put(LAUNCHER_2, LAUNCHER_UID_2);
+
+ mInjectedFilePathRoot = new File(getContext().getCacheDir(), "test-files");
+
+ // Empty the data directory.
+ if (mInjectedFilePathRoot.exists()) {
+ Assert.assertTrue("failed to delete dir",
+ FileUtils.deleteContents(mInjectedFilePathRoot));
+ }
+ mInjectedFilePathRoot.mkdirs();
+
+ initService();
+ setCaller(CALLING_PACKAGE_1);
+ }
+
+ /** (Re-) init the manager and the service. */
+ private void initService() {
+ LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
+
+ // Instantiate targets.
+ mService = new ShortcutServiceTestable(mServiceContext);
+ mManager = new ShortcutManagerTestable(mClientContext, mService);
+
+ mInternal = LocalServices.getService(ShortcutServiceInternal.class);
+
+ mLauncherAppImpl = new LauncherAppImplTestable(mServiceContext);
+ mLauncherApps = new LauncherAppsTestable(mClientContext, mLauncherAppImpl);
+
+ // Load the setting file.
+ mService.onBootPhase(SystemService.PHASE_LOCK_SETTINGS_READY);
+ }
+
+ /** Replace the current calling package */
+ private void setCaller(String packageName, int userId) {
+ mInjectedClientPackage = packageName;
+ mInjectedCallingUid = UserHandle.getUid(userId,
+ Preconditions.checkNotNull(mInjectedPackageUidMap.get(packageName),
+ "Unknown package"));
+ }
+
+ private void setCaller(String packageName) {
+ setCaller(packageName, UserHandle.USER_SYSTEM);
+ }
+
+ private String getCallingPackage() {
+ return mInjectedClientPackage;
+ }
+
+ private void runWithCaller(String packageName, int userId, Runnable r) {
+ final String previousPackage = mInjectedClientPackage;
+ final int previousUid = mInjectedCallingUid;
+
+ setCaller(packageName, userId);
+
+ r.run();
+
+ setCaller(previousPackage, previousUid);
+ }
+
+ private int getCallingUserId() {
+ return UserHandle.getUserId(mInjectedCallingUid);
+ }
+
+ private UserHandle getCallingUser() {
+ return UserHandle.of(getCallingUserId());
+ }
+
+ /** For debugging */
+ private void dumpsysOnLogcat() {
+ if (!ENABLE_DUMP) return;
+
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ final PrintWriter pw = new PrintWriter(out);
+ mService.dumpInner(pw);
+ pw.close();
+
+ Log.e(TAG, "Dumping ShortcutService:");
+ for (String line : out.toString().split("\n")) {
+ Log.e(TAG, line);
+ }
+ }
+
+ /**
+ * For debugging, dump arbitrary file on logcat.
+ */
+ private void dumpFileOnLogcat(String path) {
+ if (!ENABLE_DUMP) return;
+
+ Log.i(TAG, "Dumping file: " + path);
+ final StringBuilder sb = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new FileReader(path))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ Log.i(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.
+ */
+ private void dumpBaseStateFile() {
+ dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
+ + "/system/" + ShortcutService.FILENAME_BASE_STATE);
+ }
+
+ /**
+ * For debugging, dump per-user state file on logcat.
+ */
+ private void dumpUserFile(int userId) {
+ dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath()
+ + "/user-" + userId
+ + "/" + ShortcutService.FILENAME_USER_PACKAGES);
+ }
+
+ private static Bundle makeBundle(Object... keysAndValues) {
+ Preconditions.checkState((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;
+ }
+
+ /**
+ * Make a shortcut with an ID.
+ */
+ private ShortcutInfo makeShortcut(String id) {
+ return makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
+ }
+
+ /**
+ * Make a shortcut with an ID and timestamp.
+ */
+ private ShortcutInfo makeShortcutWithTimestamp(String id, long timestamp) {
+ final ShortcutInfo s = makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
+ s.setTimestamp(timestamp);
+ return s;
+ }
+
+ /**
+ * Make a shortcut with an ID and icon.
+ */
+ private ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
+ return makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, icon,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
+ }
+
+ private 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), /* weight =*/ 0);
+ setCaller(origCaller); // restore the caller
+
+ return s;
+ }
+
+ /**
+ * Make multiple shortcuts with IDs.
+ */
+ private List<ShortcutInfo> makeShortcuts(String... ids) {
+ final ArrayList<ShortcutInfo> ret = new ArrayList();
+ for (String id : ids) {
+ ret.add(makeShortcut(id));
+ }
+ return ret;
+ }
+
+ private ShortcutInfo.Builder makeShortcutBuilder() {
+ return new ShortcutInfo.Builder(mClientContext);
+ }
+
+ /**
+ * Make a shortcut with details.
+ */
+ private ShortcutInfo makeShortcut(String id, String title, ComponentName activity,
+ Icon icon, Intent intent, int weight) {
+ final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext)
+ .setId(id)
+ .setTitle(title)
+ .setWeight(weight)
+ .setIntent(intent);
+ if (icon != null) {
+ b.setIcon(icon);
+ }
+ if (activity != null) {
+ b.setActivityComponent(activity);
+ }
+ final ShortcutInfo s = b.build();
+
+ s.setTimestamp(mInjectedCurrentTimeLillis); // HACK
+
+ return s;
+ }
+
+ /**
+ * Make an intent.
+ */
+ private 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
+ private ComponentName makeComponent(Class<?> clazz) {
+ return new ComponentName(mClientContext, clazz);
+ }
+
+ @NonNull
+ private 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;
+ }
+
+ private void assertResetTimes(long expectedLastResetTime, long expectedNextResetTime) {
+ assertEquals(expectedLastResetTime, mService.getLastResetTimeLocked());
+ assertEquals(expectedNextResetTime, mService.getNextResetTimeLocked());
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts,
+ String... expectedIds) {
+ assertEquals(expectedIds.length, actualShortcuts.size());
+ final HashSet<String> expected = new HashSet<>(Arrays.asList(expectedIds));
+ final HashSet<String> actual = new HashSet<>();
+ for (ShortcutInfo s : actualShortcuts) {
+ actual.add(s.getId());
+ }
+
+ // Compare the sets.
+ assertEquals(expected, actual);
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllHaveIntents(
+ @NonNull List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNotNull("ID " + s.getId(), s.getIntent());
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllNotHaveIntents(
+ @NonNull List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNull("ID " + s.getId(), s.getIntent());
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllHaveTitle(
+ @NonNull List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNotNull("ID " + s.getId(), s.getTitle());
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllNotHaveTitle(
+ @NonNull List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNull("ID " + s.getId(), s.getTitle());
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllNotHaveIcon(
+ @NonNull List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertNull("ID " + s.getId(), s.getIcon());
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllHaveIconResId(
+ @NonNull 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;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllHaveIconFile(
+ @NonNull 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;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllHaveIcon(
+ @NonNull List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.hasIconFile() || s.hasIconResource());
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllHaveFlags(@NonNull List<ShortcutInfo> actualShortcuts,
+ int shortcutFlags) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.hasFlags(shortcutFlags));
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllKeyFieldsOnly(
+ @NonNull List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.hasKeyFieldsOnly());
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllNotKeyFieldsOnly(
+ @NonNull List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertFalse("ID " + s.getId(), s.hasKeyFieldsOnly());
+ }
+ return actualShortcuts;
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllDynamic(@NonNull List<ShortcutInfo> actualShortcuts) {
+ return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_DYNAMIC);
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllPinned(@NonNull List<ShortcutInfo> actualShortcuts) {
+ return assertAllHaveFlags(actualShortcuts, ShortcutInfo.FLAG_PINNED);
+ }
+
+ @NonNull
+ private List<ShortcutInfo> assertAllDynamicOrPinned(
+ @NonNull List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isDynamic() || s.isPinned());
+ }
+ return actualShortcuts;
+ }
+
+ private void assertBitmapSize(int expectedWidth, int expectedHeight, @NonNull Bitmap bitmap) {
+ assertEquals("width", expectedWidth, bitmap.getWidth());
+ assertEquals("height", expectedHeight, bitmap.getHeight());
+ }
+
+ private <T> void assertAllUnique(Collection<T> list) {
+ final Set<Object> set = new HashSet<>();
+ for (T item : list) {
+ if (set.contains(item)) {
+ fail("Duplicate item found: " + item + " (in the list: " + list + ")");
+ }
+ set.add(item);
+ }
+ }
+
+ @NonNull
+ private Bitmap pfdToBitmap(@NonNull ParcelFileDescriptor pfd) {
+ Preconditions.checkNotNull(pfd);
+ try {
+ return BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+ } finally {
+ IoUtils.closeQuietly(pfd);
+ }
+ }
+
+ private void assertBundleEmpty(BaseBundle b) {
+ assertTrue(b == null || b.size() == 0);
+ }
+
+ private ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) {
+ return mService.getPackageShortcutForTest(packageName, shortcutId, userId);
+ }
+
+ private ShortcutInfo getPackageShortcut(String packageName, String shortcutId) {
+ return getPackageShortcut(packageName, shortcutId, getCallingUserId());
+ }
+
+ private ShortcutInfo getCallerShortcut(String shortcutId) {
+ return getPackageShortcut(getCallingPackage(), shortcutId, getCallingUserId());
+ }
+
+ /**
+ * Test for the first launch path, no settings file available.
+ */
+ public void testFirstInitialize() {
+ assertResetTimes(START_TIME, START_TIME + INTERVAL);
+ }
+
+ /**
+ * Test for {@link ShortcutService#updateTimes()}
+ */
+ public void testUpdateAndGetNextResetTimeLocked() {
+ assertResetTimes(START_TIME, START_TIME + INTERVAL);
+
+ // Advance clock.
+ mInjectedCurrentTimeLillis += 100;
+
+ // Shouldn't have changed.
+ assertResetTimes(START_TIME, START_TIME + INTERVAL);
+
+ // Advance clock, almost the reset time.
+ mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
+
+ // Shouldn't have changed.
+ assertResetTimes(START_TIME, START_TIME + INTERVAL);
+
+ // Advance clock.
+ mInjectedCurrentTimeLillis += 1;
+
+ assertResetTimes(START_TIME + INTERVAL, START_TIME + 2 * INTERVAL);
+
+ // Advance further; 4 days since start.
+ mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
+
+ assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
+ }
+
+ /**
+ * Test for the restoration from saved file.
+ */
+ public void testInitializeFromSavedFile() {
+
+ mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
+ assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
+
+ mService.saveBaseStateLocked();
+
+ dumpBaseStateFile();
+
+ // 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_DAILY_UPDATES + "=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.getMaxDynamicShortcutsForTest());
+ assertEquals(5, mService.getMaxDailyUpdatesForTest());
+ assertEquals(100, mService.getMaxIconDimensionForTest());
+ assertEquals(CompressFormat.WEBP, mService.getIconPersistFormatForTest());
+ assertEquals(75, mService.getIconPersistQualityForTest());
+
+ mInjectdIsLowRamDevice = 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.getMaxDynamicShortcutsForTest());
+
+ assertEquals(ShortcutService.DEFAULT_MAX_DAILY_UPDATES,
+ mService.getMaxDailyUpdatesForTest());
+
+ 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#getMaxDynamicShortcutCount()} */
+ public void testGetMaxDynamicShortcutCount() {
+ assertEquals(MAX_SHORTCUTS, mManager.getMaxDynamicShortcutCount());
+ }
+
+ /** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
+ public void testGetRemainingCallCount() {
+ assertEquals(MAX_DAILY_UPDATES, mManager.getRemainingCallCount());
+ }
+
+ /** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
+ public void testGetRateLimitResetTime() {
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+
+ mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL + 50;
+
+ assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
+ }
+
+ public void testSetDynamicShortcuts() {
+ final Icon icon1 = Icon.createWithResource(mContext, R.drawable.icon1);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ mContext.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(Arrays.asList(si1, si2)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2");
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ // TODO: Check fields
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1");
+ assertEquals(1, mManager.getRemainingCallCount());
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
+ assertEquals(0, mManager.getDynamicShortcuts().size());
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ dumpsysOnLogcat();
+
+ mInjectedCurrentTimeLillis++; // Need to advance the clock for reset to work.
+ mService.resetThrottlingInner(UserHandle.USER_SYSTEM);
+
+ dumpsysOnLogcat();
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2, si3)));
+ assertEquals(2, mManager.getDynamicShortcuts().size());
+
+ // TODO Check max number
+ }
+
+ public void testAddDynamicShortcuts() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+ final ShortcutInfo si2 = makeShortcut("shortcut2");
+ final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1");
+
+ assertTrue(mManager.addDynamicShortcut(si2));
+ assertEquals(1, mManager.getRemainingCallCount());
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2");
+
+ // Add with the same ID
+ assertTrue(mManager.addDynamicShortcut(makeShortcut("shortcut1")));
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2");
+
+ // TODO Check max number
+
+ // TODO Check fields.
+ }
+
+ public void testDeleteDynamicShortcut() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+ final ShortcutInfo si2 = makeShortcut("shortcut2");
+ final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2", "shortcut3");
+
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mManager.deleteDynamicShortcut("shortcut1");
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut2", "shortcut3");
+
+ mManager.deleteDynamicShortcut("shortcut1");
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut2", "shortcut3");
+
+ mManager.deleteDynamicShortcut("shortcutXXX");
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut2", "shortcut3");
+
+ mManager.deleteDynamicShortcut("shortcut2");
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut3");
+
+ mManager.deleteDynamicShortcut("shortcut3");
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()));
+
+ // Still 2 calls left.
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ // TODO Make sure pinned shortcuts won't be deleted.
+ }
+
+ public void testDeleteAllDynamicShortcuts() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+ final ShortcutInfo si2 = makeShortcut("shortcut2");
+ final ShortcutInfo si3 = makeShortcut("shortcut3");
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(
+ mManager.getDynamicShortcuts()),
+ "shortcut1", "shortcut2", "shortcut3");
+
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mManager.deleteAllDynamicShortcuts();
+ 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(Arrays.asList(si1, si2, si3)));
+ assertEquals(3, mManager.getDynamicShortcuts().size());
+
+ // Still 1 call left
+ assertEquals(1, mManager.getRemainingCallCount());
+
+ // TODO Make sure pinned shortcuts won't be deleted.
+ }
+
+ public void testThrottling() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(1, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ // Reached the max
+
+ mInjectedCurrentTimeLillis++;
+ assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ // Still throttled
+ mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
+ assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ // Now it should work.
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); // fail
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(1, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeLillis++;
+ assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ // 4 days later...
+ mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ // Make sure getRemainingCallCount() itself gets reset withou calling setDynamicShortcuts().
+ mInjectedCurrentTimeLillis = START_TIME + 8 * INTERVAL;
+ assertEquals(3, mManager.getRemainingCallCount());
+ }
+
+ public void testThrottling_perPackage() {
+ final ShortcutInfo si1 = makeShortcut("shortcut1");
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(1, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ // Reached the max
+
+ mInjectedCurrentTimeLillis++;
+ assertFalse(mManager.setDynamicShortcuts(Arrays.asList(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(Arrays.asList(si2)));
+ assertEquals(2, mManager.getRemainingCallCount());
+
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertEquals(1, mManager.getRemainingCallCount());
+
+ // Back to the original caller, still throttled.
+ mInjectedClientPackage = CALLING_PACKAGE_1;
+ mInjectedCallingUid = CALLING_UID_1;
+
+ mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
+ assertEquals(0, mManager.getRemainingCallCount());
+ assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertEquals(0, mManager.getRemainingCallCount());
+
+ // Now it should work.
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+ mInjectedCurrentTimeLillis++;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+ mInjectedCurrentTimeLillis++;
+ assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+ mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+
+ mInjectedClientPackage = CALLING_PACKAGE_2;
+ mInjectedCallingUid = CALLING_UID_2;
+
+ assertEquals(3, mManager.getRemainingCallCount());
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ }
+
+ public void testIcons() {
+ final Icon res32x32 = Icon.createWithResource(mContext, R.drawable.black_32x32);
+ final Icon res64x64 = Icon.createWithResource(mContext, R.drawable.black_64x64);
+ final Icon res512x512 = Icon.createWithResource(mContext, R.drawable.black_512x512);
+
+ final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ mContext.getResources(), R.drawable.black_32x32));
+ final Icon bmp64x64 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ mContext.getResources(), R.drawable.black_64x64));
+ final Icon bmp512x512 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ mContext.getResources(), R.drawable.black_512x512));
+
+ // Set from package 1
+ setCaller(CALLING_PACKAGE_1);
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ 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(Arrays.asList(
+ makeShortcutWithIcon("res32x32", res512x512),
+ makeShortcutWithIcon("res64x64", res512x512),
+ makeShortcutWithIcon("none", res512x512)
+ )));
+ assertShortcutIds(assertAllNotHaveIcon(mManager.getDynamicShortcuts()),
+ "res32x32",
+ "res64x64",
+ "none");
+
+ // Re-initialize and load from the files.
+ initService();
+
+ // Load from launcher.
+ Bitmap bmp;
+
+ setCaller(LAUNCHER_1);
+
+ // Check hasIconResource()/hasIconFile().
+ assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("res32x32"),
+ getCallingUser())), "res32x32");
+
+ assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("res64x64"), getCallingUser())),
+ "res64x64");
+
+ assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("bmp32x32"), getCallingUser())),
+ "bmp32x32");
+
+ assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("bmp64x64"), getCallingUser())),
+ "bmp64x64");
+
+ assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
+ CALLING_PACKAGE_1, Arrays.asList("bmp512x512"), getCallingUser())),
+ "bmp512x512");
+
+ // Check
+ assertEquals(
+ R.drawable.black_32x32,
+ mLauncherApps.getShortcutIconResId(
+ makePackageShortcut(CALLING_PACKAGE_1, "res32x32"), getCallingUser()));
+
+ assertEquals(
+ R.drawable.black_64x64,
+ mLauncherApps.getShortcutIconResId(
+
+ makePackageShortcut(CALLING_PACKAGE_1, "res64x64"), getCallingUser()));
+
+ assertEquals(
+ 0, // because it's not a resource
+ mLauncherApps.getShortcutIconResId(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
+ assertEquals(
+ 0, // because it's not a resource
+ mLauncherApps.getShortcutIconResId(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
+ assertEquals(
+ 0, // because it's not a resource
+ mLauncherApps.getShortcutIconResId(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
+
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp32x32"), getCallingUser()));
+ assertBitmapSize(32, 32, bmp);
+
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp64x64"), getCallingUser()));
+ assertBitmapSize(64, 64, bmp);
+
+ bmp = pfdToBitmap(mLauncherApps.getShortcutIconFd(
+ makePackageShortcut(CALLING_PACKAGE_1, "bmp512x512"), getCallingUser()));
+ assertBitmapSize(128, 128, bmp);
+
+ // TODO Test the content URI case too.
+ }
+
+ private void checkShrinkBitmap(int expectedWidth, int expectedHeight, int resId, int maxSize) {
+ assertBitmapSize(expectedWidth, expectedHeight,
+ ShortcutService.shrinkBitmap(BitmapFactory.decodeResource(
+ mContext.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);
+ }
+
+ private 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 {
+ mInjectedCurrentTimeLillis = 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);
+
+ mInjectedCurrentTimeLillis++;
+
+ 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(Arrays.asList(
+ 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(Arrays.asList(
+ 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(Arrays.asList(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ )));
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ makeShortcut("s1"),
+ makeShortcut("s2"),
+ makeShortcut("s3"),
+ makeShortcut("s4"),
+ makeShortcut("s5")
+ )));
+ });
+ runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s2", "s3"),
+ getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s4", "s5"),
+ getCallingUser());
+ });
+ runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
+ mManager.deleteDynamicShortcut("s1");
+ mManager.deleteDynamicShortcut("s2");
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ mManager.deleteDynamicShortcut("s1");
+ mManager.deleteDynamicShortcut("s3");
+ mManager.deleteDynamicShortcut("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(mContext, R.drawable.black_32x32))
+ .build();
+
+ ShortcutInfo s4 = makeShortcutBuilder()
+ .setId("s4")
+ .setTitle("new title")
+ .build();
+
+ mManager.updateShortcuts(Arrays.asList(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(Arrays.asList(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("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.
+ }
+
+ // TODO: updateShortcuts()
+ // TODO: getPinnedShortcuts()
+
+ // === Test for launcher side APIs ===
+
+ private static ShortcutQuery buildQuery(long changedSince,
+ String packageName, ComponentName componentName,
+ /* @ShortcutQuery.QueryFlags */ int flags) {
+ final ShortcutQuery q = new ShortcutQuery();
+ q.setChangedSince(changedSince);
+ q.setPackage(packageName);
+ q.setActivity(componentName);
+ q.setQueryFlags(flags);
+ return q;
+ }
+
+ public void testGetShortcuts() {
+
+ // Set up shortcuts.
+
+ setCaller(CALLING_PACKAGE_1);
+ final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 5000);
+ final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 1000);
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+
+ setCaller(CALLING_PACKAGE_2);
+ final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
+ final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
+ final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+
+ setCaller(CALLING_PACKAGE_3);
+ final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s3", 5000);
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s3_2)));
+
+ setCaller(LAUNCHER_1);
+
+ // Get dynamic
+ assertAllDynamic(assertAllHaveTitle(assertAllNotHaveIntents(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"))));
+
+ // Pin some shortcuts.
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ Arrays.asList("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");
+
+ // TODO More tests: pinned but dynamic, filter by activity
+ }
+
+ 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(Arrays.asList(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(Arrays.asList(s2_1)));
+ dumpsysOnLogcat();
+
+ // Pin some.
+ setCaller(LAUNCHER_1);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ Arrays.asList("s2"), getCallingUser());
+
+ dumpsysOnLogcat();
+
+ // Delete some.
+ setCaller(CALLING_PACKAGE_1);
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ mManager.deleteDynamicShortcut("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,
+ Arrays.asList("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,
+ Arrays.asList("s3"), getCallingUser())))
+ /* none */);
+
+ list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
+ mLauncherApps.getShortcutInfo(CALLING_PACKAGE_2,
+ Arrays.asList("s1", "s2", "s3"), getCallingUser()))),
+ "s1");
+ assertEquals("ABC", findById(list, "s1").getTitle());
+ }
+
+ public void testPinShortcutAndGetPinnedShortcuts() {
+ // Create some shortcuts.
+ setCaller(CALLING_PACKAGE_1);
+ final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
+ final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
+
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+
+ setCaller(CALLING_PACKAGE_2);
+ final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
+ final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
+ final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+
+ setCaller(CALLING_PACKAGE_3);
+ final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2)));
+
+ // Pin some.
+ setCaller(LAUNCHER_1);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ Arrays.asList("s2", "s3"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ Arrays.asList("s3", "s4", "s5"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
+ Arrays.asList("s3"), getCallingUser()); // Note ID doesn't exist
+
+ // Delete some.
+ setCaller(CALLING_PACKAGE_1);
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ mManager.deleteDynamicShortcut("s2");
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+
+ setCaller(CALLING_PACKAGE_2);
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+ mManager.deleteDynamicShortcut("s3");
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+
+ setCaller(CALLING_PACKAGE_3);
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+ mManager.deleteDynamicShortcut("s2");
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+
+ // Get pinned shortcuts from launcher
+ setCaller(LAUNCHER_1);
+
+ // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s2");
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ "s3", "s4");
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_3,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
+ /* none */);
+ }
+
+ public void testCreateShortcutIntent() {
+ // Create some 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(Arrays.asList(s1_1, s1_2)));
+
+ setCaller(CALLING_PACKAGE_2);
+ 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(Arrays.asList(s2_1)));
+
+ // Pin all.
+ setCaller(LAUNCHER_1);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ Arrays.asList("s1", "s2"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ Arrays.asList("s1"), getCallingUser());
+
+ // Just to make it complicated, delete some.
+ setCaller(CALLING_PACKAGE_1);
+ mManager.deleteDynamicShortcut("s2");
+
+ // intent and check.
+ setCaller(LAUNCHER_1);
+ Intent intent;
+ intent = mInternal.createShortcutIntent(getCallingPackage(),
+ CALLING_PACKAGE_1, "s1", getCallingUserId());
+ assertEquals(ShortcutActivity2.class.getName(), intent.getComponent().getClassName());
+
+ intent = mInternal.createShortcutIntent(getCallingPackage(),
+ CALLING_PACKAGE_1, "s2", getCallingUserId());
+ assertEquals(ShortcutActivity3.class.getName(), intent.getComponent().getClassName());
+
+ intent = mInternal.createShortcutIntent(getCallingPackage(),
+ CALLING_PACKAGE_2, "s1", getCallingUserId());
+ assertEquals(ShortcutActivity.class.getName(), intent.getComponent().getClassName());
+
+ // TODO Check extra, etc
+ }
+
+ // === Test for persisting ===
+
+ public void testSaveAndLoadUser_empty() {
+ assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
+
+ Log.i(TAG, "Saved state");
+ dumpsysOnLogcat();
+ dumpUserFile(0);
+
+ // Restore.
+ 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(mContext, R.drawable.black_64x16);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ mContext.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(Arrays.asList(si1, si2)));
+
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
+ final Icon icon1 = Icon.createWithResource(mContext, R.drawable.black_16x64);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ mContext.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(Arrays.asList(si1, si2)));
+
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ final Icon icon1 = Icon.createWithResource(mContext, R.drawable.black_64x64);
+ final Icon icon2 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ mContext.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(Arrays.asList(si1, si2)));
+
+ assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
+ assertEquals(2, mManager.getRemainingCallCount());
+ });
+
+ mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM).setLauncherComponent(
+ mService, new ComponentName("pkg1", "class"));
+
+ // Restore.
+ initService();
+
+ // Before the load, the map should be empty.
+ assertEquals(0, mService.getShortcutsForTest().size());
+
+ // this will pre-load the per-user info.
+ mService.onStartUserLocked(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)
+ .getLauncherComponent().getPackageName());
+
+ // Start another user
+ mService.onStartUserLocked(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).getLauncherComponent());
+
+ // Try stopping the user
+ mService.onCleanupUserInner(USER_10);
+
+ // Now it's unloaded.
+ assertEquals(1, mService.getShortcutsForTest().size());
+
+ // TODO Check all other fields
+ }
+
+ // TODO Detailed test for hasShortcutPermissionInner().
+
+ // TODO Add tests for the command line functions too.
+}
diff --git a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
new file mode 100644
index 0000000..52e8f37
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java
@@ -0,0 +1,47 @@
+/*
+ * 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.testutis;
+
+import android.test.MoreAsserts;
+
+import junit.framework.Assert;
+
+public class TestUtils {
+ private TestUtils() {
+ }
+
+ public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
+ Runnable r) {
+ assertExpectException(expectedExceptionType, null, r);
+ }
+
+ public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
+ String expectedExceptionMessageRegex, Runnable r) {
+ try {
+ r.run();
+ Assert.fail("Expected exception type " + expectedExceptionType.getClass().getName()
+ + " was not thrown");
+ } catch (Throwable e) {
+ Assert.assertTrue(
+ "Expected exception type was " + expectedExceptionType.getClass().getName()
+ + " but caught " + e.getClass().getName(),
+ expectedExceptionType.isAssignableFrom(e.getClass()));
+ if (expectedExceptionMessageRegex != null) {
+ MoreAsserts.assertContainsRegex(expectedExceptionMessageRegex, e.getMessage());
+ }
+ }
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 3e2b43d..a3313c9 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -274,6 +274,11 @@
- (idle ? mScreenOnTimeThreshold : 0) - 1000 /* just a second more */;
}
+ public void clearUsageLocked(String packageName, int userId) {
+ ArrayMap<String, PackageHistory> userHistory = getUserHistoryLocked(userId);
+ userHistory.remove(packageName);
+ }
+
private boolean hasPassedThresholdsLocked(PackageHistory packageHistory, long elapsedRealtime) {
return (packageHistory.lastUsedScreenTime
<= getScreenOnTimeLocked(elapsedRealtime) - mScreenOnTimeThreshold)
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 8da1785..beec40f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -180,6 +180,7 @@
IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageFilter.addDataScheme("package");
getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
@@ -266,6 +267,12 @@
|| Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
clearCarrierPrivilegedApps();
}
+ if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+ Intent.ACTION_PACKAGE_ADDED.equals(action))
+ && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
+ getSendingUserId());
+ }
}
}
@@ -332,6 +339,12 @@
}
}
+ void clearAppIdleForPackage(String packageName, int userId) {
+ synchronized (mLock) {
+ mAppIdleHistory.clearUsageLocked(packageName, userId);
+ }
+ }
+
private void cleanUpRemovedUsersLocked() {
final List<UserInfo> users = mUserManager.getUsers(true);
if (users == null || users.size() == 0) {
diff --git a/services/usb/java/com/android/server/usb/MtpNotificationManager.java b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
index 17039bb..101e200 100644
--- a/services/usb/java/com/android/server/usb/MtpNotificationManager.java
+++ b/services/usb/java/com/android/server/usb/MtpNotificationManager.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
@@ -101,8 +102,8 @@
TAG, device.getDeviceId(), notification);
}
- void hideNotification(UsbDevice device) {
- mContext.getSystemService(NotificationManager.class).cancel(TAG, device.getDeviceId());
+ void hideNotification(int deviceId) {
+ mContext.getSystemService(NotificationManager.class).cancel(TAG, deviceId);
}
private class Receiver extends BroadcastReceiver {
@@ -121,7 +122,13 @@
}
}
- static boolean isMtpDevice(UsbDevice device) {
+ static boolean shouldShowNotification(PackageManager packageManager, UsbDevice device) {
+ // We don't show MTP notification for devices that has FEATURE_AUTOMOTIVE.
+ return !packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) &&
+ isMtpDevice(device);
+ }
+
+ private static boolean isMtpDevice(UsbDevice device) {
for (int i = 0; i < device.getInterfaceCount(); i++) {
final UsbInterface usbInterface = device.getInterface(i);
if ((usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE &&
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index c4d7336..de9ede3 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -738,7 +738,7 @@
// Send broadcast to running activity with registered intent
mUserContext.sendBroadcast(intent);
- if (MtpNotificationManager.isMtpDevice(device)) {
+ if (MtpNotificationManager.shouldShowNotification(mPackageManager, device)) {
// Show notification if the device is MTP storage.
mMtpNotificationManager.showNotification(device);
} else {
@@ -769,9 +769,7 @@
if (DEBUG) Slog.d(TAG, "usbDeviceRemoved, sending " + intent);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- if (MtpNotificationManager.isMtpDevice(device)) {
- mMtpNotificationManager.hideNotification(device);
- }
+ mMtpNotificationManager.hideNotification(device.getDeviceId());
}
public void accessoryAttached(UsbAccessory accessory) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index e05f00d..40687b0 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -171,7 +171,7 @@
// Fetch a ModelData instance from the hash map. Creates a new one if none
// exists.
- ModelData modelData = getOrCreateGenericModelData(modelId);
+ ModelData modelData = getOrCreateGenericModelDataLocked(modelId);
IRecognitionStatusCallback oldCallback = modelData.getCallback();
if (oldCallback != null) {
@@ -373,7 +373,7 @@
// Also clear the internal state once the recognition has been stopped.
modelData.setLoaded();
modelData.clearCallback();
- if (!computeRecognitionRunning()) {
+ if (!computeRecognitionRunningLocked()) {
internalClearGlobalStateLocked();
}
return status;
@@ -505,12 +505,12 @@
if (modelId == null || mModule == null) {
return STATUS_ERROR;
}
- ModelData modelData = mGenericModelDataMap.get(modelId);
- if (modelData == null) {
- Slog.w(TAG, "Unload error: Attempting unload invalid generic model with id:" + modelId);
- return STATUS_ERROR;
- }
synchronized (mLock) {
+ ModelData modelData = mGenericModelDataMap.get(modelId);
+ if (modelData == null) {
+ Slog.w(TAG, "Unload error: Attempting unload invalid generic model with id:" + modelId);
+ return STATUS_ERROR;
+ }
if (!modelData.isModelLoaded()) {
// Nothing to do here.
Slog.i(TAG, "Unload: Given generic model is not loaded:" + modelId);
@@ -530,7 +530,7 @@
Slog.w(TAG, "unloadGenericSoundModel() force-marking model as unloaded.");
}
mGenericModelDataMap.remove(modelId);
- if (DBG) dumpGenericModelState();
+ if (DBG) dumpGenericModelStateLocked();
return status;
}
}
@@ -580,7 +580,7 @@
if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS) {
return;
}
- ModelData model = getModelDataFor(event.soundModelHandle);
+ ModelData model = getModelDataForLocked(event.soundModelHandle);
if (model == null) {
Slog.w(TAG, "Generic recognition event: Model does not exist for handle: " +
event.soundModelHandle);
@@ -919,7 +919,7 @@
mIsPowerSaveMode = mPowerManager.isPowerSaveMode();
}
- private ModelData getOrCreateGenericModelData(UUID modelId) {
+ private ModelData getOrCreateGenericModelDataLocked(UUID modelId) {
ModelData modelData = mGenericModelDataMap.get(modelId);
if (modelData == null) {
modelData = new ModelData(modelId);
@@ -932,7 +932,7 @@
// Instead of maintaining a second hashmap of modelHandle -> ModelData, we just
// iterate through to find the right object (since we don't expect 100s of models
// to be stored).
- private ModelData getModelDataFor(int modelHandle) {
+ private ModelData getModelDataForLocked(int modelHandle) {
// Fetch ModelData object corresponding to the model handle.
for (ModelData model : mGenericModelDataMap.values()) {
if (model.getHandle() == modelHandle) {
@@ -988,7 +988,7 @@
}
}
}
- if (DBG) dumpGenericModelState();
+ if (DBG) dumpGenericModelStateLocked();
return status;
}
@@ -1017,11 +1017,11 @@
}
}
}
- if (DBG) dumpGenericModelState();
+ if (DBG) dumpGenericModelStateLocked();
return status;
}
- private void dumpGenericModelState() {
+ private void dumpGenericModelStateLocked() {
for (UUID modelId : mGenericModelDataMap.keySet()) {
ModelData modelData = mGenericModelDataMap.get(modelId);
Slog.i(TAG, "Model :" + modelData.toString());
@@ -1030,28 +1030,24 @@
// Computes whether we have any recognition running at all (voice or generic). Sets
// the mRecognitionRunning variable with the result.
- private boolean computeRecognitionRunning() {
- synchronized (mLock) {
- if (mModuleProperties == null || mModule == null) {
- mRecognitionRunning = false;
- return mRecognitionRunning;
- }
- if (mKeyphraseListener != null &&
- mKeyphraseStarted &&
- mCurrentKeyphraseModelHandle != INVALID_VALUE &&
- mCurrentSoundModel != null) {
+ private boolean computeRecognitionRunningLocked() {
+ if (mModuleProperties == null || mModule == null) {
+ mRecognitionRunning = false;
+ return mRecognitionRunning;
+ }
+ if (mKeyphraseListener != null && mKeyphraseStarted &&
+ mCurrentKeyphraseModelHandle != INVALID_VALUE && mCurrentSoundModel != null) {
+ mRecognitionRunning = true;
+ return mRecognitionRunning;
+ }
+ for (UUID modelId : mGenericModelDataMap.keySet()) {
+ ModelData modelData = mGenericModelDataMap.get(modelId);
+ if (modelData.isModelStarted()) {
mRecognitionRunning = true;
return mRecognitionRunning;
}
- for (UUID modelId : mGenericModelDataMap.keySet()) {
- ModelData modelData = mGenericModelDataMap.get(modelId);
- if (modelData.isModelStarted()) {
- mRecognitionRunning = true;
- return mRecognitionRunning;
- }
- }
- mRecognitionRunning = false;
}
+ mRecognitionRunning = false;
return mRecognitionRunning;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 6ab0b99..837b4a4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -75,7 +75,7 @@
*/
public class VoiceInteractionManagerService extends SystemService {
static final String TAG = "VoiceInteractionManagerService";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
final Context mContext;
final ContentResolver mResolver;
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 10808da..afb7d93 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -95,6 +95,19 @@
public static final int STATE_DISCONNECTING = 10;
/**
+ * The state of an external call which is in the process of being pulled from a remote device to
+ * the local device.
+ * <p>
+ * A call can only be in this state if the {@link Details#PROPERTY_IS_EXTERNAL_CALL} property
+ * and {@link Details#CAPABILITY_CAN_PULL_CALL} capability are set on the call.
+ * <p>
+ * 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.
+ */
+ public static final int STATE_PULLING_CALL = 11;
+
+ /**
* The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
* extras. Used to pass the phone accounts to display on the front end to the user in order to
* select phone accounts to (for example) place a call.
@@ -226,8 +239,23 @@
*/
public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000;
+ /**
+ * When set for an external call, indicates that this {@code Call} can be pulled from a
+ * remote device to the current device.
+ * <p>
+ * Should only be set on a {@code Call} where {@link #PROPERTY_IS_EXTERNAL_CALL} is set.
+ * <p>
+ * An {@link InCallService} will only see calls with this capability if it has the
+ * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
+ * in its manifest.
+ * <p>
+ * See {@link Connection#CAPABILITY_CAN_PULL_CALL} and
+ * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+ */
+ public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
+
//******************************************************************************************
- // Next CAPABILITY value: 0x00800000
+ // Next CAPABILITY value: 0x01000000
//******************************************************************************************
/**
@@ -261,8 +289,25 @@
*/
public static final int PROPERTY_WORK_CALL = 0x00000020;
+ /**
+ * When set, indicates that this {@code Call} does not actually exist locally for the
+ * {@link ConnectionService}.
+ * <p>
+ * Consider, for example, a scenario where a user has two phones with the same phone number.
+ * When a user places a call on one device, the telephony stack can represent that call on
+ * the other device by adding it to the {@link ConnectionService} with the
+ * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability set.
+ * <p>
+ * An {@link InCallService} will only see calls with this property if it has the
+ * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
+ * in its manifest.
+ * <p>
+ * See {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+ */
+ public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
+
//******************************************************************************************
- // Next PROPERTY value: 0x00000040
+ // Next PROPERTY value: 0x00000100
//******************************************************************************************
private final String mTelecomCallId;
@@ -362,6 +407,9 @@
if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
}
+ if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
+ builder.append(" CAPABILITY_CAN_PULL_CALL");
+ }
builder.append("]");
return builder.toString();
}
@@ -411,6 +459,9 @@
if (hasProperty(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
builder.append(" PROPERTY_EMERGENCY_CALLBACK_MODE");
}
+ if (hasProperty(properties, PROPERTY_IS_EXTERNAL_CALL)) {
+ builder.append(" PROPERTY_IS_EXTERNAL_CALL");
+ }
builder.append("]");
return builder.toString();
}
@@ -723,6 +774,17 @@
* conferenced.
*/
public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
+
+ /**
+ * Invoked when a call receives an event from its associated {@link Connection}.
+ * <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.
+ */
+ public void onConnectionEvent(Call call, String event, Bundle extras) {}
}
/**
@@ -889,6 +951,43 @@
}
/**
+ * Initiates a request to the {@link ConnectionService} to pull an external call to the local
+ * device.
+ * <p>
+ * Calls to this method are ignored if the call does not have the
+ * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} property set.
+ * <p>
+ * 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.
+ */
+ public void pullExternalCall() {
+ // If this isn't an external call, ignore the request.
+ if (!mDetails.hasProperty(Details.PROPERTY_IS_EXTERNAL_CALL)) {
+ return;
+ }
+
+ mInCallAdapter.pullExternalCall(mTelecomCallId);
+ }
+
+ /**
+ * Sends a {@code Call} event from this {@code Call} to the associated {@link Connection} in
+ * the {@link ConnectionService}.
+ * <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.
+ *
+ * @param event The connection event.
+ * @param extras Bundle containing extra information associated with the event.
+ */
+ public void sendCallEvent(String event, Bundle extras) {
+ mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras);
+ }
+
+ /**
* 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
@@ -1211,6 +1310,11 @@
}
}
+ /** {@hide} */
+ final void internalOnConnectionEvent(String event, Bundle extras) {
+ fireOnConnectionEvent(event, extras);
+ }
+
private void fireStateChanged(final int newState) {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
@@ -1358,6 +1462,27 @@
}
/**
+ * Notifies listeners of an incoming connection event.
+ * <p>
+ * Connection events are issued via {@link Connection#sendConnectionEvent(String, Bundle)}.
+ *
+ * @param event
+ * @param extras
+ */
+ private void fireOnConnectionEvent(final String event, final Bundle extras) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onConnectionEvent(call, event, extras);
+ }
+ });
+ }
+ }
+
+ /**
* Determines if two bundles are equal.
*
* @param bundle The original bundle.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4547c6a..51a6588 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -93,6 +93,15 @@
public static final int STATE_DISCONNECTED = 6;
/**
+ * The state of an external connection which is in the process of being pulled from a remote
+ * device to the local device.
+ * <p>
+ * A connection can only be in this state if the {@link #CAPABILITY_IS_EXTERNAL_CALL} and
+ * {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
+ */
+ public static final int STATE_PULLING_CALL = 7;
+
+ /**
* Connection can currently be put on hold or unheld. This is distinct from
* {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times,
* it does not at the moment support the function. This can be true while the call is in the
@@ -251,7 +260,6 @@
/**
* Indicates that the connection itself wants to handle any sort of reply response, rather than
* relying on SMS.
- * @hide
*/
public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
@@ -270,8 +278,33 @@
*/
public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
+ /**
+ * When set, indicates that the {@code Connection} does not actually exist locally for the
+ * {@link ConnectionService}.
+ * <p>
+ * Consider, for example, a scenario where a user has two devices with the same phone number.
+ * When a user places a call on one devices, the telephony stack can represent that call on the
+ * other device by adding is to the {@link ConnectionService} with the
+ * {@code CAPABILITY_IS_EXTERNAL_CALL} capability set.
+ * <p>
+ * An {@link ConnectionService} should not assume that all {@link InCallService}s will handle
+ * 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.
+ */
+ public static final int CAPABILITY_IS_EXTERNAL_CALL = 0x01000000;
+
+ /**
+ * When set for an external connection, indicates that this {@code Connection} can be pulled
+ * from a remote device to the current device.
+ * <p>
+ * Should only be set on a {@code Connection} where {@link #CAPABILITY_IS_EXTERNAL_CALL}
+ * is set.
+ */
+ public static final int CAPABILITY_CAN_PULL_CALL = 0x02000000;
+
//**********************************************************************************************
- // Next CAPABILITY value: 0x01000000
+ // Next CAPABILITY value: 0x04000000
//**********************************************************************************************
/**
@@ -315,6 +348,18 @@
public static final String EVENT_ON_HOLD_TONE_END =
"android.telecom.event.ON_HOLD_TONE_END";
+ /**
+ * Connection event used to inform {@link InCallService}s when pulling of an external call has
+ * failed. The user interface should inform the user of the error.
+ * <p>
+ * Expected to be used by the {@link ConnectionService} when the {@link Call#pullExternalCall()}
+ * API is called on a {@link Call} with the properties
+ * {@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.
+ */
+ public static final String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
+
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
@@ -434,6 +479,12 @@
if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
builder.append(" CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION");
}
+ if (can(capabilities, CAPABILITY_IS_EXTERNAL_CALL)) {
+ builder.append(" CAPABILITY_IS_EXTERNAL_CALL");
+ }
+ if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
+ builder.append(" CAPABILITY_CAN_PULL_CALL");
+ }
builder.append("]");
return builder.toString();
@@ -465,8 +516,7 @@
public void onConferenceStarted() {}
public void onConferenceMergeFailed(Connection c) {}
public void onExtrasChanged(Connection c, Bundle extras) {}
- /** @hide */
- public void onConnectionEvent(Connection c, String event) {}
+ public void onConnectionEvent(Connection c, String event, Bundle extras) {}
}
/**
@@ -1836,9 +1886,8 @@
public void onReject() {}
/**
- * Notifies ths Connection of a request reject with a message.
- *
- * @hide
+ * Notifies this Connection, which is in {@link #STATE_RINGING}, of
+ * a request to reject with a message.
*/
public void onReject(String replyMessage) {}
@@ -1854,6 +1903,31 @@
*/
public void onPostDialContinue(boolean proceed) {}
+ /**
+ * Notifies this Connection of a request to pull an external call to the local device.
+ * <p>
+ * The {@link InCallService} issues a request to pull an external call to the local device via
+ * {@link Call#pullExternalCall()}.
+ * <p>
+ * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL} and
+ * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability bits must be set.
+ * <p>
+ * For more information on external calls, see {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+ */
+ public void onPullExternalCall() {}
+
+ /**
+ * Notifies this Connection of a {@link Call} event initiated from an {@link InCallService}.
+ * <p>
+ * The {@link InCallService} issues a Call event via {@link Call#sendCallEvent(String, Bundle)}.
+ * <p>
+ * See also {@link Call#sendCallEvent(String, Bundle)}.
+ *
+ * @param event The call event.
+ * @param extras Extras associated with the call event.
+ */
+ public void onCallEvent(String event, Bundle extras) {}
+
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
@@ -2008,14 +2082,20 @@
}
/**
- * Sends a connection event to Telecom.
+ * 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.
+ *
+ * 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.
*
* @param event The connection event.
- * @hide
+ * @param extras Bundle containing extra information associated with the event.
*/
- protected void sendConnectionEvent(String event) {
+ public void sendConnectionEvent(String event, Bundle extras) {
for (Listener l : mListeners) {
- l.onConnectionEvent(this, event);
+ l.onConnectionEvent(this, event, null);
}
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 5b62e03..d18b317 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -103,6 +103,8 @@
private static final int MSG_SWAP_CONFERENCE = 19;
private static final int MSG_REJECT_WITH_MESSAGE = 20;
private static final int MSG_SILENCE = 21;
+ private static final int MSG_PULL_EXTERNAL_CALL = 22;
+ private static final int MSG_SEND_CALL_EVENT = 23;
private static Connection sNullConnection;
@@ -245,6 +247,20 @@
args.argi1 = proceed ? 1 : 0;
mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
}
+
+ @Override
+ public void pullExternalCall(String callId) {
+ mHandler.obtainMessage(MSG_PULL_EXTERNAL_CALL, callId).sendToTarget();
+ }
+
+ @Override
+ public void sendCallEvent(String callId, String event, Bundle extras) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = event;
+ args.arg3 = extras;
+ mHandler.obtainMessage(MSG_SEND_CALL_EVENT, args).sendToTarget();
+ }
};
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -382,6 +398,22 @@
}
break;
}
+ case MSG_PULL_EXTERNAL_CALL: {
+ pullExternalCall((String) msg.obj);
+ break;
+ }
+ case MSG_SEND_CALL_EVENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ String event = (String) args.arg2;
+ Bundle extras = (Bundle) args.arg3;
+ sendCallEvent(callId, event, extras);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
default:
break;
}
@@ -615,10 +647,10 @@
}
@Override
- public void onConnectionEvent(Connection connection, String event) {
+ public void onConnectionEvent(Connection connection, String event, Bundle extras) {
String id = mIdByConnection.get(connection);
if (id != null) {
- mAdapter.onConnectionEvent(id, event);
+ mAdapter.onConnectionEvent(id, event, extras);
}
}
};
@@ -864,6 +896,39 @@
}
}
+ /**
+ * Notifies a {@link Connection} of a request to pull an external call.
+ *
+ * See {@link Call#pullExternalCall()}.
+ *
+ * @param callId The ID of the call to pull.
+ */
+ private void pullExternalCall(String callId) {
+ Log.d(this, "pullExternalCall(%s)", callId);
+ Connection connection = findConnectionForAction(callId, "pullExternalCall");
+ if (connection != null) {
+ connection.onPullExternalCall();
+ }
+ }
+
+ /**
+ * Notifies a {@link Connection} of a call event.
+ *
+ * See {@link Call#sendCallEvent(String, Bundle)}.
+ *
+ * @param callId The ID of the call receiving the event.
+ * @param event The event.
+ * @param extras Extras associated with the event.
+ */
+ private void sendCallEvent(String callId, String event, Bundle extras) {
+ Log.d(this, "sendCallEvent(%s, %s)", callId, event);
+ Connection connection = findConnectionForAction(callId, "sendCallEvent");
+ if (connection != null) {
+ connection.onCallEvent(event, extras);
+ }
+
+ }
+
private void onPostDialContinue(String callId, boolean proceed) {
Log.d(this, "onPostDialContinue(%s)", callId);
findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 30fc5ad..e91128f 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -418,12 +418,13 @@
*
* @param callId The unique ID of the call.
* @param event The event.
+ * @param extras Extras associated with the event.
*/
- void onConnectionEvent(String callId, String event) {
+ void onConnectionEvent(String callId, String event, Bundle extras) {
Log.v(this, "onConnectionEvent: %s", event);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.onConnectionEvent(callId, event);
+ adapter.onConnectionEvent(callId, event, extras);
} catch (RemoteException ignored) {
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 6a8c1cb..4b15e54 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -245,7 +245,8 @@
case MSG_ON_CONNECTION_EVENT: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.onConnectionEvent((String) args.arg1, (String) args.arg2);
+ mDelegate.onConnectionEvent((String) args.arg1, (String) args.arg2,
+ (Bundle) args.arg3);
} finally {
args.recycle();
}
@@ -432,10 +433,11 @@
}
@Override
- public final void onConnectionEvent(String connectionId, String event) {
+ public final void onConnectionEvent(String connectionId, String event, Bundle extras) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = event;
+ args.arg3 = extras;
mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget();
}
};
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 2eef7ee..cf73d4f 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -64,6 +64,17 @@
*/
public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10;
+ /**
+ * Disconnected because the user did not locally answer the incoming call, but it was answered
+ * on another device where the call was ringing.
+ */
+ public static final int ANSWERED_ELSEWHERE = 11;
+
+ /**
+ * Disconnected because the call was pulled from the current device to another device.
+ */
+ public static final int CALL_PULLED = 12;
+
private int mDisconnectCode;
private CharSequence mDisconnectLabel;
private CharSequence mDisconnectDescription;
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 0cf7212ba..52ef4a7 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.os.Bundle;
import android.os.RemoteException;
import com.android.internal.telecom.IInCallAdapter;
@@ -251,6 +252,32 @@
}
/**
+ * Instructs Telecom to pull an external call to the local device.
+ *
+ * @param callId The callId to pull.
+ */
+ public void pullExternalCall(String callId) {
+ try {
+ mAdapter.pullExternalCall(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Intructs Telecom to send a call event.
+ *
+ * @param callId The callId to send the event for.
+ * @param event The event.
+ * @param extras Extras associated with the event.
+ */
+ public void sendCallEvent(String callId, String event, Bundle extras) {
+ try {
+ mAdapter.sendCallEvent(callId, event, extras);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
* Instructs Telecom to turn the proximity sensor on.
*/
public void turnProximitySensorOn() {
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 671399b..df6715d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -74,6 +75,7 @@
private static final int MSG_BRING_TO_FOREGROUND = 6;
private static final int MSG_ON_CAN_ADD_CALL_CHANGED = 7;
private static final int MSG_SILENCE_RINGER = 8;
+ private static final int MSG_ON_CONNECTION_EVENT = 9;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -118,6 +120,18 @@
case MSG_SILENCE_RINGER:
mPhone.internalSilenceRinger();
break;
+ case MSG_ON_CONNECTION_EVENT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ String event = (String) args.arg2;
+ Bundle extras = (Bundle) args.arg3;
+ mPhone.internalOnConnectionEvent(callId, event, extras);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
default:
break;
}
@@ -174,6 +188,15 @@
public void silenceRinger() {
mHandler.obtainMessage(MSG_SILENCE_RINGER).sendToTarget();
}
+
+ @Override
+ public void onConnectionEvent(String callId, String event, Bundle extras) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = event;
+ args.arg3 = extras;
+ mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget();
+ }
}
private Phone.Listener mPhoneListener = new Phone.Listener() {
@@ -426,6 +449,19 @@
}
/**
+ * Called when a {@link Call} has received a connection event issued by the
+ * {@link ConnectionService}.
+ * <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.
+ */
+ public void onConnectionEvent(Call call, String event, Bundle extras) {
+ }
+
+ /**
* Used to issue commands to the {@link Connection.VideoProvider} associated with a
* {@link Call}.
*/
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index d45938c..a4ef560 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.annotation.SystemApi;
+import android.os.Bundle;
import android.util.ArrayMap;
import java.util.Collections;
@@ -190,6 +191,13 @@
fireSilenceRinger();
}
+ final void internalOnConnectionEvent(String telecomId, String event, Bundle extras) {
+ Call call = mCallByTelecomCallId.get(telecomId);
+ if (call != null) {
+ call.internalOnConnectionEvent(event, extras);
+ }
+ }
+
/**
* Called to destroy the phone and cleanup any lingering calls.
*/
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 0185808..5b602eb 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -212,12 +212,14 @@
/**
* Handles a connection event propagated to this {@link RemoteConnection}.
+ * <p>
+ * Connection events originate from {@link Connection#sendConnectionEvent(String, Bundle)}.
*
* @param connection The {@code RemoteConnection} invoking this method.
* @param event The connection event.
- * @hide
+ * @param extras Extras associated with the event.
*/
- public void onConnectionEvent(RemoteConnection connection, String event) {}
+ public void onConnectionEvent(RemoteConnection connection, String event, Bundle extras) {}
}
/**
@@ -962,6 +964,20 @@
}
/**
+ * Instructs this {@link RemoteConnection} to pull itself to the local device.
+ * <p>
+ * See {@link Call#pullExternalCall()} for more information.
+ */
+ public void pullExternalCall() {
+ try {
+ if (mConnected) {
+ mConnectionService.pullExternalCall(mConnectionId);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
* Set the audio state of this {@code RemoteConnection}.
*
* @param state The audio state of this {@code RemoteConnection}.
@@ -1301,14 +1317,14 @@
}
/** @hide */
- void onConnectionEvent(final String event) {
+ void onConnectionEvent(final String event, final Bundle extras) {
for (CallbackRecord record : mCallbackRecords) {
final RemoteConnection connection = this;
final Callback callback = record.getCallback();
record.getHandler().post(new Runnable() {
@Override
public void run() {
- callback.onConnectionEvent(connection, event);
+ callback.onConnectionEvent(connection, event, extras);
}
});
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index b85382f..fa7183ac 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -332,9 +332,10 @@
}
@Override
- public void onConnectionEvent(String callId, String event) {
+ public void onConnectionEvent(String callId, String event, Bundle extras) {
if (mConnectionById.containsKey(callId)) {
- findConnectionForAction(callId, "onConnectionEvent").onConnectionEvent(event);
+ findConnectionForAction(callId, "onConnectionEvent").onConnectionEvent(event,
+ extras);
}
}
};
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 605e0d3..4fa8fe9 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -317,6 +317,18 @@
"android.telecom.IN_CALL_SERVICE_RINGING";
/**
+ * A boolean meta-data value indicating whether an {@link InCallService} wants to be informed of
+ * calls which have the {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} property. An external
+ * call is one which a {@link ConnectionService} knows about, but is not connected to directly.
+ * Dialer implementations (see {@link #getDefaultDialerPackage()}) which would like to be
+ * 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.
+ */
+ public static final String METADATA_INCLUDE_EXTERNAL_CALLS =
+ "android.telecom.INCLUDE_EXTERNAL_CALLS";
+
+ /**
* The dual tone multi-frequency signaling character sent to indicate the dialing system should
* pause for a predefined period.
*/
@@ -1442,7 +1454,7 @@
/**
* Creates the {@link Intent} which can be used with {@link Context#startActivity(Intent)} to
* launch the activity to manage blocked numbers.
- * <p> This method displays the UI to manage blocked numbers only if
+ * <p> The activity will display the UI to manage blocked numbers only if
* {@link android.provider.BlockedNumberContract#canCurrentUserBlockNumbers(Context)} returns
* {@code true} for the current user.
*/
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 8a54add..3ee0e9f 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -75,4 +75,8 @@
void swapConference(String conferenceCallId);
void onPostDialContinue(String callId, boolean proceed);
+
+ void pullExternalCall(String callId);
+
+ void sendCallEvent(String callId, String event, in Bundle extras);
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 569c244..dff1b11 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -87,5 +87,5 @@
void setExtras(String callId, in Bundle extras);
- void onConnectionEvent(String callId, String event);
+ void onConnectionEvent(String callId, String event, in Bundle extras);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 863fff2..0678fe2 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecom;
+import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
/**
@@ -60,4 +61,8 @@
void turnOnProximitySensor();
void turnOffProximitySensor(boolean screenOnImmediately);
+
+ void pullExternalCall(String callId);
+
+ void sendCallEvent(String callId, String event, in Bundle extras);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index 0088e0c..3e43fe2 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.telecom;
import android.app.PendingIntent;
+import android.os.Bundle;
import android.telecom.CallAudioState;
import android.telecom.ParcelableCall;
@@ -47,4 +48,6 @@
void onCanAddCallChanged(boolean canAddCall);
void silenceRinger();
+
+ void onConnectionEvent(String callId, String event, in Bundle extras);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1278c07..ea437d0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -251,6 +251,30 @@
*/
public static final String KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL = "carrier_wfc_ims_available_bool";
+ /**
+ * Default WFC_IMS_mode 0: WIFI_ONLY
+ * 1: CELLULAR_PREFERRED
+ * 2: WIFI_PREFERRED
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT =
+ "carrier_default_wfc_ims_mode_int";
+ /**
+ * Default WFC_IMS_enabled: true VoWiFi by default is on
+ * false VoWiFi by default is off
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL =
+ "carrier_default_wfc_ims_enabled_bool";
+
+ /**
+ * Default WFC_IMS_roaming_enabled: true VoWiFi roaming by default is on
+ * false VoWiFi roaming by default is off
+ * @hide
+ */
+ public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_BOOL =
+ "carrier_default_wfc_ims_roaming_enabled_bool";
+
/** Flag specifying whether provisioning is required for VOLTE. */
public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
= "carrier_volte_provisioning_required_bool";
@@ -613,6 +637,9 @@
sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_WFC_IMS_AVAILABLE_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_ENABLED_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);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
@@ -637,7 +664,7 @@
sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL, true);
sDefaults.putBoolean(KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL, true);
- sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL, true);
sDefaults.putBoolean(KEY_USE_HFA_FOR_PROVISIONING_BOOL, false);
sDefaults.putBoolean(KEY_USE_OTASP_FOR_PROVISIONING_BOOL, false);
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index ad007c6..39a9295 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -36,7 +36,7 @@
public class ServiceState implements Parcelable {
static final String LOG_TAG = "PHONE";
- static final boolean DBG = true;
+ static final boolean DBG = false;
static final boolean VDBG = false; // STOPSHIP if true
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e90be91..b482811 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4403,7 +4403,6 @@
Log.e(TAG, "Error calling ITelephony#getDataEnabled", e);
} catch (NullPointerException e) {
}
- Log.d(TAG, "getDataEnabled: retVal=" + retVal);
return retVal;
}
diff --git a/telephony/java/com/android/ims/ImsExternalCallState.aidl b/telephony/java/com/android/ims/ImsExternalCallState.aidl
new file mode 100644
index 0000000..c208702
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsExternalCallState.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 com.android.ims;
+
+parcelable ImsExternalCallState;
diff --git a/telephony/java/com/android/ims/ImsExternalCallState.java b/telephony/java/com/android/ims/ImsExternalCallState.java
new file mode 100644
index 0000000..edb6bfc
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsExternalCallState.java
@@ -0,0 +1,132 @@
+/*
+ * 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.ims;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+/*
+ * This file contains all the api's through which
+ * information received in Dialog Event Package can be
+ * queried
+ */
+
+/**
+ * Parcelable object to handle VICE Dialog Information
+ * @hide
+ */
+public class ImsExternalCallState implements Parcelable {
+
+ private static final String TAG = "ImsExternalCallState";
+
+ // Dialog States
+ public static final int CALL_STATE_CONFIRMED = 1;
+ public static final int CALL_STATE_TERMINATED = 2;
+ // Dialog Id
+ public int mCallId;
+ // Number
+ public Uri mAddress;
+ public boolean mIsPullable;
+ // CALL_STATE_CONFIRMED / CALL_STATE_TERMINATED
+ public int mCallState;
+ // ImsCallProfile#CALL_TYPE_*
+ public int mCallType;
+ public boolean mIsHeld;
+
+ public ImsExternalCallState() {
+ }
+
+ public ImsExternalCallState(Parcel in) {
+ mCallId = in.readInt();
+ ClassLoader classLoader = ImsExternalCallState.class.getClassLoader();
+ mAddress = in.readParcelable(classLoader);
+ mIsPullable = (in.readInt() != 0);
+ mCallState = in.readInt();
+ mCallType = in.readInt();
+ mIsHeld = (in.readInt() != 0);
+ Rlog.d(TAG, "ImsExternalCallState const = " +
+ "callid = " + getCallId() +
+ ", address = " + getAddress() +
+ ", mCallState = " + getCallState() +
+ ", calltype = " + getCallType() +
+ ", isheld = " + isCallHeld());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mCallId);
+ out.writeParcelable(mAddress, 0);
+ out.writeInt(mIsPullable ? 1 : 0);
+ out.writeInt(mCallState);
+ out.writeInt(mCallType);
+ out.writeInt(mIsHeld ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<ImsExternalCallState> CREATOR =
+ new Parcelable.Creator<ImsExternalCallState>() {
+ @Override
+ public ImsExternalCallState createFromParcel(Parcel in) {
+ return new ImsExternalCallState(in);
+ }
+
+ @Override
+ public ImsExternalCallState[] newArray(int size) {
+ return new ImsExternalCallState[size];
+ }
+ };
+
+ public int getCallId() {
+ return mCallId;
+ }
+
+ public Uri getAddress() {
+ return mAddress;
+ }
+
+ public boolean isCallPullable() {
+ return mIsPullable;
+ }
+
+ public int getCallState() {
+ return mCallState;
+ }
+
+ public int getCallType() {
+ return mCallType;
+ }
+
+ public boolean isCallHeld() {
+ return mIsHeld;
+ }
+
+ @Override
+ public String toString() {
+ return "ImsExternalCallState { mCallId = " + mCallId +
+ ", mAddress = " + mAddress +
+ ", mIsPullable = " + mIsPullable +
+ ", mCallState = " + mCallState +
+ ", mCallType = " + mCallType +
+ ", mIsHeld = " + mIsHeld + "}";
+ }
+}
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 558c1dc..f06d154 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -241,12 +241,12 @@
public static final int CODE_ANSWERED_ELSEWHERE = 1014;
/**
- * Call pull request failure from the network.
+ * For MultiEndpoint - Call Pull request has failed
*/
public static final int CODE_CALL_PULL_OUT_OF_SYNC = 1015;
/**
- * Call ended due to being pulled onto another device.
+ * For MultiEndpoint - Call has been pulled from primary to secondary
*/
public static final int CODE_CALL_END_CAUSE_CALL_PULL = 1016;
diff --git a/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl b/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
new file mode 100644
index 0000000..70a474e
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.ims.internal;
+
+import com.android.ims.ImsExternalCallState;
+
+/**
+ * A listener type for receiving notifications about DEP through IMS
+ *
+ * {@hide}
+ */
+interface IImsExternalCallStateListener {
+
+ /**
+ * Notifies client when Dialog Event Package update is received
+ *
+ * @param List<ImsExternalCallState> - External Call Dialog
+ *
+ * @return void.
+ */
+ void notifyRefreshExternalCallState(in List<ImsExternalCallState> externalCallDialogs);
+
+}
+
diff --git a/telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl b/telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl
new file mode 100644
index 0000000..1bfb9b2
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.ims.internal;
+
+import com.android.ims.internal.IImsExternalCallStateListener;
+
+/**
+ * Provides the ImsMultiEndpoint interface
+ *
+ * {@hide}
+ */
+interface IImsMultiEndpoint {
+ /**
+ * Sets the listener.
+ */
+ void setListener(in IImsExternalCallStateListener listener);
+
+
+ /**
+ * Query api to get the latest Dialog Event Package information
+ * Should be invoked only after setListener is done
+ */
+ void requestDialogEventPackageState();
+}
diff --git a/telephony/java/com/android/ims/internal/IImsService.aidl b/telephony/java/com/android/ims/internal/IImsService.aidl
index 30c48d7..a9614a6 100644
--- a/telephony/java/com/android/ims/internal/IImsService.aidl
+++ b/telephony/java/com/android/ims/internal/IImsService.aidl
@@ -19,12 +19,13 @@
import android.app.PendingIntent;
import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsRegistrationListener;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
import android.os.Message;
@@ -75,4 +76,9 @@
* Used to set current TTY Mode.
*/
void setUiTTYMode(int serviceId, int uiTtyMode, in Message onComplete);
+
+ /**
+ * MultiEndpoint interface for DEP.
+ */
+ IImsMultiEndpoint getMultiEndpointInterface(int serviceId);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index bba357e..4f0e036 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -92,6 +92,17 @@
int NO_SMS_TO_ACK = 48; /* ACK received when there is no SMS to ack */
int NETWORK_ERR = 49; /* Received error from network */
int REQUEST_RATE_LIMITED = 50; /* Operation denied due to overly-frequent requests */
+ int SIM_BUSY = 51; /* SIM is busy */
+ int SIM_FULL = 52; /* The target EF is full */
+ int NETWORK_REJECT = 53; /* Request is rejected by network */
+ int OPERATION_NOT_ALLOWED = 54; /* Not allowed the request now */
+ int EMPTY_RECORD = 55; /* The request record is empty */
+ int INVALID_SMS_FORMAT = 56; /* Invalid sms format */
+ int ENCODING_ERR = 57; /* Message not encoded properly */
+ int INVALID_SMSC_ADDRESS = 58; /* SMSC address specified is invalid */
+ int NO_SUCH_ENTRY = 59; /* No such entry present to perform the request */
+ int NETWORK_NOT_READY = 60; /* Network is not ready to perform the request */
+ int NOT_PROVISIONED = 61; /* Device doesnot have this value provisioned */
// Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
// reveal particular replacement for Generic failure
int OEM_ERROR_1 = 501;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 645c3a1..ea3b5c9 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -217,4 +217,12 @@
* or Earpiece, based on the default audio routing strategy.
*/
static final String PROPERTY_VIDEOCALL_AUDIO_OUTPUT = "persist.radio.call.audio.output";
+
+ /**
+ * For MultiEndpoint Feature
+ * If true: Dial intent is for call pull functionality
+ * if false: normal dial
+ */
+ static final String EXTRA_IS_CALL_PULL =
+ "android.telephony.extra.IS_CALL_PULL";
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 4bed941..ed7351f8 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -160,7 +160,7 @@
for (String pair : appNames) {
String[] parts = pair.split("\\^");
if (parts.length != 2) {
- Log.e(TAG, "The apps key is incorectly formatted");
+ Log.e(TAG, "The apps key is incorrectly formatted");
fail();
}
@@ -176,6 +176,10 @@
}
}
+ private boolean hasLeanback(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
private void createMappings() {
mNameToIntent = new LinkedHashMap<String, Intent>();
mNameToProcess = new LinkedHashMap<String, String>();
@@ -183,9 +187,12 @@
PackageManager pm = getInstrumentation().getContext()
.getPackageManager();
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
- intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+ intentToResolve.addCategory(hasLeanback(getInstrumentation().getContext()) ?
+ Intent.CATEGORY_LEANBACK_LAUNCHER :
+ Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
resolveLoop(ris, intentToResolve, pm);
+ // For Wear
intentToResolve = new Intent(WEARABLE_ACTION_GOOGLE);
ris = pm.queryIntentActivities(intentToResolve, 0);
resolveLoop(ris, intentToResolve, pm);
@@ -232,7 +239,7 @@
// report error if any of the following is true:
// * launch thread is alive
// * result is not null, but:
- // * result is not START_SUCESS
+ // * result is not START_SUCCESS
// * or in case of no force stop, result is not TASK_TO_FRONT either
if (t.isAlive() || (result != null
&& ((result.result != ActivityManager.START_SUCCESS)
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs
index caa947d..0a1742e 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs
@@ -14,10 +14,14 @@
for (int x = 0; x < HEIGHT; x += REGION_SIZE) {
bool interestingRegion = false;
- int regionColor = (int) rsGetElementAt_uchar4(ideal, x, y);
+ uchar4 regionColor = rsGetElementAt_uchar4(ideal, x, y);
for (int i = 0; i < REGION_SIZE && !interestingRegion; i++) {
for (int j = 0; j < REGION_SIZE && !interestingRegion; j++) {
- interestingRegion |= ((int) rsGetElementAt_uchar4(ideal, x + j, y + i)) != regionColor;
+ uchar4 testVal = rsGetElementAt_uchar4(ideal, x + j, y + i);
+ interestingRegion |= (testVal.r != regionColor.r);
+ interestingRegion |= (testVal.g != regionColor.g);
+ interestingRegion |= (testVal.b != regionColor.b);
+ interestingRegion |= (testVal.a != regionColor.a);
}
}
if (interestingRegion) {
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource.xml
new file mode 100644
index 0000000..8093b9d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource_debug.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource_debug.xml
new file mode 100644
index 0000000..fc24df5
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_extra_debug_resource_debug.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- debug-overrides not inside network-security-config should cause a parsing error -->
+<debug-overrides>
+ <trust-anchors>
+ <certificates src="system" />
+ </trust-anchors>
+</debug-overrides>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource.xml b/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource.xml
new file mode 100644
index 0000000..8093b9d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <base-config>
+ <trust-anchors>
+ </trust-anchors>
+ </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource_debug.xml b/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource_debug.xml
new file mode 100644
index 0000000..6a2ad37
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/extra_debug_resource_debug.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <debug-overrides>
+ <trust-anchors>
+ <certificates src="system" />
+ </trust-anchors>
+ </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
index 35e3ef4..10bcc18 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -431,4 +431,37 @@
TestUtils.assertConnectionSucceeds(context, "android.com", 443);
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
}
+
+ public void testExtraDebugResource() throws Exception {
+ XmlConfigSource source =
+ new XmlConfigSource(getContext(), R.xml.extra_debug_resource, true);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ assertFalse(appConfig.hasPerDomainConfigs());
+ NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+ MoreAsserts.assertNotEmpty(config.getTrustAnchors());
+
+ // Check that the _debug file is ignored if debug is false.
+ source = new XmlConfigSource(getContext(), R.xml.extra_debug_resource, false);
+ appConfig = new ApplicationConfig(source);
+ assertFalse(appConfig.hasPerDomainConfigs());
+ config = appConfig.getConfigForHostname("");
+ MoreAsserts.assertEmpty(config.getTrustAnchors());
+ }
+
+ public void testExtraDebugResourceIgnored() throws Exception {
+ // Verify that parsing the extra debug config resource fails only when debugging is true.
+ XmlConfigSource source =
+ new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource, false);
+ ApplicationConfig appConfig = new ApplicationConfig(source);
+ // Force parsing the config file.
+ appConfig.getConfigForHostname("");
+
+ source = new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource, true);
+ appConfig = new ApplicationConfig(source);
+ try {
+ appConfig.getConfigForHostname("");
+ fail("Bad extra debug resource did not fail to parse");
+ } catch (RuntimeException expected) {
+ }
+ }
}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
index c8a8d6c..6463e1f 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
@@ -47,7 +47,7 @@
OnBufferingUpdateListener, OnCompletionListener, OnErrorListener,
OnAudioFocusChangeListener {
private static final String TAG = "MediaPlayerManager";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static long sDebugInstanceId = 0;
private static final String[] SUPPORTED_FEATURES = {
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
index 4770c05..3ca96d2 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/TestSoundTriggerActivity.java
@@ -40,7 +40,7 @@
public class TestSoundTriggerActivity extends Activity {
private static final String TAG = "TestSoundTriggerActivity";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private SoundTriggerUtil mSoundTriggerUtil;
private Random mRandom;
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
index 407a9d7..e67134d 100644
--- a/tests/SoundTriggerTests/Android.mk
+++ b/tests/SoundTriggerTests/Android.mk
@@ -18,8 +18,16 @@
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
+ LOCAL_SRC_FILES := $(call all-subdir-java-files)
+ LOCAL_PRIVILEGED_MODULE := true
+ LOCAL_CERTIFICATE := platform
+ TARGET_OUT_DATA_APPS_PRIVILEGED := $(TARGET_OUT_DATA)/priv-app
+else
+ LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java
+endif
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := SoundTriggerTests
diff --git a/tests/SoundTriggerTests/AndroidManifest.xml b/tests/SoundTriggerTests/AndroidManifest.xml
index 5e5a108..f7454c7 100644
--- a/tests/SoundTriggerTests/AndroidManifest.xml
+++ b/tests/SoundTriggerTests/AndroidManifest.xml
@@ -14,7 +14,11 @@
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.hardware.soundtrigger">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.hardware.soundtrigger">
+ <uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
new file mode 100644
index 0000000..ad02d2b
--- /dev/null
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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 android.hardware.soundtrigger;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundTriggerManager;
+import android.os.ParcelUuid;
+import android.os.ServiceManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.app.ISoundTriggerService;
+
+import java.io.DataOutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.UUID;
+
+import org.mockito.MockitoAnnotations;
+
+public class GenericSoundModelTest extends AndroidTestCase {
+ static final int MSG_DETECTION_ERROR = -1;
+ static final int MSG_DETECTION_RESUME = 0;
+ static final int MSG_DETECTION_PAUSE = 1;
+ static final int MSG_KEYPHRASE_TRIGGER = 2;
+ static final int MSG_GENERIC_TRIGGER = 4;
+
+ private Random random = new Random();
+ private ArrayList<UUID> loadedModelUuids;
+ private ISoundTriggerService soundTriggerService;
+ private SoundTriggerManager soundTriggerManager;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+
+ Context context = getContext();
+ soundTriggerService = ISoundTriggerService.Stub.asInterface(
+ ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
+ soundTriggerManager = (SoundTriggerManager) context.getSystemService(
+ Context.SOUND_TRIGGER_SERVICE);
+
+ loadedModelUuids = new ArrayList<UUID>();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ for (UUID modelUuid : loadedModelUuids) {
+ soundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
+ }
+ super.tearDown();
+ }
+
+ GenericSoundModel new_sound_model() {
+ // Create sound model
+ byte[] data = new byte[1024];
+ random.nextBytes(data);
+ UUID modelUuid = UUID.randomUUID();
+ UUID mVendorUuid = UUID.randomUUID();
+ return new GenericSoundModel(modelUuid, mVendorUuid, data);
+ }
+
+ @SmallTest
+ public void testUpdateGenericSoundModel() throws Exception {
+ GenericSoundModel model = new_sound_model();
+
+ // Update sound model
+ soundTriggerService.updateSoundModel(model);
+ loadedModelUuids.add(model.uuid);
+
+ // Confirm it was updated
+ GenericSoundModel returnedModel =
+ soundTriggerService.getSoundModel(new ParcelUuid(model.uuid));
+ assertEquals(model, returnedModel);
+ }
+
+ @SmallTest
+ public void testDeleteGenericSoundModel() throws Exception {
+ GenericSoundModel model = new_sound_model();
+
+ // Update sound model
+ soundTriggerService.updateSoundModel(model);
+ loadedModelUuids.add(model.uuid);
+
+ // Delete sound model
+ soundTriggerService.deleteSoundModel(new ParcelUuid(model.uuid));
+ loadedModelUuids.remove(model.uuid);
+
+ // Confirm it was deleted
+ GenericSoundModel returnedModel =
+ soundTriggerService.getSoundModel(new ParcelUuid(model.uuid));
+ assertEquals(null, returnedModel);
+ }
+
+ @LargeTest
+ public void testStartStopGenericSoundModel() throws Exception {
+ GenericSoundModel model = new_sound_model();
+
+ boolean captureTriggerAudio = true;
+ boolean allowMultipleTriggers = true;
+ RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
+ null, null);
+ TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
+
+ // Update and start sound model recognition
+ soundTriggerService.updateSoundModel(model);
+ loadedModelUuids.add(model.uuid);
+ int r = soundTriggerService.startRecognition(new ParcelUuid(model.uuid), spyCallback,
+ config);
+ assertEquals("Could Not Start Recognition with code: " + r,
+ android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
+
+ // Stop recognition
+ r = soundTriggerService.stopRecognition(new ParcelUuid(model.uuid), spyCallback);
+ assertEquals("Could Not Stop Recognition with code: " + r,
+ android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
+ }
+
+ @LargeTest
+ public void testTriggerGenericSoundModel() throws Exception {
+ GenericSoundModel model = new_sound_model();
+
+ boolean captureTriggerAudio = true;
+ boolean allowMultipleTriggers = true;
+ RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
+ null, null);
+ TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
+
+ // Update and start sound model
+ soundTriggerService.updateSoundModel(model);
+ loadedModelUuids.add(model.uuid);
+ soundTriggerService.startRecognition(new ParcelUuid(model.uuid), spyCallback, config);
+
+ // Send trigger to stub HAL
+ Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
+ DataOutputStream out = new DataOutputStream(socket.getOutputStream());
+ out.writeBytes("trig " + model.uuid.toString() + "\r\n");
+ out.flush();
+ socket.close();
+
+ // Verify trigger was received
+ verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
+ }
+
+
+ public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
+ @Override
+ public void onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent) {
+ }
+
+ @Override
+ public void onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent) {
+ }
+
+ @Override
+ public void onError(int status) {
+ }
+
+ @Override
+ public void onRecognitionPaused() {
+ }
+
+ @Override
+ public void onRecognitionResumed() {
+ }
+ }
+}
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_clamp.xml b/tests/VectorDrawableTest/res/color/fill_gradient_linear_clamp.xml
new file mode 100644
index 0000000..6a24453
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_linear_clamp.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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:angle="90"
+ android:startColor="?android:attr/colorPrimary"
+ android:endColor="?android:attr/colorControlActivated"
+ android:centerColor="#00ff0000"
+ android:startX="0"
+ android:startY="0"
+ android:endX="50"
+ android:endY="50"
+ android:type="linear"
+ android:tileMode="clamp">
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.xml b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.xml
new file mode 100644
index 0000000..d342bca
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:angle="90"
+ android:startColor="?android:attr/colorPrimary"
+ android:endColor="?android:attr/colorControlActivated"
+ android:centerColor="#f00"
+ android:startX="0"
+ android:startY="0"
+ android:endX="50"
+ android:endY="50"
+ android:type="linear"
+ android:tileMode="mirror">
+ <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+ <item android:offset="0.4" android:color="#f00"/>
+ <item android:offset="0.4" android:color="#fff"/>
+ <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.xml b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.xml
new file mode 100644
index 0000000..afb45aa
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:angle="90"
+ android:startColor="?android:attr/colorPrimary"
+ android:endColor="?android:attr/colorControlActivated"
+ android:centerColor="#f00"
+ android:startX="0"
+ android:startY="0"
+ android:endX="50"
+ android:endY="50"
+ android:type="linear"
+ android:tileMode="repeat">
+ <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+ <item android:offset="0.4" android:color="#fff"/>
+ <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml
new file mode 100644
index 0000000..64b32f6
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml
@@ -0,0 +1,28 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerColor="#ff0000"
+ android:endColor="?android:attr/colorControlActivated"
+ android:centerX="300"
+ android:centerY="300"
+ android:gradientRadius="50"
+ android:startColor="?android:attr/colorPrimary"
+ android:type="radial"
+ android:tileMode="clamp">
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
index 51b0e17..c6cea7c 100644
--- a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
- <!--
+<!--
/*
* Copyright (C) 2016 The Android Open Source Project
*
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml
new file mode 100644
index 0000000..fb4346a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml
@@ -0,0 +1,31 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerColor="#ff0000"
+ android:endColor="#ff0000ff"
+ android:centerX="300"
+ android:centerY="300"
+ android:gradientRadius="50"
+ android:startColor="#ffffffff"
+ android:type="radial"
+ android:tileMode="repeat">
+ <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+ <item android:offset="0.4" android:color="#fff"/>
+ <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
index 8caa1b4..fefbe9f 100644
--- a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
- <!--
+<!--
/*
* Copyright (C) 2016 The Android Open Source Project
*
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.xml b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.xml
new file mode 100644
index 0000000..8b5ad7c
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerX="300"
+ android:centerY="300"
+ android:gradientRadius="50"
+ android:type="radial"
+ android:tileMode="mirror">
+ <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+ <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml
new file mode 100644
index 0000000..80f39f3e
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml
@@ -0,0 +1,28 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerColor="#ff0000"
+ android:endColor="#ff0000ff"
+ android:centerX="500"
+ android:centerY="500"
+ android:gradientRadius="10"
+ android:startColor="#ffffffff"
+ android:type="sweep"
+ android:tileMode="clamp">
+</gradient>
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml
new file mode 100644
index 0000000..0890bd6
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml
@@ -0,0 +1,31 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerX="500"
+ android:centerY="500"
+ android:gradientRadius="10"
+ android:type="sweep"
+ android:tileMode="mirror">
+ <item android:offset="-0.3" android:color="#f00"/>
+ <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+ <item android:offset="0.4" android:color="#0f0"/>
+ <item android:offset="0.6" android:color="#00f"/>
+ <item android:offset="0.7" android:color="?android:attr/colorControlActivated"/>
+ <item android:offset="1.5" android:color="#00f"/>
+</gradient>
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml
new file mode 100644
index 0000000..2ec5014
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml
@@ -0,0 +1,31 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:centerColor="#ff0000"
+ android:endColor="#ff0000ff"
+ android:centerX="500"
+ android:centerY="500"
+ android:gradientRadius="10"
+ android:startColor="#ffffffff"
+ android:type="sweep"
+ android:tileMode="repeat">
+ <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+ <item android:offset="0.4" android:color="#fff"/>
+ <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_clamp.xml b/tests/VectorDrawableTest/res/color/stroke_gradient_clamp.xml
new file mode 100644
index 0000000..3d746e7
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/stroke_gradient_clamp.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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:angle="90"
+ android:centerColor="#7f7f7f"
+ android:endColor="#ffffff"
+ android:startColor="#000000"
+ android:startX="0"
+ android:endX="50"
+ android:startY="0"
+ android:endY="0"
+ android:type="linear"
+ android:tileMode="clamp">
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml b/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml
new file mode 100644
index 0000000..352a2fd
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml
@@ -0,0 +1,29 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:startX="0"
+ android:endX="50"
+ android:startY="0"
+ android:endY="0"
+ android:type="linear"
+ android:tileMode="mirror">
+ <item android:offset="0.1" android:color="#f00"/>
+ <item android:offset="0.2" android:color="#2f0f"/>
+ <item android:offset="0.9" android:color="#f00f"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item_repeat.xml b/tests/VectorDrawableTest/res/color/stroke_gradient_item_repeat.xml
new file mode 100644
index 0000000..42281d1
--- /dev/null
+++ b/tests/VectorDrawableTest/res/color/stroke_gradient_item_repeat.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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:angle="90"
+ android:centerColor="#7f7f7f"
+ android:endColor="#ffffff"
+ android:startColor="#000000"
+ android:startX="0"
+ android:endX="50"
+ android:startY="0"
+ android:endY="0"
+ android:type="linear"
+ android:tileMode="repeat">
+ <item android:offset="0.1" android:color="#f00"/>
+ <item android:offset="0.2" android:color="#f0f"/>
+ <item android:offset="0.9" android:color="#f00f"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml
new file mode 100644
index 0000000..2fa440a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml
@@ -0,0 +1,91 @@
+<?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:height="64dp"
+ android:width="64dp"
+ android:viewportHeight="400"
+ android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+ <path
+ android:name="background1"
+ android:fillColor="@color/fill_gradient_linear_clamp"
+ android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background2"
+ android:fillColor="@color/fill_gradient_radial_clamp"
+ android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background3"
+ android:fillColor="@color/fill_gradient_sweep_clamp"
+ android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+ android:name="translateToCenterGroup"
+ android:translateX="50.0"
+ android:translateY="90.0" >
+ <path
+ android:name="twoLines"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_clamp"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="rotationGroup"
+ android:pivotX="0.0"
+ android:pivotY="0.0"
+ android:rotation="-45.0">
+ <path
+ android:name="twoLines1"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_clamp"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroup"
+ android:translateX="130.0"
+ android:translateY="160.0">
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines3"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_clamp"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+
+ <group
+ android:name="translateGroupHalf"
+ android:translateX="65.0"
+ android:translateY="80.0">
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines2"
+ android:pathData="@string/twoLinePathData"
+ android:fillColor="@color/fill_gradient_linear_clamp"
+ android:strokeColor="@color/stroke_gradient_clamp"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+ </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml
new file mode 100644
index 0000000..5a43f80
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml
@@ -0,0 +1,90 @@
+<?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:height="64dp"
+ android:width="64dp"
+ android:viewportHeight="400"
+ android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+ <path
+ android:name="background1"
+ android:fillColor="@color/fill_gradient_linear_item_repeat"
+ android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background2"
+ android:fillColor="@color/fill_gradient_radial_item_repeat"
+ android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background3"
+ android:fillColor="@color/fill_gradient_sweep_item_repeat"
+ android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+ android:name="translateToCenterGroup"
+ android:translateX="50.0"
+ android:translateY="90.0" >
+ <path
+ android:name="twoLines"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_item_repeat"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="rotationGroup"
+ android:pivotX="0.0"
+ android:pivotY="0.0"
+ android:rotation="-45.0">
+ <path
+ android:name="twoLines1"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_item_repeat"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroup"
+ android:translateX="130.0"
+ android:translateY="160.0">
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines3"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_item_repeat"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+
+ <group
+ android:name="translateGroupHalf"
+ android:translateX="65.0"
+ android:translateY="80.0">
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines2"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_item_repeat"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+ </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml
new file mode 100644
index 0000000..e8de7c2
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml
@@ -0,0 +1,90 @@
+<?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:height="64dp"
+ android:width="64dp"
+ android:viewportHeight="400"
+ android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+ <path
+ android:name="background1"
+ android:fillColor="@color/fill_gradient_linear_item_overlap_mirror"
+ android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background2"
+ android:fillColor="@color/fill_gradient_radial_item_short_mirror"
+ android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background3"
+ android:fillColor="@color/fill_gradient_sweep_item_long_mirror"
+ android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+ android:name="translateToCenterGroup"
+ android:translateX="50.0"
+ android:translateY="90.0" >
+ <path
+ android:name="twoLines"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_item_alpha_mirror"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="rotationGroup"
+ android:pivotX="0.0"
+ android:pivotY="0.0"
+ android:rotation="-45.0">
+ <path
+ android:name="twoLines1"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_item_alpha_mirror"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroup"
+ android:translateX="130.0"
+ android:translateY="160.0">
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines3"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_item_alpha_mirror"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+
+ <group
+ android:name="translateGroupHalf"
+ android:translateX="65.0"
+ android:translateY="80.0">
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines2"
+ android:pathData="@string/twoLinePathData"
+ android:strokeColor="@color/stroke_gradient_item_alpha"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+ </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
index 7172147..495d620 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
@@ -38,6 +38,9 @@
R.drawable.vector_icon_gradient_1,
R.drawable.vector_icon_gradient_2,
R.drawable.vector_icon_gradient_3,
+ R.drawable.vector_icon_gradient_1_clamp,
+ R.drawable.vector_icon_gradient_2_repeat,
+ R.drawable.vector_icon_gradient_3_mirror,
R.drawable.vector_icon_state_list_simple,
R.drawable.vector_icon_state_list_theme,
R.drawable.vector_drawable01,
diff --git a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
index 2494db7..54c944f9 100644
--- a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
+++ b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
@@ -31,7 +31,7 @@
public class TestEnrollmentActivity extends Activity {
private static final String TAG = "TestEnrollmentActivity";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
/** Keyphrase related constants, must match those defined in enrollment_application.xml */
private static final int KEYPHRASE_ID = 101;
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 3b01827..cbd8480 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -373,7 +373,7 @@
void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
config.unpackLanguage(language);
config.unpackRegion(region);
- if (config.localeScriptWasProvided) {
+ if (config.localeScript[0] && !config.localeScriptWasComputed) {
memcpy(script, config.localeScript, sizeof(config.localeScript));
}
@@ -388,10 +388,10 @@
if (script[0]) {
memcpy(out->localeScript, script, sizeof(out->localeScript));
- out->localeScriptWasProvided = true;
+ out->localeScriptWasComputed = false;
} else {
out->computeScript();
- out->localeScriptWasProvided = false;
+ out->localeScriptWasComputed = true;
}
if (variant[0]) {
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index f9d35ab..85d22ff 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -13,15 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
-# This tool is prebuilt if we're doing an app-only build.
-ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
+LOCAL_PATH:= $(call my-dir)
# ==========================================================
# Setup some common variables for the different build
# targets here.
# ==========================================================
-LOCAL_PATH:= $(call my-dir)
main := Main.cpp
sources := \
@@ -93,6 +90,7 @@
proto/TableProtoSerializer_test.cpp \
split/TableSplitter_test.cpp \
util/BigBuffer_test.cpp \
+ util/Files_test.cpp \
util/Maybe_test.cpp \
util/StringPiece_test.cpp \
util/Util_test.cpp \
@@ -127,10 +125,12 @@
libexpat \
libziparchive-host \
libpng \
- libbase
+ libbase \
+ libprotobuf-cpp-lite_static
-hostSharedLibs := \
- libprotobuf-cpp-lite
+# Do not add any shared libraries. AAPT2 is built to run on many
+# environments that may not have the required dependencies.
+hostSharedLibs :=
ifneq ($(strip $(USE_MINGW)),)
hostStaticLibs += libz
@@ -192,4 +192,6 @@
include $(BUILD_HOST_EXECUTABLE)
-endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
+ifeq ($(ONE_SHOT_MAKEFILE),)
+include $(call all-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
index 6acf3b0..12f56fc 100644
--- a/tools/aapt2/Locale.cpp
+++ b/tools/aapt2/Locale.cpp
@@ -253,7 +253,7 @@
void LocaleValue::initFromResTable(const ResTable_config& config) {
config.unpackLanguage(language);
config.unpackRegion(region);
- if (config.localeScriptWasProvided) {
+ if (config.localeScript[0] && !config.localeScriptWasComputed) {
memcpy(script, config.localeScript, sizeof(config.localeScript));
}
@@ -268,10 +268,10 @@
if (script[0]) {
memcpy(out->localeScript, script, sizeof(out->localeScript));
- out->localeScriptWasProvided = true;
+ out->localeScriptWasComputed = false;
} else {
out->computeScript();
- out->localeScriptWasProvided = false;
+ out->localeScriptWasComputed = true;
}
if (variant[0]) {
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index c71e249..03ca42b 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -77,13 +77,10 @@
ResourceType type;
std::u16string entry;
- ResourceName() = default;
+ ResourceName() : type(ResourceType::kRaw) {}
ResourceName(const StringPiece16& p, ResourceType t, const StringPiece16& e);
bool isValid() const;
- bool operator<(const ResourceName& rhs) const;
- bool operator==(const ResourceName& rhs) const;
- bool operator!=(const ResourceName& rhs) const;
std::u16string toString() const;
};
@@ -109,10 +106,6 @@
ResourceName toResourceName() const;
bool isValid() const;
-
- bool operator<(const ResourceNameRef& rhs) const;
- bool operator==(const ResourceNameRef& rhs) const;
- bool operator!=(const ResourceNameRef& rhs) const;
};
/**
@@ -138,17 +131,11 @@
uint8_t packageId() const;
uint8_t typeId() const;
uint16_t entryId() const;
- bool operator<(const ResourceId& rhs) const;
- bool operator==(const ResourceId& rhs) const;
};
struct SourcedResourceName {
ResourceName name;
size_t line;
-
- inline bool operator==(const SourcedResourceName& rhs) const {
- return name == rhs.name && line == rhs.line;
- }
};
struct ResourceFile {
@@ -227,16 +214,23 @@
return static_cast<uint16_t>(id);
}
-inline bool ResourceId::operator<(const ResourceId& rhs) const {
- return id < rhs.id;
+inline bool operator<(const ResourceId& lhs, const ResourceId& rhs) {
+ return lhs.id < rhs.id;
}
-inline bool ResourceId::operator==(const ResourceId& rhs) const {
- return id == rhs.id;
+inline bool operator>(const ResourceId& lhs, const ResourceId& rhs) {
+ return lhs.id > rhs.id;
}
-inline ::std::ostream& operator<<(::std::ostream& out,
- const ResourceId& resId) {
+inline bool operator==(const ResourceId& lhs, const ResourceId& rhs) {
+ return lhs.id == rhs.id;
+}
+
+inline bool operator!=(const ResourceId& lhs, const ResourceId& rhs) {
+ return lhs.id != rhs.id;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceId& resId) {
std::ios_base::fmtflags oldFlags = out.flags();
char oldFill = out.fill();
out << "0x" << std::internal << std::setfill('0') << std::setw(8)
@@ -266,29 +260,21 @@
return !package.empty() && !entry.empty();
}
-inline bool ResourceName::operator<(const ResourceName& rhs) const {
- return std::tie(package, type, entry)
+inline bool operator<(const ResourceName& lhs, const ResourceName& rhs) {
+ return std::tie(lhs.package, lhs.type, lhs.entry)
< std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline bool operator<(const ResourceName& lhs, const ResourceNameRef& b) {
- return ResourceNameRef(lhs) < b;
-}
-
-inline bool ResourceName::operator==(const ResourceName& rhs) const {
- return std::tie(package, type, entry)
+inline bool operator==(const ResourceName& lhs, const ResourceName& rhs) {
+ return std::tie(lhs.package, lhs.type, lhs.entry)
== std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline bool ResourceName::operator!=(const ResourceName& rhs) const {
- return std::tie(package, type, entry)
+inline bool operator!=(const ResourceName& lhs, const ResourceName& rhs) {
+ return std::tie(lhs.package, lhs.type, lhs.entry)
!= std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline bool operator!=(const ResourceName& lhs, const ResourceNameRef& rhs) {
- return ResourceNameRef(lhs) != rhs;
-}
-
inline std::u16string ResourceName::toString() const {
std::u16string result;
if (!package.empty()) {
@@ -333,18 +319,18 @@
return !package.empty() && !entry.empty();
}
-inline bool ResourceNameRef::operator<(const ResourceNameRef& rhs) const {
- return std::tie(package, type, entry)
+inline bool operator<(const ResourceNameRef& lhs, const ResourceNameRef& rhs) {
+ return std::tie(lhs.package, lhs.type, lhs.entry)
< std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline bool ResourceNameRef::operator==(const ResourceNameRef& rhs) const {
- return std::tie(package, type, entry)
+inline bool operator==(const ResourceNameRef& lhs, const ResourceNameRef& rhs) {
+ return std::tie(lhs.package, lhs.type, lhs.entry)
== std::tie(rhs.package, rhs.type, rhs.entry);
}
-inline bool ResourceNameRef::operator!=(const ResourceNameRef& rhs) const {
- return std::tie(package, type, entry)
+inline bool operator!=(const ResourceNameRef& lhs, const ResourceNameRef& rhs) {
+ return std::tie(lhs.package, lhs.type, lhs.entry)
!= std::tie(rhs.package, rhs.type, rhs.entry);
}
@@ -355,6 +341,18 @@
return out << name.type << "/" << name.entry;
}
+inline bool operator<(const ResourceName& lhs, const ResourceNameRef& b) {
+ return ResourceNameRef(lhs) < b;
+}
+
+inline bool operator!=(const ResourceName& lhs, const ResourceNameRef& rhs) {
+ return ResourceNameRef(lhs) != rhs;
+}
+
+inline bool operator==(const SourcedResourceName& lhs, const SourcedResourceName& rhs) {
+ return lhs.name == rhs.name && lhs.line == rhs.line;
+}
+
} // namespace aapt
#endif // AAPT_RESOURCE_H
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index b100e84..9704d970 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -81,6 +81,12 @@
// Recursively adds resources to the ResourceTable.
static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
+ StringPiece16 trimmedComment = util::trimWhitespace(res->comment);
+ if (trimmedComment.size() != res->comment.size()) {
+ // Only if there was a change do we re-assign.
+ res->comment = trimmedComment.toString();
+ }
+
if (res->symbolState) {
Symbol symbol;
symbol.state = res->symbolState.value();
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 5f9719e..2452a1d 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -422,10 +422,6 @@
}
class CompileContext : public IAaptContext {
-private:
- StdErrDiagnostics mDiagnostics;
- bool mVerbose = false;
-
public:
void setVerbose(bool val) {
mVerbose = val;
@@ -444,18 +440,24 @@
return nullptr;
}
- StringPiece16 getCompilationPackage() override {
- return {};
+ const std::u16string& getCompilationPackage() override {
+ static std::u16string empty;
+ return empty;
}
uint8_t getPackageId() override {
return 0x0;
}
- ISymbolTable* getExternalSymbols() override {
+ SymbolTable* getExternalSymbols() override {
abort();
return nullptr;
}
+
+private:
+ StdErrDiagnostics mDiagnostics;
+ bool mVerbose = false;
+
};
/**
diff --git a/tools/aapt2/data/AndroidManifest.xml b/tools/aapt2/data/AndroidManifest.xml
deleted file mode 100644
index d3b2fbe..0000000
--- a/tools/aapt2/data/AndroidManifest.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.app">
- <application
- android:name=".ActivityMain">
- </application>
-</manifest>
diff --git a/tools/aapt2/data/Makefile b/tools/aapt2/data/Makefile
deleted file mode 100644
index 37012de..0000000
--- a/tools/aapt2/data/Makefile
+++ /dev/null
@@ -1,61 +0,0 @@
-##
-# Environment dependent variables
-##
-
-AAPT := aapt2
-ZIPALIGN := zipalign -f 4
-FRAMEWORK := ../../../../../out/target/common/obj/APPS/framework-res_intermediates/package-export.apk
-
-##
-# Project depenedent variables
-##
-
-LOCAL_PACKAGE := com.android.app
-LOCAL_RESOURCE_DIR := res
-LOCAL_LIBS := lib/out/package.apk
-LOCAL_OUT := out
-LOCAL_GEN := out/gen
-LOCAL_PROGUARD := out/proguard.rule
-
-##
-# AAPT2 custom rules.
-##
-
-PRIVATE_R_FILE := $(LOCAL_GEN)/$(subst .,/,$(LOCAL_PACKAGE))/R.java
-$(info PRIVATE_R_FILE = $(PRIVATE_R_FILE))
-
-# Eg: framework.apk, etc.
-PRIVATE_INCLUDES := $(FRAMEWORK)
-$(info PRIVATE_INCLUDES = $(PRIVATE_INCLUDES))
-
-# Eg: res/drawable/icon.png, res/values/styles.xml
-PRIVATE_RESOURCES := $(shell find $(LOCAL_RESOURCE_DIR) -mindepth 1 -maxdepth 2 -type f)
-$(info PRIVATE_RESOURCES = $(PRIVATE_RESOURCES))
-
-PRIVATE_RESOURCE_OBJECTS := $(subst /,_,$(patsubst $(LOCAL_RESOURCE_DIR)/%,%,$(filter $(LOCAL_RESOURCE_DIR)/values%,$(PRIVATE_RESOURCES))))
-PRIVATE_RESOURCE_OBJECTS := $(addprefix $(LOCAL_OUT)/,$(PRIVATE_RESOURCE_OBJECTS:.xml=.arsc.flat))
-$(info PRIVATE_RESOURCE_OBJECTS = $(PRIVATE_RESOURCE_OBJECTS))
-
-PRIVATE_FILE_OBJECTS := $(subst /,_,$(patsubst $(LOCAL_RESOURCE_DIR)/%,%,$(filter-out $(LOCAL_RESOURCE_DIR)/values%,$(PRIVATE_RESOURCES))))
-PRIVATE_FILE_OBJECTS := $(addprefix $(LOCAL_OUT)/,$(addsuffix .flat,$(PRIVATE_FILE_OBJECTS)))
-$(info PRIVATE_FILE_OBJECTS = $(PRIVATE_FILE_OBJECTS))
-
-.SECONDEXPANSION:
-
-$(LOCAL_OUT)/%.arsc.flat: $(LOCAL_RESOURCE_DIR)/$$(subst _,/,%).xml
- $(AAPT) compile -o $(LOCAL_OUT) $<
-
-$(LOCAL_OUT)/%.flat: $(LOCAL_RESOURCE_DIR)/$$(subst _,/,%)
- $(AAPT) compile -o $(LOCAL_OUT) $<
-
-$(LOCAL_PROGUARD) $(LOCAL_OUT)/package.apk: AndroidManifest.xml
-$(PRIVATE_R_FILE) $(LOCAL_PROGUARD) $(LOCAL_OUT)/package.apk: $(PRIVATE_FILE_OBJECTS) $(PRIVATE_RESOURCE_OBJECTS)
- $(AAPT) link -o $(LOCAL_OUT)/package.apk --manifest AndroidManifest.xml --java $(LOCAL_GEN) --proguard $(LOCAL_PROGUARD) -I $(PRIVATE_INCLUDES) $(filter-out AndroidManifest.xml,$^) -v
-
-# Create the out directory if needed.
-dummy := $(shell test -d $(LOCAL_OUT) || mkdir -p $(LOCAL_OUT))
-
-.PHONY: all
-all: $(LOCAL_OUT)/package.apk $(LOCAL_PROGUARD) $(PRIVATE_R_FILE)
-
-.DEFAULT_GOAL := all
diff --git a/tools/aapt2/data/lib/AndroidManifest.xml b/tools/aapt2/data/lib/AndroidManifest.xml
deleted file mode 100644
index 08b468e..0000000
--- a/tools/aapt2/data/lib/AndroidManifest.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.appcompat">
-
- <uses-feature android:name="bloooop" />
-</manifest>
diff --git a/tools/aapt2/data/lib/Makefile b/tools/aapt2/data/lib/Makefile
deleted file mode 100644
index 741be9a..0000000
--- a/tools/aapt2/data/lib/Makefile
+++ /dev/null
@@ -1,81 +0,0 @@
-##
-# Environment dependent variables
-##
-
-AAPT := aapt2
-ZIPALIGN := zipalign -f 4
-FRAMEWORK := ../../../../../../out/target/common/obj/APPS/framework-res_intermediates/package-export.apk
-
-##
-# Project depenedent variables
-##
-
-LOCAL_PACKAGE := android.appcompat
-LOCAL_RESOURCE_DIR := res
-LOCAL_OUT := out
-LOCAL_GEN := out/gen
-
-##
-# AAPT2 custom rules.
-##
-
-PRIVATE_APK_UNALIGNED := $(LOCAL_OUT)/package-unaligned.apk
-PRIVATE_APK_ALIGNED := $(LOCAL_OUT)/package.apk
-
-# Eg: framework.apk, etc.
-PRIVATE_LIBS := $(FRAMEWORK)
-$(info PRIVATE_LIBS = $(PRIVATE_LIBS))
-
-# Eg: gen/com/android/app/R.java
-PRIVATE_R_JAVA := $(LOCAL_GEN)/$(subst .,/,$(LOCAL_PACKAGE))/R.java
-$(info PRIVATE_R_JAVA = $(PRIVATE_R_JAVA))
-
-# Eg: res/drawable/icon.png, res/values/styles.xml
-PRIVATE_RESOURCES := $(shell find $(LOCAL_RESOURCE_DIR) -mindepth 1 -maxdepth 2 -type f)
-$(info PRIVATE_RESOURCES = $(PRIVATE_RESOURCES))
-
-# Eg: drawable, values, layouts
-PRIVATE_RESOURCE_TYPES := \
- $(patsubst $(LOCAL_RESOURCE_DIR)/%/,%,$(sort $(dir $(PRIVATE_RESOURCES))))
-$(info PRIVATE_RESOURCE_TYPES = $(PRIVATE_RESOURCE_TYPES))
-
-# Eg: out/values-v4.apk, out/drawable-xhdpi.apk
-PRIVATE_INTERMEDIATE_TABLES := $(patsubst %,$(LOCAL_OUT)/%.apk,$(PRIVATE_RESOURCE_TYPES))
-$(info PRIVATE_INTERMEDIATE_TABLES = $(PRIVATE_INTERMEDIATE_TABLES))
-
-# Generates rules for collect phase.
-# $1: Resource type (values-v4)
-# returns: out/values-v4.apk: res/values-v4/styles.xml res/values-v4/colors.xml
-define make-collect-rule
-$(LOCAL_OUT)/$1.apk: $(filter $(LOCAL_RESOURCE_DIR)/$1/%,$(PRIVATE_RESOURCES))
- $(AAPT) compile -o $$@ $$^
-endef
-
-# Collect: out/values-v4.apk <- res/values-v4/styles.xml res/values-v4/colors.xml
-$(foreach d,$(PRIVATE_RESOURCE_TYPES),$(eval $(call make-collect-rule,$d)))
-
-# Link: out/package-unaligned.apk <- out/values-v4.apk out/drawable-v4.apk
-$(PRIVATE_APK_UNALIGNED): $(PRIVATE_INTERMEDIATE_TABLES) $(PRIVATE_LIBS) AndroidManifest.xml
- $(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_LIBS)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) --static-lib
-
-# R.java: gen/com/android/app/R.java <- out/resources.arsc
-# No action since R.java is generated when out/resources.arsc is.
-$(PRIVATE_R_JAVA): $(PRIVATE_APK_UNALIGNED)
-
-# Assemble: zip out/resources.arsc AndroidManifest.xml and res/**/*
-$(PRIVATE_APK_ALIGNED): $(PRIVATE_APK_UNALIGNED)
- $(ZIPALIGN) $< $@
-
-# Create the out directory if needed.
-dummy := $(shell test -d $(LOCAL_OUT) || mkdir -p $(LOCAL_OUT))
-
-.PHONY: java
-java: $(PRIVATE_R_JAVA)
-
-.PHONY: assemble
-assemble: $(PRIVATE_APK_ALIGNED)
-
-.PHONY: all
-all: assemble java
-
-.DEFAULT_GOAL := all
diff --git a/tools/aapt2/data/lib/res/layout/main.xml b/tools/aapt2/data/lib/res/layout/main.xml
deleted file mode 100644
index 187ed2d..0000000
--- a/tools/aapt2/data/lib/res/layout/main.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
diff --git a/tools/aapt2/data/lib/res/raw/hello.txt b/tools/aapt2/data/lib/res/raw/hello.txt
deleted file mode 100644
index 44fc22b..0000000
--- a/tools/aapt2/data/lib/res/raw/hello.txt
+++ /dev/null
@@ -1 +0,0 @@
-Oh howdy there
diff --git a/tools/aapt2/data/lib/res/values/styles.xml b/tools/aapt2/data/lib/res/values/styles.xml
deleted file mode 100644
index 4ce6333..0000000
--- a/tools/aapt2/data/lib/res/values/styles.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="Platform.AppCompat" parent="@android:style/Theme">
- <item name="android:windowNoTitle">true</item>
- </style>
-
- <bool name="allow">true</bool>
-</resources>
diff --git a/tools/aapt2/data/res/drawable/image.xml b/tools/aapt2/data/res/drawable/image.xml
deleted file mode 100644
index 9b38739..0000000
--- a/tools/aapt2/data/res/drawable/image.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector />
diff --git a/tools/aapt2/data/res/layout-v21/main.xml b/tools/aapt2/data/res/layout-v21/main.xml
deleted file mode 100644
index 959b349..0000000
--- a/tools/aapt2/data/res/layout-v21/main.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
- android:id="@+id/view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-</LinearLayout>
diff --git a/tools/aapt2/data/res/layout/main.xml b/tools/aapt2/data/res/layout/main.xml
deleted file mode 100644
index 8a5e9e8..0000000
--- a/tools/aapt2/data/res/layout/main.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
- android:id="@+id/view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <fragment class="android.test.sample.App$Inner" />
-
- <variable name="user" type="com.android.User" />
-
- <View xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/me"
- android:layout_width="1dp"
- android:onClick="doClick"
- android:text="@{user.name}"
- android:background="#ffffff"
- android:layout_height="match_parent"
- app:flags="complex|weak"
- android:colorAccent="#ffffff"/>
-</LinearLayout>
diff --git a/tools/aapt2/data/res/values-v4/styles.xml b/tools/aapt2/data/res/values-v4/styles.xml
deleted file mode 100644
index 979a82a..0000000
--- a/tools/aapt2/data/res/values-v4/styles.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="App" parent="android:Theme.Material">
- <item name="android:colorAccent">@color/accent</item>
- <item name="android:text">Hey</item>
- </style>
-</resources>
diff --git a/tools/aapt2/data/res/values/colors.xml b/tools/aapt2/data/res/values/colors.xml
deleted file mode 100644
index 89db5fb..0000000
--- a/tools/aapt2/data/res/values/colors.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <color name="primary">#f44336</color>
- <color name="primary_dark">#b71c1c</color>
- <color name="accent">#fdd835</color>
-</resources>
diff --git a/tools/aapt2/data/res/values/styles.xml b/tools/aapt2/data/res/values/styles.xml
deleted file mode 100644
index 2bbdad1..0000000
--- a/tools/aapt2/data/res/values/styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
- <style name="App">
- <item name="android:background">@color/primary</item>
- <item name="android:colorPrimary">@color/primary</item>
- <item name="android:colorPrimaryDark">@color/primary_dark</item>
- <item name="android:colorAccent">@color/accent</item>
- </style>
- <attr name="custom" format="reference" />
- <style name="Pop">
- <item name="custom">@android:drawable/btn_default</item>
- <item name="android:focusable">true</item>
- </style>
- <string name="yo">@string/wow</string>
-
- <declare-styleable name="View">
- <attr name="custom" />
- <attr name="decor">
- <enum name="no-border" value="0"/>
- <enum name="border" value="1"/>
- <enum name="shadow" value="2"/>
- </attr>
- </declare-styleable>
-
-</resources>
diff --git a/tools/aapt2/data/res/values/test.xml b/tools/aapt2/data/res/values/test.xml
deleted file mode 100644
index d7ab1c8..0000000
--- a/tools/aapt2/data/res/values/test.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="hooha"><font bgcolor="#ffffff">Hey guys!</font> <xliff:g>My</xliff:g> name is <b>Adam</b>. How <b><i>are</i></b> you?</string>
- <public name="hooha" type="string" id="0x7f020001"/>
- <string name="wow">@android:string/ok</string>
- <public name="layout_width" type="attr" />
- <attr name="layout_width" format="boolean" />
- <attr name="flags">
- <flag name="complex" value="1" />
- <flag name="pub" value="2" />
- <flag name="weak" value="4" />
- </attr>
-</resources>
diff --git a/tools/aapt2/data/resources.arsc b/tools/aapt2/data/resources.arsc
deleted file mode 100644
index 6a416df..0000000
--- a/tools/aapt2/data/resources.arsc
+++ /dev/null
Binary files differ
diff --git a/tools/aapt2/data/resources_base.arsc b/tools/aapt2/data/resources_base.arsc
deleted file mode 100644
index f9d0610..0000000
--- a/tools/aapt2/data/resources_base.arsc
+++ /dev/null
Binary files differ
diff --git a/tools/aapt2/data/resources_hdpi.arsc b/tools/aapt2/data/resources_hdpi.arsc
deleted file mode 100644
index 97232a3..0000000
--- a/tools/aapt2/data/resources_hdpi.arsc
+++ /dev/null
Binary files differ
diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp
index ad7de0a..56b9f9a 100644
--- a/tools/aapt2/dump/Dump.cpp
+++ b/tools/aapt2/dump/Dump.cpp
@@ -17,6 +17,7 @@
#include "Debug.h"
#include "Diagnostics.h"
#include "Flags.h"
+#include "io/ZipArchive.h"
#include "process/IResourceTableConsumer.h"
#include "proto/ProtoSerialize.h"
#include "util/Files.h"
@@ -56,6 +57,35 @@
void tryDumpFile(IAaptContext* context, const std::string& filePath) {
std::string err;
+ std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::create(filePath, &err);
+ if (zip) {
+ io::IFile* file = zip->findFile("resources.arsc.flat");
+ if (file) {
+ std::unique_ptr<io::IData> data = file->openAsData();
+ if (!data) {
+ context->getDiagnostics()->error(DiagMessage(filePath)
+ << "failed to open resources.arsc.flat");
+ return;
+ }
+
+ pb::ResourceTable pbTable;
+ if (!pbTable.ParseFromArray(data->data(), data->size())) {
+ context->getDiagnostics()->error(DiagMessage(filePath)
+ << "invalid resources.arsc.flat");
+ return;
+ }
+
+ std::unique_ptr<ResourceTable> table = deserializeTableFromPb(
+ pbTable, Source(filePath), context->getDiagnostics());
+ if (table) {
+ DebugPrintTableOptions debugPrintTableOptions;
+ debugPrintTableOptions.showSources = true;
+ Debug::printTable(table.get(), debugPrintTableOptions);
+ }
+ }
+ return;
+ }
+
Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
if (!file) {
context->getDiagnostics()->error(DiagMessage(filePath) << err);
@@ -90,15 +120,16 @@
return nullptr;
}
- StringPiece16 getCompilationPackage() override {
- return {};
+ const std::u16string& getCompilationPackage() override {
+ static std::u16string empty;
+ return empty;
}
uint8_t getPackageId() override {
return 0;
}
- ISymbolTable* getExternalSymbols() override {
+ SymbolTable* getExternalSymbols() override {
abort();
return nullptr;
}
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index 8219462..3eac633 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -144,9 +144,9 @@
}
static bool cmpXmlAttributeById(const xml::Attribute* a, const xml::Attribute* b) {
- if (a->compiledAttribute) {
- if (b->compiledAttribute) {
- return a->compiledAttribute.value().id < b->compiledAttribute.value().id;
+ if (a->compiledAttribute && a->compiledAttribute.value().id) {
+ if (b->compiledAttribute && b->compiledAttribute.value().id) {
+ return a->compiledAttribute.value().id.value() < b->compiledAttribute.value().id.value();
}
return true;
} else if (!b->compiledAttribute) {
@@ -167,8 +167,8 @@
// Filter the attributes.
for (xml::Attribute& attr : node->attributes) {
- if (mOptions.maxSdkLevel && attr.compiledAttribute) {
- size_t sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id);
+ if (mOptions.maxSdkLevel && attr.compiledAttribute && attr.compiledAttribute.value().id) {
+ size_t sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id.value());
if (sdkLevel > mOptions.maxSdkLevel.value()) {
continue;
}
@@ -191,8 +191,8 @@
uint16_t attributeIndex = 1;
for (const xml::Attribute* xmlAttr : mFilteredAttrs) {
// Assign the indices for specific attributes.
- if (xmlAttr->compiledAttribute &&
- xmlAttr->compiledAttribute.value().id == kIdAttr) {
+ if (xmlAttr->compiledAttribute && xmlAttr->compiledAttribute.value().id &&
+ xmlAttr->compiledAttribute.value().id.value() == kIdAttr) {
flatElem->idIndex = util::hostToDevice16(attributeIndex);
} else if (xmlAttr->namespaceUri.empty()) {
if (xmlAttr->name == u"class") {
@@ -208,7 +208,7 @@
flatAttr->rawValue.index = util::hostToDevice32(-1);
- if (!xmlAttr->compiledAttribute) {
+ if (!xmlAttr->compiledAttribute || !xmlAttr->compiledAttribute.value().id) {
// The attribute has no associated ResourceID, so the string order doesn't matter.
addString(xmlAttr->name, kLowPriority, &flatAttr->name);
} else {
@@ -221,17 +221,17 @@
// Lookup the StringPool for this package and make the reference there.
const xml::AaptAttribute& aaptAttr = xmlAttr->compiledAttribute.value();
- StringPool::Ref nameRef = mPackagePools[aaptAttr.id.packageId()].makeRef(
- xmlAttr->name, StringPool::Context{ aaptAttr.id.id });
+ StringPool::Ref nameRef = mPackagePools[aaptAttr.id.value().packageId()].makeRef(
+ xmlAttr->name, StringPool::Context{ aaptAttr.id.value().id });
// Add it to the list of strings to flatten.
addString(nameRef, &flatAttr->name);
+ }
- if (mOptions.keepRawValues) {
- // Keep raw values (this is for static libraries).
- // TODO(with a smarter inflater for binary XML, we can do without this).
- addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
- }
+ if (mOptions.keepRawValues || !xmlAttr->compiledValue) {
+ // Keep raw values if the value is not compiled or
+ // if we're building a static library (need symbols).
+ addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
}
if (xmlAttr->compiledValue) {
@@ -240,7 +240,6 @@
} else {
// Flatten as a regular string type.
flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
- addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
addString(xmlAttr->value, kLowPriority,
(ResStringPool_ref*) &flatAttr->typedValue.data);
}
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index 8648879..fef5ca3 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -32,7 +32,7 @@
mContext = test::ContextBuilder()
.setCompilationPackage(u"com.app.test")
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
- .setSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbolSource(test::StaticSymbolSourceBuilder()
.addSymbol(u"@android:attr/id", ResourceId(0x010100d0),
test::AttributeBuilder().build())
.addSymbol(u"@com.app.test:id/id", ResourceId(0x7f020000))
diff --git a/tools/aapt2/integration-tests/Android.mk b/tools/aapt2/integration-tests/Android.mk
new file mode 100644
index 0000000..6361f9b
--- /dev/null
+++ b/tools/aapt2/integration-tests/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH := $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/AppOne/Android.mk b/tools/aapt2/integration-tests/AppOne/Android.mk
new file mode 100644
index 0000000..bc40a62
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/Android.mk
@@ -0,0 +1,28 @@
+#
+# 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_USE_AAPT2 := true
+LOCAL_PACKAGE_NAME := AaptTestAppOne
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ AaptTestStaticLibOne \
+ AaptTestStaticLibTwo
+LOCAL_AAPT_FLAGS := --no-version-vectors
+include $(BUILD_PACKAGE)
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml
similarity index 64%
rename from packages/DocumentsUI/res/animator/dir_frozen.xml
rename to tools/aapt2/integration-tests/AppOne/AndroidManifest.xml
index b541d13..b6d8f2d 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,4 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<manifest package="com.android.aapt.app.one" />
diff --git a/tools/aapt2/data/res/drawable/icon.png b/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png
similarity index 100%
rename from tools/aapt2/data/res/drawable/icon.png
rename to tools/aapt2/integration-tests/AppOne/res/drawable/icon.png
Binary files differ
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/AppOne/res/drawable/image.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to tools/aapt2/integration-tests/AppOne/res/drawable/image.xml
index b541d13..6132a75d 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/image.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,4 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<vector />
diff --git a/tools/aapt2/data/res/drawable/test.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png
similarity index 100%
rename from tools/aapt2/data/res/drawable/test.9.png
rename to tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png
Binary files differ
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml
similarity index 60%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml
index b541d13..9f5a4a8 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,9 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
+ android:id="@+id/view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+</LinearLayout>
diff --git a/tools/aapt2/integration-tests/AppOne/res/layout/main.xml b/tools/aapt2/integration-tests/AppOne/res/layout/main.xml
new file mode 100644
index 0000000..ab1a251
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/layout/main.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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
+ android:id="@+id/view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <fragment class="android.test.sample.App$Inner" />
+
+ <variable name="user" type="com.android.User" />
+
+ <View xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/me"
+ android:layout_width="1dp"
+ android:onClick="doClick"
+ android:text="@{user.name}"
+ android:background="#ffffff"
+ android:layout_height="match_parent"
+ app:flags="complex|weak"
+ android:colorAccent="#ffffff"/>
+</LinearLayout>
diff --git a/tools/aapt2/data/res/raw/test.txt b/tools/aapt2/integration-tests/AppOne/res/raw/test.txt
similarity index 100%
rename from tools/aapt2/data/res/raw/test.txt
rename to tools/aapt2/integration-tests/AppOne/res/raw/test.txt
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml
index b541d13..d8c11e2 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,9 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<resources>
+ <style name="App" parent="android:Theme.Material">
+ <item name="android:colorAccent">@color/accent</item>
+ <item name="android:text">Hey</item>
+ </style>
+</resources>
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/AppOne/res/values/colors.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to tools/aapt2/integration-tests/AppOne/res/values/colors.xml
index b541d13..4df5077 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/AppOne/res/values/colors.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,8 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<resources>
+ <color name="primary">#f44336</color>
+ <color name="primary_dark">#b71c1c</color>
+ <color name="accent">#fdd835</color>
+</resources>
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/styles.xml b/tools/aapt2/integration-tests/AppOne/res/values/styles.xml
new file mode 100644
index 0000000..f05845c
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/values/styles.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.
+-->
+
+<resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
+ <style name="App">
+ <item name="android:background">@color/primary</item>
+ <item name="android:colorPrimary">@color/primary</item>
+ <item name="android:colorPrimaryDark">@color/primary_dark</item>
+ <item name="android:colorAccent">@color/accent</item>
+ </style>
+ <attr name="custom" format="reference" />
+ <style name="Pop">
+ <item name="custom">@android:drawable/btn_default</item>
+ <item name="android:focusable">true</item>
+ </style>
+ <string name="yo">@string/wow</string>
+
+ <declare-styleable name="View">
+ <attr name="custom" />
+ <attr name="decor">
+ <enum name="no-border" value="0"/>
+ <enum name="border" value="1"/>
+ <enum name="shadow" value="2"/>
+ </attr>
+ </declare-styleable>
+
+</resources>
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/test.xml b/tools/aapt2/integration-tests/AppOne/res/values/test.xml
new file mode 100644
index 0000000..f4b7471
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/values/test.xml
@@ -0,0 +1,32 @@
+<?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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Reference the two static libraries -->
+ <string name="AppFooBar">@string/FooBar</string>
+ <string name="AppFoo">@string/Foo</string>
+
+ <string name="hooha"><font bgcolor="#ffffff">Hey guys!</font> <xliff:g>My</xliff:g> name is <b>Adam</b>. How <b><i>are</i></b> you?</string>
+ <public name="hooha" type="string" id="0x7f020001"/>
+ <string name="wow">@android:string/ok</string>
+ <public name="layout_width" type="attr" />
+ <attr name="layout_width" format="boolean" />
+ <attr name="flags">
+ <flag name="complex" value="1" />
+ <flag name="pub" value="2" />
+ <flag name="weak" value="4" />
+ </attr>
+</resources>
diff --git a/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java b/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java
new file mode 100644
index 0000000..472b35a
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java
@@ -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.
+ */
+package com.android.aapt.app.one;
+
+public class AppOne {
+ // IDs from StaticLibOne
+ public static int FooId = com.android.aapt.staticlib.one.R.string.Foo;
+ public static int LayoutId = com.android.aapt.staticlib.one.R.layout.layout;
+
+ // IDs from StaticLibTwo
+ public static int FooBarId = com.android.aapt.staticlib.two.R.string.FooBar;
+}
+
diff --git a/tools/aapt2/integration-tests/StaticLibOne/Android.mk b/tools/aapt2/integration-tests/StaticLibOne/Android.mk
new file mode 100644
index 0000000..d59dc60
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibOne/Android.mk
@@ -0,0 +1,25 @@
+#
+# 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_USE_AAPT2 := true
+LOCAL_MODULE := AaptTestStaticLibOne
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml
index b541d13..705047e7 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,4 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<manifest package="com.android.aapt.staticlib.one" />
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml
index b541d13..683c91c 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,5 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:text="@string/Foo" />
diff --git a/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml b/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml
new file mode 100644
index 0000000..d09a485
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibOne/res/values/values.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.
+-->
+
+<resources>
+ <!-- An attribute from StaticLibOne -->
+ <attr name="StaticLibOne_attr" format="string" />
+
+ <string name="Foo">Foo</string>
+ <string name="Foo" product="tablet">Bar</string>
+
+ <declare-styleable name="Widget">
+ <attr name="StaticLibOne_attr" />
+ </declare-styleable>
+</resources>
diff --git a/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java b/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
new file mode 100644
index 0000000..cf48f67
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
@@ -0,0 +1,22 @@
+/*
+ * 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.aapt.staticlib.one;
+
+public class StaticLibOne {
+ // IDs from StaticLibOne
+ public static int FooId = com.android.aapt.staticlib.one.R.string.Foo;
+ public static int LayoutId = com.android.aapt.staticlib.one.R.layout.layout;
+}
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/Android.mk b/tools/aapt2/integration-tests/StaticLibTwo/Android.mk
new file mode 100644
index 0000000..8b6eb41
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTwo/Android.mk
@@ -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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE := AaptTestStaticLibTwo
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLibOne
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml
index b541d13..28f0699 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,4 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<manifest package="com.android.aapt.staticlib.two" />
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/res/drawable/vector.xml b/tools/aapt2/integration-tests/StaticLibTwo/res/drawable/vector.xml
new file mode 100644
index 0000000..dd5979f
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTwo/res/drawable/vector.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="1123"/>
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml
index b541d13..ba98307 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,5 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<View xmlns:custom="http://schemas.android.com/apk/res-auto"
+ custom:StaticLibOne_attr="@string/FooBar" />
diff --git a/packages/DocumentsUI/res/animator/dir_frozen.xml b/tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml
similarity index 64%
copy from packages/DocumentsUI/res/animator/dir_frozen.xml
copy to tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml
index b541d13..97bb2a5 100644
--- a/packages/DocumentsUI/res/animator/dir_frozen.xml
+++ b/tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2013 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.
@@ -13,9 +14,6 @@
limitations under the License.
-->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="0"
- android:propertyName="position"
- android:valueType="floatType"
- android:duration="@android:integer/config_mediumAnimTime" />
+<resources>
+ <string name="FooBar">@string/Foo</string>
+</resources>
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java b/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
new file mode 100644
index 0000000..7110dcd
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
@@ -0,0 +1,25 @@
+/*
+ * 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.aapt.staticlib.two;
+
+public class StaticLibTwo {
+ // IDs from StaticLibOne
+ public static int FooId = com.android.aapt.staticlib.one.R.string.Foo;
+ public static int LayoutId = com.android.aapt.staticlib.one.R.layout.layout;
+
+ // IDs from StaticLibTwo
+ public static int FooBarId = com.android.aapt.staticlib.two.R.string.FooBar;
+}
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index 329dac9..b3e7a02 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -92,9 +92,8 @@
return {};
}
- ZipString suffix(".flat");
void* cookie = nullptr;
- result = StartIteration(collection->mHandle, &cookie, nullptr, &suffix);
+ result = StartIteration(collection->mHandle, &cookie, nullptr, nullptr);
if (result != 0) {
if (outError) *outError = ErrorCodeString(result);
return {};
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 9c25d4e..496e92e 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -38,7 +38,7 @@
mComment << "/**";
}
- mComment << "\n" << " * " << std::move(comment);
+ mComment << "\n * " << std::move(comment);
}
void AnnotationProcessor::appendComment(const StringPiece16& comment) {
@@ -60,6 +60,10 @@
}
}
+void AnnotationProcessor::appendNewLine() {
+ mComment << "\n *";
+}
+
void AnnotationProcessor::writeToStream(std::ostream* out, const StringPiece& prefix) {
if (mHasComments) {
std::string result = mComment.str();
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index e7f2be0..fadf584 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -61,6 +61,8 @@
void appendComment(const StringPiece16& comment);
void appendComment(const StringPiece& comment);
+ void appendNewLine();
+
/**
* Writes the comments and annotations to the stream, with the given prefix before each line.
*/
diff --git a/tools/aapt2/java/ClassDefinitionWriter.h b/tools/aapt2/java/ClassDefinitionWriter.h
index 04e1274..cf92c9a 100644
--- a/tools/aapt2/java/ClassDefinitionWriter.h
+++ b/tools/aapt2/java/ClassDefinitionWriter.h
@@ -65,7 +65,7 @@
<< "String " << name << "=\"" << val << "\";\n";
}
- void addResourceMember(const StringPiece16& name, AnnotationProcessor* processor,
+ void addResourceMember(const StringPiece& name, AnnotationProcessor* processor,
const ResourceId id) {
ensureClassDeclaration();
if (processor) {
@@ -76,7 +76,7 @@
}
template <typename Iterator, typename FieldAccessorFunc>
- void addArrayMember(const StringPiece16& name, AnnotationProcessor* processor,
+ void addArrayMember(const StringPiece& name, AnnotationProcessor* processor,
const Iterator begin, const Iterator end, FieldAccessorFunc f) {
ensureClassDeclaration();
if (processor) {
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 6e340a2..01330dc 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -23,6 +23,7 @@
#include "java/AnnotationProcessor.h"
#include "java/ClassDefinitionWriter.h"
#include "java/JavaClassGenerator.h"
+#include "process/SymbolTable.h"
#include "util/StringPiece.h"
#include <algorithm>
@@ -33,8 +34,9 @@
namespace aapt {
-JavaClassGenerator::JavaClassGenerator(ResourceTable* table, JavaClassGeneratorOptions options) :
- mTable(table), mOptions(options) {
+JavaClassGenerator::JavaClassGenerator(IAaptContext* context, ResourceTable* table,
+ const JavaClassGeneratorOptions& options) :
+ mContext(context), mTable(table), mOptions(options) {
}
static void generateHeader(const StringPiece16& packageNameToGenerate, std::ostream* out) {
@@ -68,16 +70,120 @@
* Java symbols can not contain . or -, but those are valid in a resource name.
* Replace those with '_'.
*/
-static std::u16string transform(const StringPiece16& symbol) {
- std::u16string output = symbol.toString();
- for (char16_t& c : output) {
- if (c == u'.' || c == u'-') {
- c = u'_';
+static std::string transform(const StringPiece16& symbol) {
+ std::string output = util::utf16ToUtf8(symbol);
+ for (char& c : output) {
+ if (c == '.' || c == '-') {
+ c = '_';
}
}
return output;
}
+/**
+ * Transforms an attribute in a styleable to the Java field name:
+ *
+ * <declare-styleable name="Foo">
+ * <attr name="android:bar" />
+ * <attr name="bar" />
+ * </declare-styleable>
+ *
+ * Foo_android_bar
+ * Foo_bar
+ */
+static std::string transformNestedAttr(const ResourceNameRef& attrName,
+ const std::string& styleableClassName,
+ const StringPiece16& packageNameToGenerate) {
+ std::string output = styleableClassName;
+
+ // We may reference IDs from other packages, so prefix the entry name with
+ // the package.
+ if (!attrName.package.empty() && packageNameToGenerate != attrName.package) {
+ output += "_" + transform(attrName.package);
+ }
+ output += "_" + transform(attrName.entry);
+ return output;
+}
+
+static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
+ const uint32_t typeMask = attr->typeMask;
+ if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
+ processor->appendComment(
+ "<p>May be a reference to another resource, in the form\n"
+ "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
+ "attribute in the form\n"
+ "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_STRING) {
+ processor->appendComment(
+ "<p>May be a string value, using '\\\\;' to escape characters such as\n"
+ "'\\\\n' or '\\\\uxxxx' for a unicode character;");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_INTEGER) {
+ processor->appendComment("<p>May be an integer value, such as \"<code>100</code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
+ processor->appendComment(
+ "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
+ "\"<code>false</code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_COLOR) {
+ processor->appendComment(
+ "<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
+ "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
+ "\"<code>#<i>aarrggbb</i></code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_FLOAT) {
+ processor->appendComment(
+ "<p>May be a floating point value, such as \"<code>1.2</code>\".");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
+ processor->appendComment(
+ "<p>May be a dimension value, which is a floating point number appended with a\n"
+ "unit such as \"<code>14.5sp</code>\".\n"
+ "Available units are: px (pixels), dp (density-independent pixels),\n"
+ "sp (scaled pixels based on preferred font size), in (inches), and\n"
+ "mm (millimeters).");
+ }
+
+ if (typeMask & android::ResTable_map::TYPE_FRACTION) {
+ processor->appendComment(
+ "<p>May be a fractional value, which is a floating point number appended with\n"
+ "either % or %p, such as \"<code>14.5%</code>\".\n"
+ "The % suffix always means a percentage of the base size;\n"
+ "the optional %p suffix provides a size relative to some parent container.");
+ }
+
+ if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
+ if (typeMask & android::ResTable_map::TYPE_FLAGS) {
+ processor->appendComment(
+ "<p>Must be one or more (separated by '|') of the following "
+ "constant values.</p>");
+ } else {
+ processor->appendComment("<p>Must be one of the following constant values.</p>");
+ }
+
+ processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
+ for (const Attribute::Symbol& symbol : attr->symbols) {
+ std::stringstream line;
+ line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
+ << "<td>" << std::hex << symbol.value << std::dec << "</td>"
+ << "<td>" << util::trimWhitespace(symbol.symbol.getComment()) << "</td></tr>";
+ processor->appendComment(line.str());
+ }
+ processor->appendComment("</table>");
+ }
+}
+
bool JavaClassGenerator::skipSymbol(SymbolState state) {
switch (mOptions.types) {
case JavaClassGeneratorOptions::SymbolTypes::kAll:
@@ -90,127 +196,159 @@
return true;
}
+struct StyleableAttr {
+ const Reference* attrRef;
+ std::shared_ptr<Attribute> attribute;
+ std::string fieldName;
+};
+
+static bool lessStyleableAttr(const StyleableAttr& lhs, const StyleableAttr& rhs) {
+ const ResourceId lhsId = lhs.attrRef->id ? lhs.attrRef->id.value() : ResourceId(0);
+ const ResourceId rhsId = rhs.attrRef->id ? rhs.attrRef->id.value() : ResourceId(0);
+ if (lhsId < rhsId) {
+ return true;
+ } else if (lhsId > rhsId) {
+ return false;
+ } else {
+ return lhs.attrRef->name.value() < rhs.attrRef->name.value();
+ }
+}
+
void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
AnnotationProcessor* processor,
const StringPiece16& packageNameToGenerate,
const std::u16string& entryName,
const Styleable* styleable) {
+ const std::string className = transform(entryName);
+
// This must be sorted by resource ID.
- std::vector<std::pair<ResourceId, ResourceNameRef>> sortedAttributes;
+ std::vector<StyleableAttr> sortedAttributes;
sortedAttributes.reserve(styleable->entries.size());
for (const auto& attr : styleable->entries) {
// If we are not encoding final attributes, the styleable entry may have no ID
// if we are building a static library.
assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
assert(attr.name && "no name set for Styleable entry");
- sortedAttributes.emplace_back(attr.id ? attr.id.value() : ResourceId(0), attr.name.value());
- }
- std::sort(sortedAttributes.begin(), sortedAttributes.end());
- auto accessorFunc = [](const std::pair<ResourceId, ResourceNameRef>& a) -> ResourceId {
- return a.first;
+ StyleableAttr styleableAttr = {};
+ styleableAttr.attrRef = &attr;
+ styleableAttr.fieldName = transformNestedAttr(attr.name.value(), className,
+ packageNameToGenerate);
+
+ Reference mangledReference;
+ mangledReference.id = attr.id;
+ mangledReference.name = attr.name;
+ if (mangledReference.name.value().package.empty()) {
+ mangledReference.name.value().package = mContext->getCompilationPackage();
+ }
+
+ if (Maybe<ResourceName> mangledName =
+ mContext->getNameMangler()->mangleName(mangledReference.name.value())) {
+ mangledReference.name = mangledName;
+ }
+
+ const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
+ mangledReference);
+ if (symbol) {
+ styleableAttr.attribute = symbol->attribute;
+ }
+ sortedAttributes.push_back(std::move(styleableAttr));
+ }
+ std::sort(sortedAttributes.begin(), sortedAttributes.end(), lessStyleableAttr);
+
+ const size_t attrCount = sortedAttributes.size();
+
+ if (attrCount > 0) {
+ // Build the comment string for the Styleable. It includes details about the
+ // child attributes.
+ std::stringstream styleableComment;
+ if (!styleable->getComment().empty()) {
+ styleableComment << styleable->getComment() << "\n";
+ } else {
+ styleableComment << "Attributes that can be used with a " << className << ".\n";
+ }
+ styleableComment <<
+ "<p>Includes the following attributes:</p>\n"
+ "<table>\n"
+ "<colgroup align=\"left\" />\n"
+ "<colgroup align=\"left\" />\n"
+ "<tr><th>Attribute</th><th>Description</th></tr>\n";
+
+ for (const auto& entry : sortedAttributes) {
+ const ResourceName& attrName = entry.attrRef->name.value();
+ styleableComment << "<tr><td>";
+ styleableComment << "<code>{@link #"
+ << entry.fieldName << " "
+ << (!attrName.package.empty()
+ ? attrName.package : mContext->getCompilationPackage())
+ << ":" << attrName.entry
+ << "}</code>";
+ styleableComment << "</td>";
+
+ styleableComment << "<td>";
+ if (entry.attribute) {
+ styleableComment << entry.attribute->getComment();
+ }
+ styleableComment << "</td></tr>\n";
+ }
+ styleableComment << "</table>\n";
+ for (const auto& entry : sortedAttributes) {
+ styleableComment << "@see #" << entry.fieldName << "\n";
+ }
+ processor->appendComment(styleableComment.str());
+ }
+
+ auto accessorFunc = [](const StyleableAttr& a) -> ResourceId {
+ return a.attrRef->id ? a.attrRef->id.value() : ResourceId(0);
};
// First we emit the array containing the IDs of each attribute.
- outClassDef->addArrayMember(transform(entryName), processor,
+ outClassDef->addArrayMember(className, processor,
sortedAttributes.begin(),
sortedAttributes.end(),
accessorFunc);
// Now we emit the indices into the array.
- size_t attrCount = sortedAttributes.size();
for (size_t i = 0; i < attrCount; i++) {
- std::stringstream name;
- name << transform(entryName);
+ const StyleableAttr& styleableAttr = sortedAttributes[i];
+ const ResourceName& attrName = styleableAttr.attrRef->name.value();
- // We may reference IDs from other packages, so prefix the entry name with
- // the package.
- const ResourceNameRef& itemName = sortedAttributes[i].second;
- if (!itemName.package.empty() && packageNameToGenerate != itemName.package) {
- name << "_" << transform(itemName.package);
+ StringPiece16 packageName = attrName.package;
+ if (packageName.empty()) {
+ packageName = mContext->getCompilationPackage();
}
- name << "_" << transform(itemName.entry);
- outClassDef->addIntMember(name.str(), nullptr, i);
- }
-}
+ AnnotationProcessor attrProcessor;
-static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
- const uint32_t typeMask = attr->typeMask;
- if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
- processor->appendComment(
- "<p>May be a reference to another resource, in the form\n"
- "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
- "attribute in the form\n"
- "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
- }
+ StringPiece16 comment = styleableAttr.attrRef->getComment();
+ if (styleableAttr.attribute && comment.empty()) {
+ comment = styleableAttr.attribute->getComment();
+ }
- if (typeMask & android::ResTable_map::TYPE_STRING) {
- processor->appendComment(
- "<p>May be a string value, using '\\\\;' to escape characters such as\n"
- "'\\\\n' or '\\\\uxxxx' for a unicode character;");
- }
-
- if (typeMask & android::ResTable_map::TYPE_INTEGER) {
- processor->appendComment("<p>May be an integer value, such as \"<code>100</code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
- processor->appendComment(
- "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
- "\"<code>false</code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_COLOR) {
- processor->appendComment(
- "<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
- "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
- "\"<code>#<i>aarrggbb</i></code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_FLOAT) {
- processor->appendComment(
- "<p>May be a floating point value, such as \"<code>1.2</code>\".");
- }
-
- if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
- processor->appendComment(
- "<p>May be a dimension value, which is a floating point number appended with a\n"
- "unit such as \"<code>14.5sp</code>\".\n"
- "Available units are: px (pixels), dp (density-independent pixels),\n"
- "sp (scaled pixels based on preferred font size), in (inches), and\n"
- "mm (millimeters).");
- }
-
- if (typeMask & android::ResTable_map::TYPE_FRACTION) {
- processor->appendComment(
- "<p>May be a fractional value, which is a floating point number appended with\n"
- "either % or %p, such as \"<code>14.5%</code>\".\n"
- "The % suffix always means a percentage of the base size;\n"
- "the optional %p suffix provides a size relative to some parent container.");
- }
-
- if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
- if (typeMask & android::ResTable_map::TYPE_FLAGS) {
- processor->appendComment(
- "<p>Must be one or more (separated by '|') of the following "
- "constant values.</p>");
+ if (!comment.empty()) {
+ attrProcessor.appendComment("<p>\n@attr description");
+ attrProcessor.appendComment(comment);
} else {
- processor->appendComment("<p>Must be one of the following constant values.</p>");
+ std::stringstream defaultComment;
+ defaultComment
+ << "<p>This symbol is the offset where the "
+ << "{@link " << packageName << ".R.attr#" << transform(attrName.entry) << "}\n"
+ << "attribute's value can be found in the "
+ << "{@link #" << className << "} array.";
+ attrProcessor.appendComment(defaultComment.str());
}
- processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<colgroup align=\"left\" />\n"
- "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
- for (const Attribute::Symbol& symbol : attr->symbols) {
- std::stringstream line;
- line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
- << "<td>" << std::hex << symbol.value << std::dec << "</td>"
- << "<td>" << util::trimWhitespace(symbol.symbol.getComment()) << "</td></tr>";
- processor->appendComment(line.str());
+ attrProcessor.appendNewLine();
+
+ if (styleableAttr.attribute) {
+ addAttributeFormatDoc(&attrProcessor, styleableAttr.attribute.get());
+ attrProcessor.appendNewLine();
}
- processor->appendComment("</table>");
+
+ std::stringstream doclavaName;
+ doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
+ attrProcessor.appendComment(doclavaName.str());
+ outClassDef->addIntMember(sortedAttributes[i].fieldName, &attrProcessor, i);
}
}
@@ -223,8 +361,10 @@
continue;
}
- ResourceId id(package->id.value(), type->id.value(), entry->id.value());
- assert(id.isValid());
+ ResourceId id;
+ if (package->id && type->id && entry->id) {
+ id = ResourceId(package->id.value(), type->id.value(), entry->id.value());
+ }
std::u16string unmangledPackage;
std::u16string unmangledName = entry->name;
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 023d6d6..7e46f8c9 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -19,7 +19,7 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
-
+#include "process/IResourceTableConsumer.h"
#include "util/StringPiece.h"
#include <ostream>
@@ -51,7 +51,8 @@
*/
class JavaClassGenerator {
public:
- JavaClassGenerator(ResourceTable* table, JavaClassGeneratorOptions options);
+ JavaClassGenerator(IAaptContext* context, ResourceTable* table,
+ const JavaClassGeneratorOptions& options);
/*
* Writes the R.java file to `out`. Only symbols belonging to `package` are written.
@@ -82,6 +83,7 @@
bool skipSymbol(SymbolState state);
+ IAaptContext* mContext;
ResourceTable* mTable;
JavaClassGeneratorOptions mOptions;
std::string mError;
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index e9e7881..4f041b8 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -15,11 +15,9 @@
*/
#include "java/JavaClassGenerator.h"
+#include "test/Test.h"
#include "util/Util.h"
-#include "test/Builders.h"
-
-#include <gtest/gtest.h>
#include <sstream>
#include <string>
@@ -31,7 +29,11 @@
.addSimple(u"@android:id/class", ResourceId(0x01020000))
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
EXPECT_FALSE(generator.generate(u"android", &out));
@@ -48,7 +50,11 @@
.build())
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
EXPECT_TRUE(generator.generate(u"android", &out));
@@ -72,7 +78,11 @@
.addSimple(u"@android:id/com.foo$two", ResourceId(0x01020001))
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", u"com.android.internal", &out));
@@ -90,7 +100,11 @@
.addSimple(u"@android:^attr-private/one", ResourceId(0x01010000))
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
@@ -110,10 +124,15 @@
.setSymbolState(u"@android:id/two", ResourceId(0x01020001), SymbolState::kPrivate)
.build();
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+
JavaClassGeneratorOptions options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
{
- JavaClassGenerator generator(table.get(), options);
+ JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
std::string output = out.str();
@@ -124,7 +143,7 @@
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
{
- JavaClassGenerator generator(table.get(), options);
+ JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
std::string output = out.str();
@@ -135,7 +154,7 @@
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
{
- JavaClassGenerator generator(table.get(), options);
+ JavaClassGenerator generator(context.get(), table.get(), options);
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
std::string output = out.str();
@@ -189,7 +208,11 @@
.build())
.build();
- JavaClassGenerator generator(table.get(), {});
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
EXPECT_TRUE(generator.generate(u"android", &out));
@@ -207,8 +230,11 @@
test::getValue<Id>(table.get(), u"@android:id/foo")
->setComment(std::u16string(u"This is a comment\n@deprecated"));
- JavaClassGenerator generator(table.get(), {});
-
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
std::stringstream out;
ASSERT_TRUE(generator.generate(u"android", &out));
std::string actual = out.str();
@@ -227,7 +253,35 @@
}
TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
+ Attribute attr(false);
+ attr.setComment(StringPiece16(u"This is an attribute"));
+ Styleable styleable;
+ styleable.entries.push_back(Reference(test::parseNameOrDie(u"@android:attr/one")));
+ styleable.setComment(StringPiece16(u"This is a styleable"));
+
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .setPackageId(u"android", 0x01)
+ .addValue(u"@android:attr/one", util::make_unique<Attribute>(attr))
+ .addValue(u"@android:styleable/Container",
+ std::unique_ptr<Styleable>(styleable.clone(nullptr)))
+ .build();
+
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .setNameManglerPolicy(NameManglerPolicy{ u"android" })
+ .build();
+ JavaClassGeneratorOptions options;
+ options.useFinal = false;
+ JavaClassGenerator generator(context.get(), table.get(), options);
+ std::stringstream out;
+ ASSERT_TRUE(generator.generate(u"android", &out));
+ std::string actual = out.str();
+
+ EXPECT_NE(std::string::npos, actual.find("@attr name android:one"));
+ EXPECT_NE(std::string::npos, actual.find("@attr description"));
+ EXPECT_NE(std::string::npos, actual.find(util::utf16ToUtf8(attr.getComment())));
+ EXPECT_NE(std::string::npos, actual.find(util::utf16ToUtf8(styleable.getComment())));
}
} // namespace aapt
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index d83f6def..b84074d 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -62,7 +62,9 @@
std::set<std::u16string> extraJavaPackages;
Maybe<std::string> generateProguardRulesPath;
bool noAutoVersion = false;
+ bool noVersionVectors = false;
bool staticLib = false;
+ bool noStaticLibPackages = false;
bool generateNonFinalIds = false;
bool outputToDirectory = false;
bool autoAddOverlay = false;
@@ -74,37 +76,58 @@
TableSplitterOptions tableSplitterOptions;
};
-struct LinkContext : public IAaptContext {
- StdErrDiagnostics mDiagnostics;
- std::unique_ptr<NameMangler> mNameMangler;
- std::u16string mCompilationPackage;
- uint8_t mPackageId;
- std::unique_ptr<ISymbolTable> mSymbols;
- bool mVerbose = false;
+class LinkContext : public IAaptContext {
+public:
+ LinkContext() : mNameMangler({}) {
+ }
IDiagnostics* getDiagnostics() override {
return &mDiagnostics;
}
NameMangler* getNameMangler() override {
- return mNameMangler.get();
+ return &mNameMangler;
}
- StringPiece16 getCompilationPackage() override {
+ void setNameManglerPolicy(const NameManglerPolicy& policy) {
+ mNameMangler = NameMangler(policy);
+ }
+
+ const std::u16string& getCompilationPackage() override {
return mCompilationPackage;
}
+ void setCompilationPackage(const StringPiece16& packageName) {
+ mCompilationPackage = packageName.toString();
+ }
+
uint8_t getPackageId() override {
return mPackageId;
}
- ISymbolTable* getExternalSymbols() override {
- return mSymbols.get();
+ void setPackageId(uint8_t id) {
+ mPackageId = id;
+ }
+
+ SymbolTable* getExternalSymbols() override {
+ return &mSymbols;
}
bool verbose() override {
return mVerbose;
}
+
+ void setVerbose(bool val) {
+ mVerbose = val;
+ }
+
+private:
+ StdErrDiagnostics mDiagnostics;
+ NameMangler mNameMangler;
+ std::u16string mCompilationPackage;
+ uint8_t mPackageId = 0x0;
+ SymbolTable mSymbols;
+ bool mVerbose = false;
};
static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
@@ -117,11 +140,19 @@
return false;
}
- CompiledFileInputStream inputStream(data->data(), data->size());
- if (!inputStream.CompiledFile()) {
- context->getDiagnostics()->error(DiagMessage(file->getSource())
- << "invalid compiled file header");
- return false;
+ const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data());
+ size_t bufferSize = data->size();
+
+ // If the file ends with .flat, we must strip off the CompiledFileHeader from it.
+ if (util::stringEndsWith<char>(file->getSource().path, ".flat")) {
+ CompiledFileInputStream inputStream(data->data(), data->size());
+ if (!inputStream.CompiledFile()) {
+ context->getDiagnostics()->error(DiagMessage(file->getSource())
+ << "invalid compiled file header");
+ return false;
+ }
+ buffer = reinterpret_cast<const uint8_t*>(inputStream.data());
+ bufferSize = inputStream.size();
}
if (context->verbose()) {
@@ -129,8 +160,7 @@
}
if (writer->startEntry(outPath, compressionFlags)) {
- if (writer->writeEntry(reinterpret_cast<const uint8_t*>(inputStream.data()),
- inputStream.size())) {
+ if (writer->writeEntry(buffer, bufferSize)) {
if (writer->finishEntry()) {
return true;
}
@@ -156,7 +186,7 @@
DiagMessage msg;
msg << "writing " << path << " to archive";
if (maxSdkLevel) {
- msg << " maxSdkLevel=" << maxSdkLevel.value();
+ msg << " maxSdkLevel=" << maxSdkLevel.value() << " keepRawValues=" << keepRawValues;
}
context->getDiagnostics()->note(msg);
}
@@ -248,6 +278,7 @@
struct ResourceFileFlattenerOptions {
bool noAutoVersion = false;
+ bool noVersionVectors = false;
bool keepRawValues = false;
bool doNotCompressAnything = false;
std::vector<std::string> extensionsToNotCompress;
@@ -267,14 +298,13 @@
io::IFile* fileToCopy;
std::unique_ptr<xml::XmlResource> xmlToFlatten;
std::string dstPath;
+ bool skipVersion = false;
};
uint32_t getCompressionFlags(const StringPiece& str);
- std::unique_ptr<xml::XmlResource> linkAndVersionXmlFile(const ResourceEntry* entry,
- const ResourceFile& fileDesc,
- io::IFile* file,
- ResourceTable* table);
+ bool linkAndVersionXmlFile(const ResourceEntry* entry, const ResourceFile& fileDesc,
+ io::IFile* file, ResourceTable* table, FileOperation* outFileOp);
ResourceFileFlattenerOptions mOptions;
IAaptContext* mContext;
@@ -294,11 +324,11 @@
return ArchiveEntry::kCompress;
}
-std::unique_ptr<xml::XmlResource> ResourceFileFlattener::linkAndVersionXmlFile(
- const ResourceEntry* entry,
- const ResourceFile& fileDesc,
- io::IFile* file,
- ResourceTable* table) {
+bool ResourceFileFlattener::linkAndVersionXmlFile(const ResourceEntry* entry,
+ const ResourceFile& fileDesc,
+ io::IFile* file,
+ ResourceTable* table,
+ FileOperation* outFileOp) {
const StringPiece srcPath = file->getSource().path;
if (mContext->verbose()) {
mContext->getDiagnostics()->note(DiagMessage() << "linking " << srcPath);
@@ -307,51 +337,67 @@
std::unique_ptr<io::IData> data = file->openAsData();
if (!data) {
mContext->getDiagnostics()->error(DiagMessage(file->getSource()) << "failed to open file");
- return {};
+ return false;
}
- std::unique_ptr<xml::XmlResource> xmlRes;
if (util::stringEndsWith<char>(srcPath, ".flat")) {
- xmlRes = loadBinaryXmlSkipFileExport(file->getSource(), data->data(), data->size(),
- mContext->getDiagnostics());
+ outFileOp->xmlToFlatten = loadBinaryXmlSkipFileExport(file->getSource(),
+ data->data(), data->size(),
+ mContext->getDiagnostics());
} else {
- xmlRes = xml::inflate(data->data(), data->size(), mContext->getDiagnostics(),
- file->getSource());
+ outFileOp->xmlToFlatten = xml::inflate(data->data(), data->size(),
+ mContext->getDiagnostics(),
+ file->getSource());
}
- if (!xmlRes) {
- return {};
+ if (!outFileOp->xmlToFlatten) {
+ return false;
}
// Copy the the file description header.
- xmlRes->file = fileDesc;
+ outFileOp->xmlToFlatten->file = fileDesc;
XmlReferenceLinker xmlLinker;
- if (!xmlLinker.consume(mContext, xmlRes.get())) {
- return {};
+ if (!xmlLinker.consume(mContext, outFileOp->xmlToFlatten.get())) {
+ return false;
}
- if (!proguard::collectProguardRules(xmlRes->file.source, xmlRes.get(), mKeepSet)) {
- return {};
+ if (!proguard::collectProguardRules(outFileOp->xmlToFlatten->file.source,
+ outFileOp->xmlToFlatten.get(), mKeepSet)) {
+ return false;
}
if (!mOptions.noAutoVersion) {
+ if (mOptions.noVersionVectors) {
+ // Skip this if it is a vector or animated-vector.
+ xml::Element* el = xml::findRootElement(outFileOp->xmlToFlatten.get());
+ if (el && el->namespaceUri.empty()) {
+ if (el->name == u"vector" || el->name == u"animated-vector") {
+ // We are NOT going to version this file.
+ outFileOp->skipVersion = true;
+ return true;
+ }
+ }
+ }
+
// Find the first SDK level used that is higher than this defined config and
// not superseded by a lower or equal SDK level resource.
for (int sdkLevel : xmlLinker.getSdkLevels()) {
- if (sdkLevel > xmlRes->file.config.sdkVersion) {
- if (!shouldGenerateVersionedResource(entry, xmlRes->file.config, sdkLevel)) {
+ if (sdkLevel > outFileOp->xmlToFlatten->file.config.sdkVersion) {
+ if (!shouldGenerateVersionedResource(entry, outFileOp->xmlToFlatten->file.config,
+ sdkLevel)) {
// If we shouldn't generate a versioned resource, stop checking.
break;
}
- ResourceFile versionedFileDesc = xmlRes->file;
- versionedFileDesc.config.sdkVersion = sdkLevel;
+ ResourceFile versionedFileDesc = outFileOp->xmlToFlatten->file;
+ versionedFileDesc.config.sdkVersion = (uint16_t) sdkLevel;
if (mContext->verbose()) {
mContext->getDiagnostics()->note(DiagMessage(versionedFileDesc.source)
<< "auto-versioning resource from config '"
- << xmlRes->file.config << "' -> '"
+ << outFileOp->xmlToFlatten->file.config
+ << "' -> '"
<< versionedFileDesc.config << "'");
}
@@ -365,13 +411,13 @@
file,
mContext->getDiagnostics());
if (!added) {
- return {};
+ return false;
}
break;
}
}
}
- return xmlRes;
+ return true;
}
/**
@@ -415,9 +461,7 @@
fileDesc.config = configValue->config;
fileDesc.name = ResourceName(pkg->name, type->type, entry->name);
fileDesc.source = fileRef->getSource();
- fileOp.xmlToFlatten = linkAndVersionXmlFile(entry.get(), fileDesc,
- file, table);
- if (!fileOp.xmlToFlatten) {
+ if (!linkAndVersionXmlFile(entry.get(), fileDesc, file, table, &fileOp)) {
error = true;
continue;
}
@@ -447,7 +491,7 @@
if (fileOp.xmlToFlatten) {
Maybe<size_t> maxSdkLevel;
- if (!mOptions.noAutoVersion) {
+ if (!mOptions.noAutoVersion && !fileOp.skipVersion) {
maxSdkLevel = std::max<size_t>(config.sdkVersion, 1u);
}
@@ -474,39 +518,61 @@
class LinkCommand {
public:
LinkCommand(LinkContext* context, const LinkOptions& options) :
- mOptions(options), mContext(context), mFinalTable(), mFileCollection(nullptr) {
- std::unique_ptr<io::FileCollection> fileCollection =
- util::make_unique<io::FileCollection>();
-
- // Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
- mFileCollection = fileCollection.get();
-
- // Move it to the collection.
- mCollections.push_back(std::move(fileCollection));
+ mOptions(options), mContext(context), mFinalTable(),
+ mFileCollection(util::make_unique<io::FileCollection>()) {
}
/**
* Creates a SymbolTable that loads symbols from the various APKs and caches the
* results for faster lookup.
*/
- std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
- AssetManagerSymbolTableBuilder builder;
+ bool loadSymbolsFromIncludePaths() {
+ std::unique_ptr<AssetManagerSymbolSource> assetSource =
+ util::make_unique<AssetManagerSymbolSource>();
for (const std::string& path : mOptions.includePaths) {
if (mContext->verbose()) {
mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
}
- std::unique_ptr<android::AssetManager> assetManager =
- util::make_unique<android::AssetManager>();
- int32_t cookie = 0;
- if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
+ // First try to load the file as a static lib.
+ std::string errorStr;
+ std::unique_ptr<ResourceTable> staticInclude = loadStaticLibrary(path, &errorStr);
+ if (staticInclude) {
+ if (!mOptions.staticLib) {
+ // Can't include static libraries when not building a static library.
+ mContext->getDiagnostics()->error(
+ DiagMessage(path) << "can't include static library when building app");
+ return false;
+ }
+
+ // If we are using --no-static-lib-packages, we need to rename the package of this
+ // table to our compilation package.
+ if (mOptions.noStaticLibPackages) {
+ if (ResourceTablePackage* pkg = staticInclude->findPackageById(0x7f)) {
+ pkg->name = mContext->getCompilationPackage();
+ }
+ }
+
+ mContext->getExternalSymbols()->appendSource(
+ util::make_unique<ResourceTableSymbolSource>(staticInclude.get()));
+
+ mStaticTableIncludes.push_back(std::move(staticInclude));
+
+ } else if (!errorStr.empty()) {
+ // We had an error with reading, so fail.
+ mContext->getDiagnostics()->error(DiagMessage(path) << errorStr);
+ return false;
+ }
+
+ if (!assetSource->addAssetPath(path)) {
mContext->getDiagnostics()->error(
DiagMessage(path) << "failed to load include path");
- return {};
+ return false;
}
- builder.add(std::move(assetManager));
}
- return builder.build();
+
+ mContext->getExternalSymbols()->appendSource(std::move(assetSource));
+ return true;
}
Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
@@ -571,6 +637,35 @@
return !error;
}
+ /**
+ * Returns true if no IDs have been set, false otherwise.
+ */
+ bool verifyNoIdsSet() {
+ for (const auto& package : mFinalTable.packages) {
+ for (const auto& type : package->types) {
+ if (type->id) {
+ mContext->getDiagnostics()->error(DiagMessage() << "type " << type->type
+ << " has ID " << std::hex
+ << (int) type->id.value()
+ << std::dec << " assigned");
+ return false;
+ }
+
+ for (const auto& entry : type->entries) {
+ if (entry->id) {
+ ResourceNameRef resName(package->name, type->type, entry->name);
+ mContext->getDiagnostics()->error(DiagMessage() << "entry " << resName
+ << " has ID " << std::hex
+ << (int) entry->id.value()
+ << std::dec << " assigned");
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
if (mOptions.outputToDirectory) {
return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
@@ -599,6 +694,32 @@
return false;
}
+ bool flattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
+ // Create the file/zip entry.
+ if (!writer->startEntry("resources.arsc.flat", 0)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed to open");
+ return false;
+ }
+
+ std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
+
+ // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
+ // interface.
+ {
+ google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
+
+ if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
+ return false;
+ }
+ }
+
+ if (!writer->finishEntry()) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed to finish entry");
+ return false;
+ }
+ return true;
+ }
bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
@@ -608,20 +729,31 @@
std::string outPath = mOptions.generateJavaClassPath.value();
file::appendPath(&outPath, file::packageToPath(util::utf16ToUtf8(outPackage)));
- file::mkdirs(outPath);
+ if (!file::mkdirs(outPath)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to create directory '" << outPath << "'");
+ return false;
+ }
+
file::appendPath(&outPath, "R.java");
std::ofstream fout(outPath, std::ofstream::binary);
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;
}
- JavaClassGenerator generator(table, javaOptions);
+ JavaClassGenerator generator(mContext, table, javaOptions);
if (!generator.generate(packageNameToGenerate, outPackage, &fout)) {
mContext->getDiagnostics()->error(DiagMessage(outPath) << generator.getError());
return false;
}
+
+ if (!fout) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
+ }
return true;
}
@@ -633,12 +765,18 @@
std::string outPath = mOptions.generateJavaClassPath.value();
file::appendPath(&outPath,
file::packageToPath(util::utf16ToUtf8(mContext->getCompilationPackage())));
- file::mkdirs(outPath);
+ if (!file::mkdirs(outPath)) {
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to create directory '" << outPath << "'");
+ return false;
+ }
+
file::appendPath(&outPath, "Manifest.java");
std::ofstream fout(outPath, std::ofstream::binary);
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;
}
@@ -649,7 +787,8 @@
}
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;
}
return true;
@@ -660,32 +799,108 @@
return true;
}
- std::ofstream fout(mOptions.generateProguardRulesPath.value(), std::ofstream::binary);
+ const std::string& outPath = mOptions.generateProguardRulesPath.value();
+ std::ofstream fout(outPath, std::ofstream::binary);
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed to open '" << outPath << "': " << strerror(errno));
return false;
}
proguard::writeKeepSet(&fout, keepSet);
if (!fout) {
- mContext->getDiagnostics()->error(DiagMessage() << strerror(errno));
+ mContext->getDiagnostics()->error(
+ DiagMessage() << "failed writing to '" << outPath << "': " << strerror(errno));
return false;
}
return true;
}
- bool mergeStaticLibrary(const std::string& input) {
- // TODO(adamlesinski): Load resources from a static library APK and merge the table into
- // TableMerger.
- mContext->getDiagnostics()->warn(DiagMessage()
- << "linking static libraries not supported yet: "
- << input);
+ std::unique_ptr<ResourceTable> loadStaticLibrary(const std::string& input,
+ std::string* outError) {
+ std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
+ input, outError);
+ if (!collection) {
+ return {};
+ }
+ return loadTablePbFromCollection(collection.get());
+ }
+
+ std::unique_ptr<ResourceTable> loadTablePbFromCollection(io::IFileCollection* collection) {
+ io::IFile* file = collection->findFile("resources.arsc.flat");
+ if (!file) {
+ return {};
+ }
+
+ std::unique_ptr<io::IData> data = file->openAsData();
+ return loadTableFromPb(file->getSource(), data->data(), data->size(),
+ mContext->getDiagnostics());
+ }
+
+ bool mergeStaticLibrary(const std::string& input, bool override) {
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(DiagMessage() << "merging static library " << input);
+ }
+
+ std::string errorStr;
+ std::unique_ptr<io::ZipFileCollection> collection =
+ io::ZipFileCollection::create(input, &errorStr);
+ if (!collection) {
+ mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
+ return false;
+ }
+
+ std::unique_ptr<ResourceTable> table = loadTablePbFromCollection(collection.get());
+ if (!table) {
+ mContext->getDiagnostics()->error(DiagMessage(input) << "invalid static library");
+ return false;
+ }
+
+ ResourceTablePackage* pkg = table->findPackageById(0x7f);
+ if (!pkg) {
+ mContext->getDiagnostics()->error(DiagMessage(input)
+ << "static library has no package");
+ return false;
+ }
+
+ bool result;
+ if (mOptions.noStaticLibPackages) {
+ // Merge all resources as if they were in the compilation package. This is the old
+ // behaviour of aapt.
+
+ // Add the package to the set of --extra-packages so we emit an R.java for each
+ // library package.
+ if (!pkg->name.empty()) {
+ mOptions.extraJavaPackages.insert(pkg->name);
+ }
+
+ pkg->name = u"";
+ if (override) {
+ result = mTableMerger->mergeOverlay(Source(input), table.get(), collection.get());
+ } else {
+ result = mTableMerger->merge(Source(input), table.get(), collection.get());
+ }
+
+ } else {
+ // This is the proper way to merge libraries, where the package name is preserved
+ // and resource names are mangled.
+ result = mTableMerger->mergeAndMangle(Source(input), pkg->name, table.get(),
+ collection.get());
+ }
+
+ if (!result) {
+ return false;
+ }
+
+ // Make sure to move the collection into the set of IFileCollections.
+ mCollections.push_back(std::move(collection));
return true;
}
bool mergeResourceTable(io::IFile* file, bool override) {
if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
+ mContext->getDiagnostics()->note(DiagMessage() << "merging resource table "
+ << file->getSource());
}
std::unique_ptr<io::IData> data = file->openAsData();
@@ -711,13 +926,14 @@
return result;
}
- bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
+ bool mergeCompiledFile(io::IFile* file, ResourceFile* fileDesc, bool override) {
if (mContext->verbose()) {
- mContext->getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
+ mContext->getDiagnostics()->note(DiagMessage() << "merging compiled file "
+ << file->getSource());
}
bool result = false;
- if (overlay) {
+ if (override) {
result = mTableMerger->mergeFileOverlay(*fileDesc, file);
} else {
result = mTableMerger->mergeFile(*fileDesc, file);
@@ -730,7 +946,7 @@
// Add the exports of this file to the table.
for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
if (exportedSymbol.name.package.empty()) {
- exportedSymbol.name.package = mContext->getCompilationPackage().toString();
+ exportedSymbol.name.package = mContext->getCompilationPackage();
}
ResourceNameRef resName = exportedSymbol.name;
@@ -743,11 +959,9 @@
std::unique_ptr<Id> id = util::make_unique<Id>();
id->setSource(fileDesc->source.withLine(exportedSymbol.line));
- bool result = mFinalTable.addResourceAllowMangled(resName,
- ConfigDescription::defaultConfig(),
- std::string(),
- std::move(id),
- mContext->getDiagnostics());
+ bool result = mFinalTable.addResourceAllowMangled(
+ resName, ConfigDescription::defaultConfig(), std::string(), std::move(id),
+ mContext->getDiagnostics());
if (!result) {
return false;
}
@@ -756,12 +970,21 @@
}
/**
- * Creates an io::IFileCollection from the ZIP archive and processes the files within.
+ * Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
+ * If override is true, conflicting resources are allowed to override each other, in order of
+ * last seen.
+ *
+ * An io::IFileCollection is created from the ZIP file and added to the set of
+ * io::IFileCollections that are open.
*/
bool mergeArchive(const std::string& input, bool override) {
+ if (mContext->verbose()) {
+ mContext->getDiagnostics()->note(DiagMessage() << "merging archive " << input);
+ }
+
std::string errorStr;
- std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
- input, &errorStr);
+ std::unique_ptr<io::ZipFileCollection> collection =
+ io::ZipFileCollection::create(input, &errorStr);
if (!collection) {
mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
return false;
@@ -769,7 +992,7 @@
bool error = false;
for (auto iter = collection->iterator(); iter->hasNext(); ) {
- if (!processFile(iter->next(), override)) {
+ if (!mergeFile(iter->next(), override)) {
error = true;
}
}
@@ -779,22 +1002,45 @@
return !error;
}
- bool processFile(const std::string& path, bool override) {
+ /**
+ * Takes a path to load and merge into the master ResourceTable. If override is true,
+ * conflicting resources are allowed to override each other, in order of last seen.
+ *
+ * If the file path ends with .flata, .jar, .jack, or .zip the file is treated as ZIP archive
+ * and the files within are merged individually.
+ *
+ * Otherwise the files is processed on its own.
+ */
+ bool mergePath(const std::string& path, bool override) {
if (util::stringEndsWith<char>(path, ".flata") ||
util::stringEndsWith<char>(path, ".jar") ||
util::stringEndsWith<char>(path, ".jack") ||
util::stringEndsWith<char>(path, ".zip")) {
return mergeArchive(path, override);
+ } else if (util::stringEndsWith<char>(path, ".apk")) {
+ return mergeStaticLibrary(path, override);
}
io::IFile* file = mFileCollection->insertFile(path);
- return processFile(file, override);
+ return mergeFile(file, override);
}
- bool processFile(io::IFile* file, bool override) {
+ /**
+ * Takes a file to load and merge into the master ResourceTable. If override is true,
+ * conflicting resources are allowed to override each other, in order of last seen.
+ *
+ * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and merged into the
+ * master ResourceTable. If the file ends with .flat, then it is treated like a compiled file
+ * and the header data is read and merged into the final ResourceTable.
+ *
+ * All other file types are ignored. This is because these files could be coming from a zip,
+ * where we could have other files like classes.dex.
+ */
+ bool mergeFile(io::IFile* file, bool override) {
const Source& src = file->getSource();
if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
return mergeResourceTable(file, override);
+
} else if (util::stringEndsWith<char>(src.path, ".flat")){
// Try opening the file and looking for an Export header.
std::unique_ptr<io::IData> data = file->openAsData();
@@ -806,9 +1052,8 @@
std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
src, data->data(), data->size(), mContext->getDiagnostics());
if (resourceFile) {
- return mergeCompiledFile(file, std::move(resourceFile), override);
+ return mergeCompiledFile(file, resourceFile.get(), override);
}
-
return false;
}
@@ -826,32 +1071,30 @@
}
if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
- mContext->mCompilationPackage = maybeAppInfo.value().package;
+ mContext->setCompilationPackage(maybeAppInfo.value().package);
} else {
mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
<< "no package specified in <manifest> tag");
return 1;
}
- if (!util::isJavaPackageName(mContext->mCompilationPackage)) {
+ if (!util::isJavaPackageName(mContext->getCompilationPackage())) {
mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
<< "invalid package name '"
- << mContext->mCompilationPackage
+ << mContext->getCompilationPackage()
<< "'");
return 1;
}
- mContext->mNameMangler = util::make_unique<NameMangler>(
- NameManglerPolicy{ mContext->mCompilationPackage });
+ mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() });
- if (mContext->mCompilationPackage == u"android") {
- mContext->mPackageId = 0x01;
+ if (mContext->getCompilationPackage() == u"android") {
+ mContext->setPackageId(0x01);
} else {
- mContext->mPackageId = 0x7f;
+ mContext->setPackageId(0x7f);
}
- mContext->mSymbols = createSymbolTableFromIncludePaths();
- if (!mContext->mSymbols) {
+ if (!loadSymbolsFromIncludePaths()) {
return 1;
}
@@ -861,20 +1104,21 @@
if (mContext->verbose()) {
mContext->getDiagnostics()->note(
- DiagMessage() << "linking package '" << mContext->mCompilationPackage << "' "
- << "with package ID " << std::hex << (int) mContext->mPackageId);
+ DiagMessage() << "linking package '" << mContext->getCompilationPackage()
+ << "' with package ID " << std::hex
+ << (int) mContext->getPackageId());
}
for (const std::string& input : inputFiles) {
- if (!processFile(input, false)) {
+ if (!mergePath(input, false)) {
mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
return 1;
}
}
for (const std::string& input : mOptions.overlayFiles) {
- if (!processFile(input, true)) {
+ if (!mergePath(input, true)) {
mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
return 1;
}
@@ -893,20 +1137,28 @@
}
}
- {
+ if (!mOptions.staticLib) {
+ // Assign IDs if we are building a regular app.
IdAssigner idAssigner;
if (!idAssigner.consume(mContext, &mFinalTable)) {
mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
return 1;
}
+ } else {
+ // Static libs are merged with other apps, and ID collisions are bad, so verify that
+ // no IDs have been set.
+ if (!verifyNoIdsSet()) {
+ return 1;
+ }
}
- mContext->mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
- mContext->mCompilationPackage, mTableMerger->getMergedPackages() });
- mContext->mSymbols = JoinedSymbolTableBuilder()
- .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
- .addSymbolTable(std::move(mContext->mSymbols))
- .build();
+ // Add the names to mangle based on our source merge earlier.
+ mContext->setNameManglerPolicy(NameManglerPolicy{
+ mContext->getCompilationPackage(), mTableMerger->getMergedPackages() });
+
+ // Add our table to the symbol table.
+ mContext->getExternalSymbols()->prependSource(
+ util::make_unique<ResourceTableSymbolSource>(&mFinalTable));
{
ReferenceLinker linker;
@@ -915,20 +1167,32 @@
return 1;
}
- ProductFilter productFilter(mOptions.products);
- if (!productFilter.consume(mContext, &mFinalTable)) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
- return 1;
- }
+ if (mOptions.staticLib) {
+ if (!mOptions.products.empty()) {
+ mContext->getDiagnostics()->warn(
+ DiagMessage() << "can't select products when building static library");
+ }
- // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file
- // level.
- TableSplitter tableSplitter({}, mOptions.tableSplitterOptions);
- if (!tableSplitter.verifySplitConstraints(mContext)) {
- return 1;
- }
+ if (mOptions.tableSplitterOptions.configFilter != nullptr ||
+ mOptions.tableSplitterOptions.preferredDensity) {
+ mContext->getDiagnostics()->warn(
+ DiagMessage() << "can't strip resources when building static library");
+ }
+ } else {
+ ProductFilter productFilter(mOptions.products);
+ if (!productFilter.consume(mContext, &mFinalTable)) {
+ mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
+ return 1;
+ }
- tableSplitter.splitTable(&mFinalTable);
+ // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file
+ // level.
+ TableSplitter tableSplitter({}, mOptions.tableSplitterOptions);
+ if (!tableSplitter.verifySplitConstraints(mContext)) {
+ return 1;
+ }
+ tableSplitter.splitTable(&mFinalTable);
+ }
}
proguard::KeepSet proguardKeepSet;
@@ -949,7 +1213,7 @@
// AndroidManifest.xml has no resource name, but the CallSite is built from the name
// (aka, which package the AndroidManifest.xml is coming from).
// So we give it a package name so it can see local resources.
- manifestXml->file.name.package = mContext->getCompilationPackage().toString();
+ manifestXml->file.name.package = mContext->getCompilationPackage();
XmlReferenceLinker manifestLinker;
if (manifestLinker.consume(mContext, manifestXml.get())) {
@@ -986,6 +1250,7 @@
fileFlattenerOptions.doNotCompressAnything = mOptions.doNotCompressAnything;
fileFlattenerOptions.extensionsToNotCompress = mOptions.extensionsToNotCompress;
fileFlattenerOptions.noAutoVersion = mOptions.noAutoVersion;
+ fileFlattenerOptions.noVersionVectors = mOptions.noVersionVectors;
ResourceFileFlattener fileFlattener(fileFlattenerOptions, mContext, &proguardKeepSet);
if (!fileFlattener.flatten(&mFinalTable, archiveWriter.get())) {
@@ -1001,9 +1266,18 @@
}
}
- if (!flattenTable(&mFinalTable, archiveWriter.get())) {
- mContext->getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
- return 1;
+ if (mOptions.staticLib) {
+ if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed to write resources.arsc.flat");
+ return 1;
+ }
+ } else {
+ if (!flattenTable(&mFinalTable, archiveWriter.get())) {
+ mContext->getDiagnostics()->error(DiagMessage()
+ << "failed to write resources.arsc");
+ return 1;
+ }
}
if (mOptions.generateJavaClassPath) {
@@ -1065,14 +1339,17 @@
LinkContext* mContext;
ResourceTable mFinalTable;
- ResourceTable mLocalFileTable;
std::unique_ptr<TableMerger> mTableMerger;
// A pointer to the FileCollection representing the filesystem (not archives).
- io::FileCollection* mFileCollection;
+ std::unique_ptr<io::FileCollection> mFileCollection;
// A vector of IFileCollections. This is mainly here to keep ownership of the collections.
std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
+
+ // A vector of ResourceTables. This is here to retain ownership, so that the SymbolTable
+ // can use these.
+ std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes;
};
int link(const std::vector<StringPiece>& args) {
@@ -1089,6 +1366,7 @@
Maybe<std::string> productList;
bool legacyXFlag = false;
bool requireLocalization = false;
+ bool verbose = false;
Flags flags = Flags()
.requiredFlag("-o", "Output path", &options.outputPath)
.requiredFlag("--manifest", "Path to the Android manifest to build",
@@ -1104,6 +1382,10 @@
.optionalSwitch("--no-auto-version",
"Disables automatic style and layout SDK versioning",
&options.noAutoVersion)
+ .optionalSwitch("--no-version-vectors",
+ "Disables automatic versioning of vector drawables. Use this only\n"
+ "when building with vector drawable support library",
+ &options.noVersionVectors)
.optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
&legacyXFlag)
.optionalSwitch("-z", "Require localization of strings marked 'suggested'",
@@ -1127,6 +1409,9 @@
.optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
"if none is present", &versionName)
.optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
+ .optionalSwitch("--no-static-lib-packages",
+ "Merge all library resources under the app's package",
+ &options.noStaticLibPackages)
.optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
"This is implied when --static-lib is specified.",
&options.generateNonFinalIds)
@@ -1148,12 +1433,16 @@
&renameInstrumentationTargetPackage)
.optionalFlagList("-0", "File extensions not to compress",
&options.extensionsToNotCompress)
- .optionalSwitch("-v", "Enables verbose logging", &context.mVerbose);
+ .optionalSwitch("-v", "Enables verbose logging", &verbose);
if (!flags.parse("aapt2 link", args, &std::cerr)) {
return 1;
}
+ if (verbose) {
+ context.setVerbose(verbose);
+ }
+
if (privateSymbolsPackage) {
options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
}
@@ -1252,6 +1541,12 @@
options.tableSplitterOptions.preferredDensity = preferredDensityConfig.density;
}
+ // Turn off auto versioning for static-libs.
+ if (options.staticLib) {
+ options.noAutoVersion = true;
+ options.noVersionVectors = true;
+ }
+
LinkCommand cmd(&context, options);
return cmd.run(flags.getArgs());
}
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index f40fbfb..18c47df 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -30,7 +30,7 @@
.setCompilationPackage(u"android")
.setPackageId(0x01)
.setNameManglerPolicy(NameManglerPolicy{ u"android" })
- .setSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbolSource(test::StaticSymbolSourceBuilder()
.addSymbol(u"@android:attr/package", ResourceId(0x01010000),
test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_STRING)
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index ef3fe4f..66eb0df 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-#include "ReferenceLinker.h"
-
#include "Diagnostics.h"
+#include "ReferenceLinker.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
@@ -43,45 +42,10 @@
* NOTE: All of the entries in the ResourceTable must be assigned IDs.
*/
class ReferenceLinkerVisitor : public ValueVisitor {
-private:
- IAaptContext* mContext;
- ISymbolTable* mSymbols;
- xml::IPackageDeclStack* mPackageDecls;
- StringPool* mStringPool;
- CallSite* mCallSite;
- bool mError = false;
-
- /**
- * Transform a RawString value into a more specific, appropriate value, based on the
- * Attribute. If a non RawString value is passed in, this is an identity transform.
- */
- std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
- const Attribute* attr) {
- if (RawString* rawString = valueCast<RawString>(value.get())) {
- std::unique_ptr<Item> transformed =
- ResourceUtils::parseItemForAttribute(*rawString->value, attr);
-
- // If we could not parse as any specific type, try a basic STRING.
- if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
- util::StringBuilder stringBuilder;
- stringBuilder.append(*rawString->value);
- if (stringBuilder) {
- transformed = util::make_unique<String>(
- mStringPool->makeRef(stringBuilder.str()));
- }
- }
-
- if (transformed) {
- return transformed;
- }
- };
- return value;
- }
-
public:
using ValueVisitor::visit;
- ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, StringPool* stringPool,
+ ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols, StringPool* stringPool,
xml::IPackageDeclStack* decl,CallSite* callSite) :
mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
mCallSite(callSite) {
@@ -114,10 +78,11 @@
&transformedReference);
// Find the attribute in the symbol table and check if it is visible from this callsite.
- const ISymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
+ const SymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
if (symbol) {
// Assign our style key the correct ID.
+ // The ID may not exist.
entry.key.id = symbol->id;
// Try to convert the value to a more specific, typed value based on the
@@ -156,6 +121,41 @@
bool hasError() {
return mError;
}
+
+private:
+ IAaptContext* mContext;
+ SymbolTable* mSymbols;
+ xml::IPackageDeclStack* mPackageDecls;
+ StringPool* mStringPool;
+ CallSite* mCallSite;
+ bool mError = false;
+
+ /**
+ * Transform a RawString value into a more specific, appropriate value, based on the
+ * Attribute. If a non RawString value is passed in, this is an identity transform.
+ */
+ std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value,
+ const Attribute* attr) {
+ if (RawString* rawString = valueCast<RawString>(value.get())) {
+ std::unique_ptr<Item> transformed =
+ ResourceUtils::parseItemForAttribute(*rawString->value, attr);
+
+ // If we could not parse as any specific type, try a basic STRING.
+ if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
+ util::StringBuilder stringBuilder;
+ stringBuilder.append(*rawString->value);
+ if (stringBuilder) {
+ transformed = util::make_unique<String>(
+ mStringPool->makeRef(stringBuilder.str()));
+ }
+ }
+
+ if (transformed) {
+ return transformed;
+ }
+ };
+ return value;
+ }
};
} // namespace
@@ -164,13 +164,13 @@
* The symbol is visible if it is public, or if the reference to it is requesting private access
* or if the callsite comes from the same package.
*/
-bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
+bool ReferenceLinker::isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
const CallSite& callSite) {
if (!symbol.isPublic && !ref.privateReference) {
if (ref.name) {
return callSite.resource.package == ref.name.value().package;
- } else if (ref.id) {
- return ref.id.value().packageId() == symbol.id.packageId();
+ } else if (ref.id && symbol.id) {
+ return ref.id.value().packageId() == symbol.id.value().packageId();
} else {
return false;
}
@@ -178,9 +178,9 @@
return true;
}
-const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
- NameMangler* mangler,
- ISymbolTable* symbols) {
+const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
+ NameMangler* mangler,
+ SymbolTable* symbols) {
if (reference.name) {
Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
return symbols->findByName(mangled ? mangled.value() : reference.name.value());
@@ -191,10 +191,10 @@
}
}
-const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
- const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
+const SymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
+ const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
CallSite* callSite, std::string* outError) {
- const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
+ const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
if (!symbol) {
if (outError) *outError = "not found";
return nullptr;
@@ -207,12 +207,12 @@
return symbol;
}
-const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
- const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
+const SymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
+ const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
CallSite* callSite, std::string* outError) {
- const ISymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
- symbols, callSite,
- outError);
+ const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
+ symbols, callSite,
+ outError);
if (!symbol) {
return nullptr;
}
@@ -226,10 +226,10 @@
Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
NameMangler* nameMangler,
- ISymbolTable* symbols,
+ SymbolTable* symbols,
CallSite* callSite,
std::string* outError) {
- const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
+ const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
if (!symbol) {
return {};
}
@@ -256,7 +256,7 @@
}
bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
- ISymbolTable* symbols, xml::IPackageDeclStack* decls,
+ SymbolTable* symbols, xml::IPackageDeclStack* decls,
CallSite* callSite) {
assert(reference);
assert(reference->name || reference->id);
@@ -266,9 +266,12 @@
&transformedReference);
std::string errStr;
- const ISymbolTable::Symbol* s = resolveSymbolCheckVisibility(
+ const SymbolTable::Symbol* s = resolveSymbolCheckVisibility(
transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
if (s) {
+ // The ID may not exist. This is fine because of the possibility of building against
+ // libraries without assigned IDs.
+ // Ex: Linking against own resources when building a static library.
reference->id = s->id;
return true;
}
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index a0eb00c..7993aaf 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -38,36 +38,36 @@
/**
* Returns true if the symbol is visible by the reference and from the callsite.
*/
- static bool isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
+ static bool isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
const CallSite& callSite);
/**
* Performs name mangling and looks up the resource in the symbol table. Returns nullptr
* if the symbol was not found.
*/
- static const ISymbolTable::Symbol* resolveSymbol(const Reference& reference,
- NameMangler* mangler, ISymbolTable* symbols);
+ static const SymbolTable::Symbol* resolveSymbol(const Reference& reference,
+ NameMangler* mangler, SymbolTable* symbols);
/**
* Performs name mangling and looks up the resource in the symbol table. If the symbol is
* not visible by the reference at the callsite, nullptr is returned. outError holds
* the error message.
*/
- static const ISymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference,
- NameMangler* nameMangler,
- ISymbolTable* symbols,
- CallSite* callSite,
- std::string* outError);
+ static const SymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference,
+ NameMangler* nameMangler,
+ SymbolTable* symbols,
+ CallSite* callSite,
+ std::string* outError);
/**
* Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
* That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
*/
- static const ISymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference,
- NameMangler* nameMangler,
- ISymbolTable* symbols,
- CallSite* callSite,
- std::string* outError);
+ static const SymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference,
+ NameMangler* nameMangler,
+ SymbolTable* symbols,
+ CallSite* callSite,
+ std::string* outError);
/**
* Resolves the attribute reference and returns an xml::AaptAttribute if successful.
@@ -75,7 +75,7 @@
*/
static Maybe<xml::AaptAttribute> compileXmlAttribute(const Reference& reference,
NameMangler* nameMangler,
- ISymbolTable* symbols,
+ SymbolTable* symbols,
CallSite* callSite,
std::string* outError);
@@ -92,7 +92,7 @@
* to the reference at the callsite, the reference is updated with an ID.
* Returns false on failure, and an error message is logged to the IDiagnostics in the context.
*/
- static bool linkReference(Reference* reference, IAaptContext* context, ISymbolTable* symbols,
+ static bool linkReference(Reference* reference, IAaptContext* context, SymbolTable* symbols,
xml::IPackageDeclStack* decls, CallSite* callSite);
/**
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 8d324fe..76b2309 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -15,12 +15,9 @@
*/
#include "link/ReferenceLinker.h"
-#include "process/SymbolTable.h"
+#include "test/Test.h"
-#include "test/Builders.h"
-#include "test/Context.h"
-
-#include <gtest/gtest.h>
+using android::ResTable_map;
namespace aapt {
@@ -41,12 +38,10 @@
.setCompilationPackage(u"com.app.test")
.setPackageId(0x7f)
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
- .setSymbolTable(JoinedSymbolTableBuilder()
- .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
- .addSymbolTable(test::StaticSymbolTableBuilder()
- .addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034))
- .build())
- .build())
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .addSymbolSource(test::StaticSymbolSourceBuilder()
+ .addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034))
+ .build())
.build();
ReferenceLinker linker;
@@ -91,19 +86,20 @@
.setCompilationPackage(u"com.app.test")
.setPackageId(0x7f)
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
- .setSymbolTable(test::StaticSymbolTableBuilder()
- .addPublicSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000))
- .addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_COLOR)
- .build())
- .addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_FLAGS)
- .addItem(u"one", 0x01)
- .addItem(u"two", 0x02)
- .build())
- .build())
+ .addSymbolSource(test::StaticSymbolSourceBuilder()
+ .addPublicSymbol(u"@android:style/Theme.Material",
+ ResourceId(0x01060000))
+ .addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(ResTable_map::TYPE_COLOR)
+ .build())
+ .addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002),
+ test::AttributeBuilder()
+ .setTypeMask(ResTable_map::TYPE_FLAGS)
+ .addItem(u"one", 0x01)
+ .addItem(u"two", 0x02)
+ .build())
+ .build())
.build();
ReferenceLinker linker;
@@ -131,11 +127,13 @@
.setCompilationPackage(u"com.app.test")
.setPackageId(0x7f)
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
- .setSymbolTable(test::StaticSymbolTableBuilder()
- .addPublicSymbol(u"@com.app.test:attr/com.android.support$foo",
- ResourceId(0x7f010000), test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_COLOR).build())
- .build())
+ .addSymbolSource(test::StaticSymbolSourceBuilder()
+ .addPublicSymbol(u"@com.app.test:attr/com.android.support$foo",
+ ResourceId(0x7f010000),
+ test::AttributeBuilder()
+ .setTypeMask(ResTable_map::TYPE_COLOR)
+ .build())
+ .build())
.build();
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
@@ -167,12 +165,10 @@
.setCompilationPackage(u"com.app.test")
.setPackageId(0x7f)
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
- .setSymbolTable(JoinedSymbolTableBuilder()
- .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
- .addSymbolTable(test::StaticSymbolTableBuilder()
- .addSymbol(u"@android:string/hidden", ResourceId(0x01040034))
- .build())
- .build())
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .addSymbolSource(test::StaticSymbolSourceBuilder()
+ .addSymbol(u"@android:string/hidden", ResourceId(0x01040034))
+ .build())
.build();
ReferenceLinker linker;
@@ -190,13 +186,12 @@
.setCompilationPackage(u"com.app.test")
.setPackageId(0x7f)
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.app.lib" } })
- .setSymbolTable(JoinedSymbolTableBuilder()
- .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
- .addSymbolTable(test::StaticSymbolTableBuilder()
- .addSymbol(u"@com.app.test:string/com.app.lib$hidden",
- ResourceId(0x7f040034))
- .build())
- .build())
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .addSymbolSource(test::StaticSymbolSourceBuilder()
+ .addSymbol(u"@com.app.test:string/com.app.lib$hidden",
+ ResourceId(0x7f040034))
+ .build())
+
.build();
ReferenceLinker linker;
@@ -215,15 +210,14 @@
.setCompilationPackage(u"com.app.test")
.setPackageId(0x7f)
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
- .setSymbolTable(JoinedSymbolTableBuilder()
- .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
- .addSymbolTable(test::StaticSymbolTableBuilder()
- .addSymbol(u"@android:attr/hidden", ResourceId(0x01010001),
- test::AttributeBuilder()
- .setTypeMask(android::ResTable_map::TYPE_COLOR)
- .build())
- .build())
- .build())
+ .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .addSymbolSource(test::StaticSymbolSourceBuilder()
+ .addSymbol(u"@android:attr/hidden", ResourceId(0x01010001),
+ test::AttributeBuilder()
+ .setTypeMask(
+ android::ResTable_map::TYPE_COLOR)
+ .build())
+ .build())
.build();
ReferenceLinker linker;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 5f11745..7471e15 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -34,10 +34,21 @@
assert(mMasterPackage && "package name or ID already taken");
}
+bool TableMerger::merge(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection) {
+ return mergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
+}
+
+bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection) {
+ return mergeImpl(src, table, collection, true /* overlay */, mOptions.autoAddOverlay);
+}
+
/**
* This will merge packages with the same package name (or no package name).
*/
bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection,
bool overlay, bool allowNew) {
const uint8_t desiredPackageId = mContext->getPackageId();
@@ -51,26 +62,36 @@
}
if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
+ FileMergeCallback callback;
+ if (collection) {
+ callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
+ FileReference* newFile, FileReference* oldFile) -> bool {
+ // The old file's path points inside the APK, so we can use it as is.
+ io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path));
+ if (!f) {
+ mContext->getDiagnostics()->error(DiagMessage(src) << "file '"
+ << *oldFile->path
+ << "' not found");
+ return false;
+ }
+
+ newFile->file = f;
+ return true;
+ };
+ }
+
// Merge here. Once the entries are merged and mangled, any references to
// them are still valid. This is because un-mangled references are
// mangled, then looked up at resolution time.
// Also, when linking, we convert references with no package name to use
// the compilation package name.
error |= !doMerge(src, table, package.get(),
- false /* mangle */, overlay, allowNew, {});
+ false /* mangle */, overlay, allowNew, callback);
}
}
return !error;
}
-bool TableMerger::merge(const Source& src, ResourceTable* table) {
- return mergeImpl(src, table, false /* overlay */, true /* allow new */);
-}
-
-bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table) {
- return mergeImpl(src, table, true /* overlay */, mOptions.autoAddOverlay);
-}
-
/**
* This will merge and mangle resources from a static library.
*/
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index b3c22dd..80c2a5e 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -65,13 +65,17 @@
/**
* Merges resources from the same or empty package. This is for local sources.
+ * An io::IFileCollection is optional and used to find the referenced Files and process them.
*/
- bool merge(const Source& src, ResourceTable* table);
+ bool merge(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection = nullptr);
/**
* Merges resources from an overlay ResourceTable.
+ * An io::IFileCollection is optional and used to find the referenced Files and process them.
*/
- bool mergeOverlay(const Source& src, ResourceTable* table);
+ bool mergeOverlay(const Source& src, ResourceTable* table,
+ io::IFileCollection* collection = nullptr);
/**
* Merges resources from the given package, mangling the name. This is for static libraries.
@@ -104,7 +108,7 @@
bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay);
- bool mergeImpl(const Source& src, ResourceTable* srcTable,
+ bool mergeImpl(const Source& src, ResourceTable* srcTable, io::IFileCollection* collection,
bool overlay, bool allowNew);
bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage,
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index a26d763..568bc74 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -34,17 +34,10 @@
* as needed.
*/
class ReferenceVisitor : public ValueVisitor {
-private:
- IAaptContext* mContext;
- ISymbolTable* mSymbols;
- xml::IPackageDeclStack* mDecls;
- CallSite* mCallSite;
- bool mError;
-
public:
using ValueVisitor::visit;
- ReferenceVisitor(IAaptContext* context, ISymbolTable* symbols, xml::IPackageDeclStack* decls,
+ ReferenceVisitor(IAaptContext* context, SymbolTable* symbols, xml::IPackageDeclStack* decls,
CallSite* callSite) :
mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite),
mError(false) {
@@ -59,25 +52,23 @@
bool hasError() const {
return mError;
}
+
+private:
+ IAaptContext* mContext;
+ SymbolTable* mSymbols;
+ xml::IPackageDeclStack* mDecls;
+ CallSite* mCallSite;
+ bool mError;
};
/**
* Visits each xml Element and compiles the attributes within.
*/
class XmlVisitor : public xml::PackageAwareVisitor {
-private:
- IAaptContext* mContext;
- ISymbolTable* mSymbols;
- Source mSource;
- std::set<int>* mSdkLevelsFound;
- CallSite* mCallSite;
- ReferenceVisitor mReferenceVisitor;
- bool mError = false;
-
public:
using xml::PackageAwareVisitor::visit;
- XmlVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source,
+ XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source,
std::set<int>* sdkLevelsFound, CallSite* callSite) :
mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound),
mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) {
@@ -105,10 +96,13 @@
// Convert the string value into a compiled Value if this is a valid attribute.
if (attr.compiledAttribute) {
- // Record all SDK levels from which the attributes were defined.
- const int sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id);
- if (sdkLevel > 1) {
- mSdkLevelsFound->insert(sdkLevel);
+ if (attr.compiledAttribute.value().id) {
+ // Record all SDK levels from which the attributes were defined.
+ const size_t sdkLevel = findAttributeSdkLevel(
+ attr.compiledAttribute.value().id.value());
+ if (sdkLevel > 1) {
+ mSdkLevelsFound->insert(sdkLevel);
+ }
}
const Attribute* attribute = &attr.compiledAttribute.value().attribute;
@@ -124,6 +118,7 @@
<< *attribute);
mError = true;
}
+
} else {
mContext->getDiagnostics()->error(DiagMessage(source)
<< "attribute '" << package << ":"
@@ -150,6 +145,15 @@
bool hasError() {
return mError || mReferenceVisitor.hasError();
}
+
+private:
+ IAaptContext* mContext;
+ SymbolTable* mSymbols;
+ Source mSource;
+ std::set<int>* mSdkLevelsFound;
+ CallSite* mCallSite;
+ ReferenceVisitor mReferenceVisitor;
+ bool mError = false;
};
} // namespace
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 3bfaf91..af9098b 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -14,12 +14,9 @@
* limitations under the License.
*/
+#include <test/Context.h>
#include "link/Linkers.h"
-
-#include "test/Builders.h"
-#include "test/Context.h"
-
-#include <gtest/gtest.h>
+#include "test/Test.h"
namespace aapt {
@@ -30,7 +27,7 @@
.setCompilationPackage(u"com.app.test")
.setNameManglerPolicy(
NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
- .setSymbolTable(test::StaticSymbolTableBuilder()
+ .addSymbolSource(test::StaticSymbolSourceBuilder()
.addPublicSymbol(u"@android:attr/layout_width", ResourceId(0x01010000),
test::AttributeBuilder()
.setTypeMask(android::ResTable_map::TYPE_ENUM |
@@ -92,14 +89,16 @@
u"layout_width");
ASSERT_NE(xmlAttr, nullptr);
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010000));
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010000));
ASSERT_NE(xmlAttr->compiledValue, nullptr);
ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", u"background");
ASSERT_NE(xmlAttr, nullptr);
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010001));
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010001));
ASSERT_NE(xmlAttr->compiledValue, nullptr);
Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
ASSERT_NE(ref, nullptr);
@@ -163,7 +162,8 @@
u"http://schemas.android.com/apk/res/com.android.support", u"colorAccent");
ASSERT_NE(xmlAttr, nullptr);
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010001));
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010001));
ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
}
@@ -182,7 +182,8 @@
u"colorAccent");
ASSERT_NE(xmlAttr, nullptr);
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010000));
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010000));
Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
ASSERT_NE(ref, nullptr);
AAPT_ASSERT_TRUE(ref->name);
@@ -209,7 +210,8 @@
u"attr");
ASSERT_NE(xmlAttr, nullptr);
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010002));
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010002));
Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
ASSERT_NE(ref, nullptr);
AAPT_ASSERT_TRUE(ref->id);
@@ -223,7 +225,8 @@
xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/com.app.test", u"attr");
ASSERT_NE(xmlAttr, nullptr);
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010002));
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
ref = valueCast<Reference>(xmlAttr->compiledValue.get());
ASSERT_NE(ref, nullptr);
AAPT_ASSERT_TRUE(ref->id);
@@ -246,7 +249,8 @@
u"http://schemas.android.com/apk/res/com.app.test", u"attr");
ASSERT_NE(xmlAttr, nullptr);
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
- EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010002));
+ AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
+ EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
ASSERT_NE(ref, nullptr);
AAPT_ASSERT_TRUE(ref->id);
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index 3a88044..9affb83 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -30,14 +30,14 @@
namespace aapt {
class ResourceTable;
-struct ISymbolTable;
+class SymbolTable;
struct IAaptContext {
virtual ~IAaptContext() = default;
- virtual ISymbolTable* getExternalSymbols() = 0;
+ virtual SymbolTable* getExternalSymbols() = 0;
virtual IDiagnostics* getDiagnostics() = 0;
- virtual StringPiece16 getCompilationPackage() = 0;
+ virtual const std::u16string& getCompilationPackage() = 0;
virtual uint8_t getPackageId() = 0;
virtual NameMangler* getNameMangler() = 0;
virtual bool verbose() = 0;
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index b6030a2..eaaf06f 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -25,11 +25,83 @@
namespace aapt {
-const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& name) {
+void SymbolTable::appendSource(std::unique_ptr<ISymbolSource> source) {
+ mSources.push_back(std::move(source));
+
+ // We do not clear the cache, because sources earlier in the list take precedent.
+}
+
+void SymbolTable::prependSource(std::unique_ptr<ISymbolSource> source) {
+ mSources.insert(mSources.begin(), std::move(source));
+
+ // We must clear the cache in case we did a lookup before adding this resource.
+ mCache.clear();
+}
+
+const SymbolTable::Symbol* SymbolTable::findByName(const ResourceName& name) {
if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
return s.get();
}
+ // We did not find it in the cache, so look through the sources.
+ for (auto& symbolSource : mSources) {
+ std::unique_ptr<Symbol> symbol = symbolSource->findByName(name);
+ if (symbol) {
+ // Take ownership of the symbol into a shared_ptr. We do this because LruCache
+ // doesn't support unique_ptr.
+ std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
+ mCache.put(name, sharedSymbol);
+
+ if (sharedSymbol->id) {
+ // The symbol has an ID, so we can also cache this!
+ mIdCache.put(sharedSymbol->id.value(), sharedSymbol);
+ }
+ return sharedSymbol.get();
+ }
+ }
+ return nullptr;
+}
+
+const SymbolTable::Symbol* SymbolTable::findById(ResourceId id) {
+ if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
+ return s.get();
+ }
+
+ // We did not find it in the cache, so look through the sources.
+ for (auto& symbolSource : mSources) {
+ std::unique_ptr<Symbol> symbol = symbolSource->findById(id);
+ if (symbol) {
+ // Take ownership of the symbol into a shared_ptr. We do this because LruCache
+ // doesn't support unique_ptr.
+ std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
+ mIdCache.put(id, sharedSymbol);
+ return sharedSymbol.get();
+ }
+ }
+ return nullptr;
+}
+
+const SymbolTable::Symbol* SymbolTable::findByReference(const Reference& ref) {
+ // First try the ID. This is because when we lookup by ID, we only fill in the ID cache.
+ // Looking up by name fills in the name and ID cache. So a cache miss will cause a failed
+ // ID lookup, then a successfull name lookup. Subsequent look ups will hit immediately
+ // because the ID is cached too.
+ //
+ // If we looked up by name first, a cache miss would mean we failed to lookup by name, then
+ // succeeded to lookup by ID. Subsequent lookups will miss then hit.
+ const SymbolTable::Symbol* symbol = nullptr;
+ if (ref.id) {
+ symbol = findById(ref.id.value());
+ }
+
+ if (ref.name && !symbol) {
+ symbol = findByName(ref.name.value());
+ }
+ return symbol;
+}
+
+std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::findByName(
+ const ResourceName& name) {
Maybe<ResourceTable::SearchResult> result = mTable->findResource(name);
if (!result) {
if (name.type == ResourceType::kAttr) {
@@ -41,40 +113,35 @@
ResourceTable::SearchResult sr = result.value();
- // If no ID exists, we treat the symbol as missing. SymbolTables are used to
- // find symbols to link.
- if (!sr.package->id || !sr.type->id || !sr.entry->id) {
- return {};
- }
-
- std::shared_ptr<Symbol> symbol = std::make_shared<Symbol>();
- symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
+ std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>();
symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic);
+ if (sr.package->id && sr.type->id && sr.entry->id) {
+ symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
+ }
+
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
const ConfigDescription kDefaultConfig;
ResourceConfigValue* configValue = sr.entry->findValue(kDefaultConfig);
if (configValue) {
// This resource has an Attribute.
if (Attribute* attr = valueCast<Attribute>(configValue->value.get())) {
- symbol->attribute = util::make_unique<Attribute>(*attr);
+ symbol->attribute = std::make_shared<Attribute>(*attr);
} else {
return {};
}
}
}
-
- if (name.type == ResourceType::kAttrPrivate) {
- // Masquerade this entry as kAttr.
- mCache.put(ResourceName(name.package, ResourceType::kAttr, name.entry), symbol);
- } else {
- mCache.put(name, symbol);
- }
- return symbol.get();
+ return symbol;
}
-static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
- ResourceId id) {
+bool AssetManagerSymbolSource::addAssetPath(const StringPiece& path) {
+ int32_t cookie = 0;
+ return mAssets.addAssetPath(android::String8(path.data(), path.size()), &cookie);
+}
+
+static std::unique_ptr<SymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
+ ResourceId id) {
// Try as a bag.
const android::ResTable::bag_entry* entry;
ssize_t count = table.lockBag(id.id, &entry);
@@ -84,13 +151,13 @@
}
// We found a resource.
- std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>();
+ std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>();
s->id = id;
// Check to see if it is an attribute.
for (size_t i = 0; i < (size_t) count; i++) {
if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
- s->attribute = util::make_unique<Attribute>(false);
+ s->attribute = std::make_shared<Attribute>(false);
s->attribute->typeMask = entry[i].map.value.data;
break;
}
@@ -138,43 +205,36 @@
return s;
}
-const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findByName(
+std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByName(
const ResourceName& name) {
- if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
- return s.get();
+ const android::ResTable& table = mAssets.getResources(false);
+ StringPiece16 typeStr = toString(name.type);
+ uint32_t typeSpecFlags = 0;
+ ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(),
+ typeStr.data(), typeStr.size(),
+ name.package.data(), name.package.size(),
+ &typeSpecFlags);
+ if (!resId.isValid()) {
+ return {};
}
- for (const auto& asset : mAssets) {
- const android::ResTable& table = asset->getResources(false);
- StringPiece16 typeStr = toString(name.type);
- uint32_t typeSpecFlags = 0;
- ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(),
- typeStr.data(), typeStr.size(),
- name.package.data(), name.package.size(),
- &typeSpecFlags);
- if (!resId.isValid()) {
- continue;
- }
-
- std::shared_ptr<Symbol> s;
- if (name.type == ResourceType::kAttr) {
- s = lookupAttributeInTable(table, resId);
- } else {
- s = std::make_shared<Symbol>();
- s->id = resId;
- }
-
- if (s) {
- s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- mCache.put(name, s);
- return s.get();
- }
+ std::unique_ptr<SymbolTable::Symbol> s;
+ if (name.type == ResourceType::kAttr) {
+ s = lookupAttributeInTable(table, resId);
+ } else {
+ s = util::make_unique<SymbolTable::Symbol>();
+ s->id = resId;
}
- return nullptr;
+
+ if (s) {
+ s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ return s;
+ }
+ return {};
}
static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) {
- android::ResTable::resource_name resName;
+ android::ResTable::resource_name resName = {};
if (!table.getResourceName(id.id, true, &resName)) {
return {};
}
@@ -211,55 +271,38 @@
return name;
}
-const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findById(
- ResourceId id) {
- if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
- return s.get();
+std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findById(ResourceId id) {
+ const android::ResTable& table = mAssets.getResources(false);
+ Maybe<ResourceName> maybeName = getResourceName(table, id);
+ if (!maybeName) {
+ return {};
}
- for (const auto& asset : mAssets) {
- const android::ResTable& table = asset->getResources(false);
+ uint32_t typeSpecFlags = 0;
+ table.getResourceFlags(id.id, &typeSpecFlags);
- Maybe<ResourceName> maybeName = getResourceName(table, id);
- if (!maybeName) {
- continue;
- }
-
- uint32_t typeSpecFlags = 0;
- table.getResourceFlags(id.id, &typeSpecFlags);
-
- std::shared_ptr<Symbol> s;
- if (maybeName.value().type == ResourceType::kAttr) {
- s = lookupAttributeInTable(table, id);
- } else {
- s = std::make_shared<Symbol>();
- s->id = id;
- }
-
- if (s) {
- s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
- mIdCache.put(id, s);
- return s.get();
- }
+ std::unique_ptr<SymbolTable::Symbol> s;
+ if (maybeName.value().type == ResourceType::kAttr) {
+ s = lookupAttributeInTable(table, id);
+ } else {
+ s = util::make_unique<SymbolTable::Symbol>();
+ s->id = id;
}
- return nullptr;
-}
-const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findByName(
- const ResourceName& name) {
- for (auto& symbolTable : mSymbolTables) {
- if (const Symbol* s = symbolTable->findByName(name)) {
- return s;
- }
+ if (s) {
+ s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
+ return s;
}
return {};
}
-const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findById(ResourceId id) {
- for (auto& symbolTable : mSymbolTables) {
- if (const Symbol* s = symbolTable->findById(id)) {
- return s;
- }
+std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByReference(
+ const Reference& ref) {
+ // AssetManager always prefers IDs.
+ if (ref.id) {
+ return findById(ref.id.value());
+ } else if (ref.name) {
+ return findByName(ref.name.value());
}
return {};
}
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 22096ed..0a6a4a5 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -25,37 +25,20 @@
#include <utils/JenkinsHash.h>
#include <utils/LruCache.h>
+#include <android-base/macros.h>
#include <androidfw/AssetManager.h>
#include <algorithm>
-#include <map>
#include <memory>
#include <vector>
namespace aapt {
-struct ISymbolTable {
- virtual ~ISymbolTable() = default;
-
- struct Symbol {
- ResourceId id;
- std::unique_ptr<Attribute> attribute;
- bool isPublic;
- };
-
- /**
- * Never hold on to the result between calls to findByName or findById. The results
- * are typically stored in a cache which may evict entries.
- */
- virtual const Symbol* findByName(const ResourceName& name) = 0;
- virtual const Symbol* findById(ResourceId id) = 0;
-};
-
inline android::hash_t hash_type(const ResourceName& name) {
std::hash<std::u16string> strHash;
android::hash_t hash = 0;
- hash = android::JenkinsHashMix(hash, strHash(name.package));
+ hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.package));
hash = android::JenkinsHashMix(hash, (uint32_t) name.type);
- hash = android::JenkinsHashMix(hash, strHash(name.entry));
+ hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.entry));
return hash;
}
@@ -63,88 +46,106 @@
return android::hash_type(id.id);
}
-/**
- * Presents a ResourceTable as an ISymbolTable, caching results.
- * Instances of this class must outlive the encompassed ResourceTable.
- * Since symbols are cached, the ResourceTable should not change during the
- * lifetime of this SymbolTableWrapper.
- *
- * If a resource in the ResourceTable does not have a ResourceID assigned to it,
- * it is ignored.
- *
- * Lookups by ID are ignored.
- */
-class SymbolTableWrapper : public ISymbolTable {
+class ISymbolSource;
+
+class SymbolTable {
+public:
+ struct Symbol {
+ Maybe<ResourceId> id;
+ std::shared_ptr<Attribute> attribute;
+ bool isPublic;
+ };
+
+ SymbolTable() : mCache(200), mIdCache(200) {
+ }
+
+ void appendSource(std::unique_ptr<ISymbolSource> source);
+ void prependSource(std::unique_ptr<ISymbolSource> source);
+
+ /**
+ * Never hold on to the result between calls to findByName or findById. The results
+ * are typically stored in a cache which may evict entries.
+ */
+ const Symbol* findByName(const ResourceName& name);
+ const Symbol* findById(ResourceId id);
+
+ /**
+ * Let's the ISymbolSource decide whether looking up by name or ID is faster, if both
+ * are available.
+ */
+ const Symbol* findByReference(const Reference& ref);
+
private:
- ResourceTable* mTable;
+ std::vector<std::unique_ptr<ISymbolSource>> mSources;
// We use shared_ptr because unique_ptr is not supported and
// we need automatic deletion.
android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
+ android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
+ DISALLOW_COPY_AND_ASSIGN(SymbolTable);
+};
+
+/**
+ * An interface that a symbol source implements in order to surface symbol information
+ * to the symbol table.
+ */
+class ISymbolSource {
public:
- SymbolTableWrapper(ResourceTable* table) : mTable(table), mCache(200) {
- }
+ virtual ~ISymbolSource() = default;
- const Symbol* findByName(const ResourceName& name) override;
+ virtual std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) = 0;
+ virtual std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) = 0;
- // Unsupported, all queries to ResourceTable should be done by name.
- const Symbol* findById(ResourceId id) override {
+ /**
+ * Default implementation tries the name if it exists, else the ID.
+ */
+ virtual std::unique_ptr<SymbolTable::Symbol> findByReference(const Reference& ref) {
+ if (ref.name) {
+ return findByName(ref.name.value());
+ } else if (ref.id) {
+ return findById(ref.id.value());
+ }
return {};
}
};
-class AssetManagerSymbolTableBuilder {
-private:
- struct AssetManagerSymbolTable : public ISymbolTable {
- std::vector<std::unique_ptr<android::AssetManager>> mAssets;
-
- // We use shared_ptr because unique_ptr is not supported and
- // we need automatic deletion.
- android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
- android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
-
- AssetManagerSymbolTable() : mCache(200), mIdCache(200) {
- }
-
- const Symbol* findByName(const ResourceName& name) override;
- const Symbol* findById(ResourceId id) override;
- };
-
- std::unique_ptr<AssetManagerSymbolTable> mSymbolTable =
- util::make_unique<AssetManagerSymbolTable>();
-
+/**
+ * Exposes the resources in a ResourceTable as symbols for SymbolTable.
+ * Instances of this class must outlive the encompassed ResourceTable.
+ * Lookups by ID are ignored.
+ */
+class ResourceTableSymbolSource : public ISymbolSource {
public:
- AssetManagerSymbolTableBuilder& add(std::unique_ptr<android::AssetManager> assetManager) {
- mSymbolTable->mAssets.push_back(std::move(assetManager));
- return *this;
+ explicit ResourceTableSymbolSource(ResourceTable* table) : mTable(table) {
}
- std::unique_ptr<ISymbolTable> build() {
- return std::move(mSymbolTable);
+ std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
+
+ std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
+ return {};
}
+
+private:
+ ResourceTable* mTable;
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceTableSymbolSource);
};
-class JoinedSymbolTableBuilder {
-private:
- struct JoinedSymbolTable : public ISymbolTable {
- std::vector<std::unique_ptr<ISymbolTable>> mSymbolTables;
-
- const Symbol* findByName(const ResourceName& name) override;
- const Symbol* findById(ResourceId id) override;
- };
-
- std::unique_ptr<JoinedSymbolTable> mSymbolTable = util::make_unique<JoinedSymbolTable>();
-
+class AssetManagerSymbolSource : public ISymbolSource {
public:
- JoinedSymbolTableBuilder& addSymbolTable(std::unique_ptr<ISymbolTable> table) {
- mSymbolTable->mSymbolTables.push_back(std::move(table));
- return *this;
- }
+ AssetManagerSymbolSource() = default;
- std::unique_ptr<ISymbolTable> build() {
- return std::move(mSymbolTable);
- }
+ bool addAssetPath(const StringPiece& path);
+
+ std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
+ std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override;
+ std::unique_ptr<SymbolTable::Symbol> findByReference(const Reference& ref) override;
+
+private:
+ android::AssetManager mAssets;
+
+ DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
};
} // namespace aapt
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index 1dc3b4f..34f31be 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -15,14 +15,11 @@
*/
#include "process/SymbolTable.h"
-#include "test/Builders.h"
-#include "test/Context.h"
-
-#include <gtest/gtest.h>
+#include "test/Test.h"
namespace aapt {
-TEST(SymbolTableWrapperTest, FindSymbolsWithIds) {
+TEST(ResourceTableSymbolSourceTest, FindSymbols) {
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
.addSimple(u"@android:id/foo", ResourceId(0x01020000))
.addSimple(u"@android:id/bar")
@@ -30,27 +27,27 @@
test::AttributeBuilder().build())
.build();
- SymbolTableWrapper symbolTable(table.get());
- EXPECT_NE(symbolTable.findByName(test::parseNameOrDie(u"@android:id/foo")), nullptr);
- EXPECT_EQ(symbolTable.findByName(test::parseNameOrDie(u"@android:id/bar")), nullptr);
+ ResourceTableSymbolSource symbolSource(table.get());
+ EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie(u"@android:id/foo")));
+ EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie(u"@android:id/bar")));
- const ISymbolTable::Symbol* s = symbolTable.findByName(
+ std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName(
test::parseNameOrDie(u"@android:attr/foo"));
- ASSERT_NE(s, nullptr);
- EXPECT_NE(s->attribute, nullptr);
+ ASSERT_NE(nullptr, s);
+ EXPECT_NE(nullptr, s->attribute);
}
-TEST(SymbolTableWrapperTest, FindPrivateAttrSymbol) {
+TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) {
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
.addValue(u"@android:^attr-private/foo", ResourceId(0x01010000),
test::AttributeBuilder().build())
.build();
- SymbolTableWrapper symbolTable(table.get());
- const ISymbolTable::Symbol* s = symbolTable.findByName(
+ ResourceTableSymbolSource symbolSource(table.get());
+ std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName(
test::parseNameOrDie(u"@android:attr/foo"));
- ASSERT_NE(s, nullptr);
- EXPECT_NE(s->attribute, nullptr);
+ ASSERT_NE(nullptr, s);
+ EXPECT_NE(nullptr, s->attribute);
}
} // namespace aapt
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 9856a00..86883f8 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -483,8 +483,13 @@
}
const size_t padding = 4 - (pbSize & 0x03);
- mData += sizeof(uint64_t) + pbSize + padding;
- mSize -= sizeof(uint64_t) + pbSize + padding;
+ const size_t offset = sizeof(uint64_t) + pbSize + padding;
+ if (offset > mSize) {
+ return nullptr;
+ }
+
+ mData += offset;
+ mSize -= offset;
mPbFile = std::move(pbFile);
}
return mPbFile.get();
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index b3d87d8..5d1b72b 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -178,10 +178,13 @@
void serializeReferenceToPb(const Reference& ref, pb::Reference* pbRef) {
if (ref.id) {
pbRef->set_id(ref.id.value().id);
- } else if (ref.name) {
+ }
+
+ if (ref.name) {
StringPool::Ref symbolRef = mSymbolPool->makeRef(ref.name.value().toString());
pbRef->set_symbol_idx(static_cast<uint32_t>(symbolRef.getIndex()));
}
+
pbRef->set_private_(ref.privateReference);
pbRef->set_type(serializeReferenceTypeToPb(ref.referenceType));
}
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
index 70a33f7..dd995d8 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -62,6 +62,17 @@
test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u),
context->getDiagnostics()));
+ // Make a reference with both resource name and resource ID.
+ // The reference should point to a resource outside of this table to test that both
+ // name and id get serialized.
+ Reference expectedRef;
+ expectedRef.name = test::parseNameOrDie(u"@android:layout/main");
+ expectedRef.id = ResourceId(0x01020000);
+ ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:layout/abc"),
+ ConfigDescription::defaultConfig(), std::string(),
+ util::make_unique<Reference>(expectedRef),
+ context->getDiagnostics()));
+
std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get());
ASSERT_NE(nullptr, pbTable);
@@ -90,6 +101,13 @@
newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet");
ASSERT_NE(nullptr, prim);
EXPECT_EQ(321u, prim->value.data);
+
+ Reference* actualRef = test::getValue<Reference>(newTable.get(), u"@com.app.a:layout/abc");
+ ASSERT_NE(nullptr, actualRef);
+ AAPT_ASSERT_TRUE(actualRef->name);
+ AAPT_ASSERT_TRUE(actualRef->id);
+ EXPECT_EQ(expectedRef.name.value(), actualRef->name.value());
+ EXPECT_EQ(expectedRef.id.value(), actualRef->id.value());
}
TEST(TableProtoSerializer, SerializeFileHeader) {
@@ -130,4 +148,27 @@
EXPECT_EQ(test::parseNameOrDie(u"@+id/unchecked"), file->exportedSymbols[0].name);
}
+TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) {
+ ResourceFile f;
+ std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f);
+
+ const std::string expectedData = "1234";
+
+ std::string outputStr;
+ {
+ google::protobuf::io::StringOutputStream outStream(&outputStr);
+ CompiledFileOutputStream outFileStream(&outStream, pbFile.get());
+
+ ASSERT_TRUE(outFileStream.Write(expectedData.data(), expectedData.size()));
+ ASSERT_TRUE(outFileStream.Finish());
+ }
+
+ outputStr[0] = 0xff;
+
+ CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size());
+ EXPECT_EQ(nullptr, inFileStream.CompiledFile());
+ EXPECT_EQ(nullptr, inFileStream.data());
+ EXPECT_EQ(0u, inFileStream.size());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 834caf8..8c56ebc 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -246,7 +246,7 @@
inline std::unique_ptr<xml::XmlResource> buildXmlDomForPackageName(IAaptContext* context,
const StringPiece& str) {
std::unique_ptr<xml::XmlResource> doc = buildXmlDom(str);
- doc->file.name.package = context->getCompilationPackage().toString();
+ doc->file.name.package = context->getCompilationPackage();
return doc;
}
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index e540cd7..96752d3 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -31,33 +31,16 @@
namespace test {
class Context : public IAaptContext {
-private:
- friend class ContextBuilder;
-
- Context() = default;
-
- Maybe<std::u16string> mCompilationPackage;
- Maybe<uint8_t> mPackageId;
- std::unique_ptr<IDiagnostics> mDiagnostics = util::make_unique<StdErrDiagnostics>();
- std::unique_ptr<ISymbolTable> mSymbols;
- std::unique_ptr<NameMangler> mNameMangler;
-
public:
- ISymbolTable* getExternalSymbols() override {
- assert(mSymbols && "test symbols not set");
- return mSymbols.get();
- }
-
- void setSymbolTable(std::unique_ptr<ISymbolTable> symbols) {
- mSymbols = std::move(symbols);
+ SymbolTable* getExternalSymbols() override {
+ return &mSymbols;
}
IDiagnostics* getDiagnostics() override {
- assert(mDiagnostics && "test diagnostics not set");
- return mDiagnostics.get();
+ return &mDiagnostics;
}
- StringPiece16 getCompilationPackage() override {
+ const std::u16string& getCompilationPackage() override {
assert(mCompilationPackage && "package name not set");
return mCompilationPackage.value();
}
@@ -68,13 +51,24 @@
}
NameMangler* getNameMangler() override {
- assert(mNameMangler && "test name mangler not set");
- return mNameMangler.get();
+ return &mNameMangler;
}
bool verbose() override {
return false;
}
+
+private:
+ friend class ContextBuilder;
+
+ Context() : mNameMangler({}) {
+ }
+
+ Maybe<std::u16string> mCompilationPackage;
+ Maybe<uint8_t> mPackageId;
+ StdErrDiagnostics mDiagnostics;
+ SymbolTable mSymbols;
+ NameMangler mNameMangler;
};
class ContextBuilder {
@@ -92,18 +86,13 @@
return *this;
}
- ContextBuilder& setSymbolTable(std::unique_ptr<ISymbolTable> symbols) {
- mContext->mSymbols = std::move(symbols);
- return *this;
- }
-
- ContextBuilder& setDiagnostics(std::unique_ptr<IDiagnostics> diag) {
- mContext->mDiagnostics = std::move(diag);
- return *this;
- }
-
ContextBuilder& setNameManglerPolicy(NameManglerPolicy policy) {
- mContext->mNameMangler = util::make_unique<NameMangler>(policy);
+ mContext->mNameMangler = NameMangler(policy);
+ return *this;
+ }
+
+ ContextBuilder& addSymbolSource(std::unique_ptr<ISymbolSource> src) {
+ mContext->getExternalSymbols()->appendSource(std::move(src));
return *this;
}
@@ -112,57 +101,72 @@
}
};
-class StaticSymbolTableBuilder {
-private:
- struct SymbolTable : public ISymbolTable {
- std::list<std::unique_ptr<Symbol>> mSymbols;
- std::map<ResourceName, Symbol*> mNameMap;
- std::map<ResourceId, Symbol*> mIdMap;
+class StaticSymbolSourceBuilder {
+public:
+ StaticSymbolSourceBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id,
+ std::unique_ptr<Attribute> attr = {}) {
+ std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
+ id, std::move(attr), true);
+ mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
+ mSymbolSource->mIdMap[id] = symbol.get();
+ mSymbolSource->mSymbols.push_back(std::move(symbol));
+ return *this;
+ }
- const Symbol* findByName(const ResourceName& name) override {
+ StaticSymbolSourceBuilder& addSymbol(const StringPiece16& name, ResourceId id,
+ std::unique_ptr<Attribute> attr = {}) {
+ std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
+ id, std::move(attr), false);
+ mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
+ mSymbolSource->mIdMap[id] = symbol.get();
+ mSymbolSource->mSymbols.push_back(std::move(symbol));
+ return *this;
+ }
+
+ std::unique_ptr<ISymbolSource> build() {
+ return std::move(mSymbolSource);
+ }
+
+private:
+ class StaticSymbolSource : public ISymbolSource {
+ public:
+ StaticSymbolSource() = default;
+
+ std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override {
auto iter = mNameMap.find(name);
if (iter != mNameMap.end()) {
- return iter->second;
+ return cloneSymbol(iter->second);
}
return nullptr;
}
- const Symbol* findById(ResourceId id) override {
+ std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
auto iter = mIdMap.find(id);
if (iter != mIdMap.end()) {
- return iter->second;
+ return cloneSymbol(iter->second);
}
return nullptr;
}
+
+ std::list<std::unique_ptr<SymbolTable::Symbol>> mSymbols;
+ std::map<ResourceName, SymbolTable::Symbol*> mNameMap;
+ std::map<ResourceId, SymbolTable::Symbol*> mIdMap;
+
+ private:
+ std::unique_ptr<SymbolTable::Symbol> cloneSymbol(SymbolTable::Symbol* sym) {
+ std::unique_ptr<SymbolTable::Symbol> clone = util::make_unique<SymbolTable::Symbol>();
+ clone->id = sym->id;
+ if (sym->attribute) {
+ clone->attribute = std::unique_ptr<Attribute>(sym->attribute->clone(nullptr));
+ }
+ clone->isPublic = sym->isPublic;
+ return clone;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(StaticSymbolSource);
};
- std::unique_ptr<SymbolTable> mSymbolTable = util::make_unique<SymbolTable>();
-
-public:
- StaticSymbolTableBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id,
- std::unique_ptr<Attribute> attr = {}) {
- std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
- id, std::move(attr));
- symbol->isPublic = true;
- mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
- mSymbolTable->mIdMap[id] = symbol.get();
- mSymbolTable->mSymbols.push_back(std::move(symbol));
- return *this;
- }
-
- StaticSymbolTableBuilder& addSymbol(const StringPiece16& name, ResourceId id,
- std::unique_ptr<Attribute> attr = {}) {
- std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
- id, std::move(attr));
- mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
- mSymbolTable->mIdMap[id] = symbol.get();
- mSymbolTable->mSymbols.push_back(std::move(symbol));
- return *this;
- }
-
- std::unique_ptr<ISymbolTable> build() {
- return std::move(mSymbolTable);
- }
+ std::unique_ptr<StaticSymbolSource> mSymbolSource = util::make_unique<StaticSymbolSource>();
};
} // namespace test
diff --git a/tools/aapt2/test/Test.h b/tools/aapt2/test/Test.h
new file mode 100644
index 0000000..d4845cf
--- /dev/null
+++ b/tools/aapt2/test/Test.h
@@ -0,0 +1,32 @@
+/*
+ * 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 AAPT_TEST_TEST_H
+#define AAPT_TEST_TEST_H
+
+#include "test/Builders.h"
+#include "test/Common.h"
+#include "test/Context.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+namespace test {
+
+} // namespace test
+} // namespace aapt
+
+#endif // AAPT_TEST_TEST_H
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 04e8199..6428e98 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -96,7 +96,7 @@
const char* start = path.begin();
const char* end = path.end();
for (const char* current = start; current != end; ++current) {
- if (*current == sDirSep) {
+ if (*current == sDirSep && current != start) {
StringPiece parentPath(start, current - start);
int result = mkdirImpl(parentPath);
if (result < 0 && errno != EEXIST) {
@@ -139,6 +139,20 @@
return {};
}
+void appendPath(std::string* base, StringPiece part) {
+ assert(base);
+ const bool baseHasTrailingSep = (!base->empty() && *(base->end() - 1) == sDirSep);
+ const bool partHasLeadingSep = (!part.empty() && *(part.begin()) == sDirSep);
+ if (baseHasTrailingSep && partHasLeadingSep) {
+ // Remove the part's leading sep
+ part = part.substr(1, part.size() - 1);
+ } else if (!baseHasTrailingSep && !partHasLeadingSep) {
+ // None of the pieces has a separator.
+ *base += sDirSep;
+ }
+ base->append(part.data(), part.size());
+}
+
std::string packageToPath(const StringPiece& package) {
std::string outPath;
for (StringPiece part : util::tokenize<char>(package, '.')) {
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index c58ba5d..c2e6115 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -61,14 +61,7 @@
/*
* Appends a path to `base`, separated by the directory separator.
*/
-void appendPath(std::string* base, const StringPiece& part);
-
-/*
- * Appends a series of paths to `base`, separated by the
- * system directory separator.
- */
-template <typename... Ts >
-void appendPath(std::string* base, const StringPiece& part, const Ts&... parts);
+void appendPath(std::string* base, StringPiece part);
/*
* Makes all the directories in `path`. The last element in the path
@@ -139,20 +132,6 @@
std::vector<std::string> mPatternTokens;
};
-inline void appendPath(std::string* base, const StringPiece& part) {
- assert(base);
- *base += sDirSep;
- base->append(part.data(), part.size());
-}
-
-template <typename... Ts >
-void appendPath(std::string* base, const StringPiece& part, const Ts&... parts) {
- assert(base);
- *base += sDirSep;
- base->append(part.data(), part.size());
- appendPath(base, parts...);
-}
-
} // namespace file
} // namespace aapt
diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp
new file mode 100644
index 0000000..efb0459
--- /dev/null
+++ b/tools/aapt2/util/Files_test.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "test/Test.h"
+#include "util/Files.h"
+
+#include <sstream>
+
+namespace aapt {
+namespace file {
+
+class FilesTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ std::stringstream builder;
+ builder << "hello" << sDirSep << "there";
+ mExpectedPath = builder.str();
+ }
+
+protected:
+ std::string mExpectedPath;
+};
+
+TEST_F(FilesTest, appendPath) {
+ std::string base = "hello";
+ appendPath(&base, "there");
+ EXPECT_EQ(mExpectedPath, base);
+}
+
+TEST_F(FilesTest, appendPathWithLeadingOrTrailingSeparators) {
+ std::string base = "hello/";
+ appendPath(&base, "there");
+ EXPECT_EQ(mExpectedPath, base);
+
+ base = "hello";
+ appendPath(&base, "/there");
+ EXPECT_EQ(mExpectedPath, base);
+
+ base = "hello/";
+ appendPath(&base, "/there");
+ EXPECT_EQ(mExpectedPath, base);
+}
+
+} // namespace files
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 033b0a4d..b374d20 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -68,7 +68,7 @@
};
struct AaptAttribute {
- ResourceId id;
+ Maybe<ResourceId> id;
aapt::Attribute attribute;
};
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
new file mode 100755
index 0000000..fb2213c
--- /dev/null
+++ b/tools/fonts/fontchain_lint.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+
+import collections
+import glob
+from os import path
+import sys
+from xml.etree import ElementTree
+
+from fontTools import ttLib
+
+LANG_TO_SCRIPT = {
+ 'de': 'Latn',
+ 'en': 'Latn',
+ 'es': 'Latn',
+ 'eu': 'Latn',
+ 'ja': 'Jpan',
+ 'ko': 'Kore',
+ 'hu': 'Latn',
+ 'hy': 'Armn',
+ 'nb': 'Latn',
+ 'nn': 'Latn',
+ 'pt': 'Latn',
+}
+
+def lang_to_script(lang_code):
+ lang = lang_code.lower()
+ while lang not in LANG_TO_SCRIPT:
+ hyphen_idx = lang.rfind('-')
+ assert hyphen_idx != -1, (
+ 'We do not know what script the "%s" language is written in.'
+ % lang_code)
+ assumed_script = lang[hyphen_idx+1:]
+ if len(assumed_script) == 4 and assumed_script.isalpha():
+ # This is actually the script
+ return assumed_script.title()
+ lang = lang[:hyphen_idx]
+ return LANG_TO_SCRIPT[lang]
+
+
+def get_best_cmap(font):
+ font_file, index = font
+ font_path = path.join(_fonts_dir, font_file)
+ if index is not None:
+ ttfont = ttLib.TTFont(font_path, fontNumber=index)
+ else:
+ ttfont = ttLib.TTFont(font_path)
+ all_unicode_cmap = None
+ bmp_cmap = None
+ for cmap in ttfont['cmap'].tables:
+ specifier = (cmap.format, cmap.platformID, cmap.platEncID)
+ if specifier == (4, 3, 1):
+ assert bmp_cmap is None, 'More than one BMP cmap in %s' % (font, )
+ bmp_cmap = cmap
+ elif specifier == (12, 3, 10):
+ assert all_unicode_cmap is None, (
+ 'More than one UCS-4 cmap in %s' % (font, ))
+ all_unicode_cmap = cmap
+
+ return all_unicode_cmap.cmap if all_unicode_cmap else bmp_cmap.cmap
+
+
+def assert_font_supports_any_of_chars(font, chars):
+ best_cmap = get_best_cmap(font)
+ for char in chars:
+ if char in best_cmap:
+ return
+ sys.exit('None of characters in %s were found in %s' % (chars, font))
+
+
+def check_hyphens(hyphens_dir):
+ # Find all the scripts that need automatic hyphenation
+ scripts = set()
+ for hyb_file in glob.iglob(path.join(hyphens_dir, '*.hyb')):
+ hyb_file = path.basename(hyb_file)
+ assert hyb_file.startswith('hyph-'), (
+ 'Unknown hyphenation file %s' % hyb_file)
+ lang_code = hyb_file[hyb_file.index('-')+1:hyb_file.index('.')]
+ scripts.add(lang_to_script(lang_code))
+
+ HYPHENS = {0x002D, 0x2010}
+ for script in scripts:
+ fonts = _script_to_font_map[script]
+ assert fonts, 'No fonts found for the "%s" script' % script
+ for font in fonts:
+ assert_font_supports_any_of_chars(font, HYPHENS)
+
+
+def parse_fonts_xml(fonts_xml_path):
+ global _script_to_font_map, _fallback_chain
+ _script_to_font_map = collections.defaultdict(set)
+ _fallback_chain = []
+ tree = ElementTree.parse(fonts_xml_path)
+ for family in tree.findall('family'):
+ name = family.get('name')
+ variant = family.get('variant')
+ langs = family.get('lang')
+ if name:
+ assert variant is None, (
+ 'No variant expected for LGC font %s.' % name)
+ assert langs is None, (
+ 'No language expected for LGC fonts %s.' % name)
+ else:
+ assert variant in {None, 'elegant', 'compact'}, (
+ 'Unexpected value for variant: %s' % variant)
+
+ if langs:
+ langs = langs.split()
+ scripts = {lang_to_script(lang) for lang in langs}
+ else:
+ scripts = set()
+
+ for child in family:
+ assert child.tag == 'font', (
+ 'Unknown tag <%s>' % child.tag)
+ font_file = child.text
+ weight = int(child.get('weight'))
+ assert weight % 100 == 0, (
+ 'Font weight "%d" is not a multiple of 100.' % weight)
+
+ style = child.get('style')
+ assert style in {'normal', 'italic'}, (
+ 'Unknown style "%s"' % style)
+
+ index = child.get('index')
+ if index:
+ index = int(index)
+
+ _fallback_chain.append((
+ name,
+ frozenset(scripts),
+ variant,
+ weight,
+ style,
+ (font_file, index)))
+
+ if name: # non-empty names are used for default LGC fonts
+ map_scripts = {'Latn', 'Grek', 'Cyrl'}
+ else:
+ map_scripts = scripts
+ for script in map_scripts:
+ _script_to_font_map[script].add((font_file, index))
+
+
+def main():
+ target_out = sys.argv[1]
+ global _fonts_dir
+ _fonts_dir = path.join(target_out, 'fonts')
+
+ fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml')
+ parse_fonts_xml(fonts_xml_path)
+
+ hyphens_dir = path.join(target_out, 'usr', 'hyphen-data')
+ check_hyphens(hyphens_dir)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/layoutlib/.idea/runConfigurations/Create.xml b/tools/layoutlib/.idea/runConfigurations/Create.xml
index fb798b6..536a23f 100644
--- a/tools/layoutlib/.idea/runConfigurations/Create.xml
+++ b/tools/layoutlib/.idea/runConfigurations/Create.xml
@@ -3,7 +3,7 @@
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<option name="MAIN_CLASS_NAME" value="com.android.tools.layoutlib.create.Main" />
<option name="VM_PARAMETERS" value="-ea" />
- <option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/icu4j-icudata-jarjar_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/icu4j-icutzdata-jarjar_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar" />
+ <option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icudata-host-jarjar_intermediates/classes-jarjar.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icutzdata-host-jarjar_intermediates/classes-jarjar.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/../../../../" />
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" value="" />
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index ae42ba6..d0e431a 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -25,14 +25,9 @@
import com.android.internal.util.XmlUtils;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.impl.ParserFactory;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.resources.ResourceType;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.annotation.Nullable;
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
@@ -42,7 +37,6 @@
import android.view.LayoutInflater_Delegate;
import android.view.ViewGroup.LayoutParams;
-import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
@@ -71,7 +65,7 @@
*/
public final class BridgeTypedArray extends TypedArray {
- private final BridgeResources mBridgeResources;
+ private final Resources mBridgeResources;
private final BridgeContext mContext;
private final boolean mPlatformFile;
@@ -84,7 +78,7 @@
@Nullable
private int[] mEmptyIds;
- public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
+ public BridgeTypedArray(Resources resources, BridgeContext context, int len,
boolean platformFile) {
super(resources, null, null, 0);
mBridgeResources = resources;
@@ -306,76 +300,21 @@
}
@Override
- public ComplexColor getComplexColor(int index) {
- // TODO: Support GradientColor
- return getColorStateList(index);
- }
-
- /**
- * Retrieve the ColorStateList for the attribute at <var>index</var>.
- * The value may be either a single solid color or a reference to
- * a color or complex {@link android.content.res.ColorStateList} description.
- *
- * @param index Index of attribute to retrieve.
- *
- * @return ColorStateList for the attribute, or null if not defined.
- */
- @Override
public ColorStateList getColorStateList(int index) {
if (!hasValue(index)) {
return null;
}
- ResourceValue resValue = mResourceData[index];
- String value = resValue.getValue();
+ return ResourceHelper.getColorStateList(mResourceData[index], mContext);
+ }
- if (value == null) {
+ @Override
+ public ComplexColor getComplexColor(int index) {
+ if (!hasValue(index)) {
return null;
}
-
- try {
- // Get the state list file content from callback to parse PSI file
- XmlPullParser parser = mContext.getLayoutlibCallback().getXmlFileParser(value);
- if (parser == null) {
- // If used with a version of Android Studio that does not implement getXmlFileParser
- // fall back to reading the file from disk
- File f = new File(value);
- if (f.isFile()) {
- parser = ParserFactory.create(f);
- }
- }
- if (parser != null) {
- BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
- parser, mContext, resValue.isFramework());
- try {
- return ColorStateList.createFromXml(mContext.getResources(), blockParser,
- mContext.getTheme());
- } finally {
- blockParser.ensurePopped();
- }
- }
- } catch (XmlPullParserException e) {
- Bridge.getLog().error(LayoutLog.TAG_BROKEN,
- "Failed to configure parser for " + value, e, null);
- return null;
- } catch (Exception e) {
- // this is an error and not warning since the file existence is checked before
- // attempting to parse it.
- Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
- "Failed to parse file " + value, e, null);
-
- return null;
- }
-
- try {
- int color = ResourceHelper.getColor(value);
- return ColorStateList.valueOf(color);
- } catch (NumberFormatException e) {
- Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null);
- }
-
- return null;
+ return ResourceHelper.getComplexColor(mResourceData[index], mContext);
}
/**
@@ -762,7 +701,8 @@
if (resVal instanceof ArrayResourceValue) {
ArrayResourceValue array = (ArrayResourceValue) resVal;
int count = array.getElementCount();
- return count >= 0 ? mBridgeResources.fillValues(array, new CharSequence[count]) : null;
+ return count >= 0 ? Resources_Delegate.fillValues(mBridgeResources, array, new CharSequence[count]) :
+ null;
}
int id = getResourceId(index, 0);
String resIdMessage = id > 0 ? " (resource id 0x" + Integer.toHexString(id) + ')' : "";
@@ -1010,7 +950,6 @@
}
static TypedArray obtain(Resources res, int len) {
- return res instanceof BridgeResources ?
- new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
+ return new BridgeTypedArray(res, null, len, true);
}
}
diff --git a/tools/layoutlib/bridge/src/android/content/res/ComplexColor_Accessor.java b/tools/layoutlib/bridge/src/android/content/res/ComplexColor_Accessor.java
new file mode 100644
index 0000000..09c0260
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/content/res/ComplexColor_Accessor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.content.res;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources.Theme;
+import android.util.AttributeSet;
+
+import java.io.IOException;
+
+/**
+ * Class that provides access to the {@link GradientColor#createFromXmlInner(Resources,
+ * XmlPullParser, AttributeSet, Theme)} and {@link ColorStateList#createFromXmlInner(Resources,
+ * XmlPullParser, AttributeSet, Theme)} methods
+ */
+public class ComplexColor_Accessor {
+ public static GradientColor createGradientColorFromXmlInner(@NonNull Resources r,
+ @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws IOException, XmlPullParserException {
+ return GradientColor.createFromXmlInner(r, parser, attrs, theme);
+ }
+
+ public static ColorStateList createColorStateListFromXmlInner(@NonNull Resources r,
+ @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
+ throws IOException, XmlPullParserException {
+ return ColorStateList.createFromXmlInner(r, parser, attrs, theme);
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
similarity index 63%
rename from tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
rename to tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index fe46480..985dd5a 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 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.
@@ -28,8 +28,10 @@
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.impl.ParserFactory;
import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.layoutlib.bridge.util.NinePatchInputStream;
import com.android.ninepatch.NinePatch;
import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import com.android.util.Pair;
import org.xmlpull.v1.XmlPullParser;
@@ -37,6 +39,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources.Theme;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -50,132 +54,101 @@
import java.util.Iterator;
@SuppressWarnings("deprecation")
-public final class BridgeResources extends Resources {
+public class Resources_Delegate {
- private BridgeContext mContext;
- private LayoutlibCallback mLayoutlibCallback;
- private boolean[] mPlatformResourceFlag = new boolean[1];
- private TypedValue mTmpValue = new TypedValue();
+ private static boolean[] mPlatformResourceFlag = new boolean[1];
- /**
- * Simpler wrapper around FileInputStream. This is used when the input stream represent
- * not a normal bitmap but a nine patch.
- * This is useful when the InputStream is created in a method but used in another that needs
- * to know whether this is 9-patch or not, such as BitmapFactory.
- */
- public class NinePatchInputStream extends FileInputStream {
- private boolean mFakeMarkSupport = true;
- public NinePatchInputStream(File file) throws FileNotFoundException {
- super(file);
- }
-
- @Override
- public boolean markSupported() {
- //noinspection SimplifiableIfStatement
- if (mFakeMarkSupport) {
- // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
- return true;
- }
-
- return super.markSupported();
- }
-
- public void disableFakeMarkSupport() {
- // disable fake mark support so that in case codec actually try to use them
- // we don't lie to them.
- mFakeMarkSupport = false;
- }
- }
-
- /**
- * This initializes the static field {@link Resources#mSystem} which is used
- * by methods who get global resources using {@link Resources#getSystem()}.
- * <p/>
- * They will end up using our bridge resources.
- * <p/>
- * {@link Bridge} calls this method after setting up a new bridge.
- */
public static Resources initSystem(BridgeContext context,
AssetManager assets,
DisplayMetrics metrics,
Configuration config,
LayoutlibCallback layoutlibCallback) {
- return Resources.mSystem = new BridgeResources(context,
- assets,
- metrics,
- config,
- layoutlibCallback);
+ Resources resources = new Resources(assets, metrics, config);
+ resources.mContext = context;
+ resources.mLayoutlibCallback = layoutlibCallback;
+ return Resources.mSystem = resources;
}
/**
- * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects
- * around that would prevent us from unloading the library.
+ * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects around that
+ * would prevent us from unloading the library.
*/
public static void disposeSystem() {
- if (Resources.mSystem instanceof BridgeResources) {
- ((BridgeResources)(Resources.mSystem)).mContext = null;
- ((BridgeResources)(Resources.mSystem)).mLayoutlibCallback = null;
- }
+ Resources.mSystem.mContext = null;
+ Resources.mSystem.mLayoutlibCallback = null;
Resources.mSystem = null;
}
- private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics,
- Configuration config, LayoutlibCallback layoutlibCallback) {
- super(assets, metrics, config);
- mContext = context;
- mLayoutlibCallback = layoutlibCallback;
+ public static BridgeTypedArray newTypeArray(Resources resources, int numEntries,
+ boolean platformFile) {
+ return new BridgeTypedArray(resources, resources.mContext, numEntries, platformFile);
}
- public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) {
- return new BridgeTypedArray(this, mContext, numEntries, platformFile);
- }
-
- private Pair<String, ResourceValue> getResourceValue(int id, boolean[] platformResFlag_out) {
+ private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id,
+ boolean[] platformResFlag_out) {
// first get the String related to this id in the framework
Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
+ // Set the layoutlib callback and context for resources
+ if (resources != Resources.mSystem && resources.mLayoutlibCallback == null) {
+ resources.mLayoutlibCallback = Resources.mSystem.mLayoutlibCallback;
+ resources.mContext = Resources.mSystem.mContext;
+ }
+
if (resourceInfo != null) {
platformResFlag_out[0] = true;
String attributeName = resourceInfo.getSecond();
- return Pair.of(attributeName, mContext.getRenderResources().getFrameworkResource(
- resourceInfo.getFirst(), attributeName));
+ return Pair.of(attributeName,
+ resources.mContext.getRenderResources().getFrameworkResource(
+ resourceInfo.getFirst(), attributeName));
}
// didn't find a match in the framework? look in the project.
- if (mLayoutlibCallback != null) {
- resourceInfo = mLayoutlibCallback.resolveResourceId(id);
+ if (resources.mLayoutlibCallback != null) {
+ resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id);
if (resourceInfo != null) {
platformResFlag_out[0] = false;
String attributeName = resourceInfo.getSecond();
- return Pair.of(attributeName, mContext.getRenderResources().getProjectResource(
- resourceInfo.getFirst(), attributeName));
+ return Pair.of(attributeName,
+ resources.mContext.getRenderResources().getProjectResource(
+ resourceInfo.getFirst(), attributeName));
}
}
return null;
}
- @Override
- public Drawable getDrawable(int id, Theme theme) {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static Drawable getDrawable(Resources resources, int id) {
+ return getDrawable(resources, id, null);
+ }
+
+ @LayoutlibDelegate
+ static Drawable getDrawable(Resources resources, int id, Theme theme) {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
- return ResourceHelper.getDrawable(value.getSecond(), mContext, theme);
+ return ResourceHelper.getDrawable(value.getSecond(), resources.mContext, theme);
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
- @Override
- public int getColor(int id, Theme theme) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static int getColor(Resources resources, int id) {
+ return getColor(resources, id, null);
+ }
+
+ @LayoutlibDelegate
+ static int getColor(Resources resources, int id, Theme theme) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
ResourceValue resourceValue = value.getSecond();
@@ -187,7 +160,7 @@
String message;
if (new File(resourceValue.getValue()).isFile()) {
String resource = (resourceValue.isFramework() ? "@android:" : "@") + "color/"
- + resourceValue.getName();
+ + resourceValue.getName();
message = "Hexadecimal color expected, found Color State List for " + resource;
} else {
message = e.getMessage();
@@ -200,31 +173,57 @@
// Suppress possible NPE. getColorStateList will never return null, it will instead
// throw an exception, but intelliJ can't figure that out
//noinspection ConstantConditions
- return getColorStateList(id, theme).getDefaultColor();
+ return getColorStateList(resources, id, theme).getDefaultColor();
}
- @Override
- public ColorStateList getColorStateList(int id, Theme theme) throws NotFoundException {
- Pair<String, ResourceValue> resValue = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static ColorStateList getColorStateList(Resources resources, int id) throws NotFoundException {
+ return getColorStateList(resources, id, null);
+ }
+
+ @LayoutlibDelegate
+ static ColorStateList getColorStateList(Resources resources, int id, Theme theme)
+ throws NotFoundException {
+ Pair<String, ResourceValue> resValue =
+ getResourceValue(resources, id, mPlatformResourceFlag);
if (resValue != null) {
ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(),
- mContext);
+ resources.mContext);
if (stateList != null) {
return stateList.obtainForTheme(theme);
}
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
- @Override
- public CharSequence getText(int id) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static CharSequence getText(Resources resources, int id, CharSequence def) {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+
+ if (value != null) {
+ ResourceValue resValue = value.getSecond();
+
+ assert resValue != null;
+ if (resValue != null) {
+ String v = resValue.getValue();
+ if (v != null) {
+ return v;
+ }
+ }
+ }
+
+ return def;
+ }
+
+ @LayoutlibDelegate
+ static CharSequence getText(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
ResourceValue resValue = value.getSecond();
@@ -239,38 +238,38 @@
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
- @Override
- public CharSequence[] getTextArray(int id) throws NotFoundException {
- ResourceValue resValue = getArrayResourceValue(id);
+ @LayoutlibDelegate
+ static CharSequence[] getTextArray(Resources resources, int id) throws NotFoundException {
+ ResourceValue resValue = getArrayResourceValue(resources, id);
if (resValue == null) {
// Error already logged by getArrayResourceValue.
return new CharSequence[0];
} else if (!(resValue instanceof ArrayResourceValue)) {
return new CharSequence[]{
- resolveReference(resValue.getValue(), resValue.isFramework())};
+ resolveReference(resources, resValue.getValue(), resValue.isFramework())};
}
ArrayResourceValue arv = ((ArrayResourceValue) resValue);
- return fillValues(arv, new CharSequence[arv.getElementCount()]);
+ return fillValues(resources, arv, new CharSequence[arv.getElementCount()]);
}
- @Override
- public String[] getStringArray(int id) throws NotFoundException {
- ResourceValue resValue = getArrayResourceValue(id);
+ @LayoutlibDelegate
+ static String[] getStringArray(Resources resources, int id) throws NotFoundException {
+ ResourceValue resValue = getArrayResourceValue(resources, id);
if (resValue == null) {
// Error already logged by getArrayResourceValue.
return new String[0];
} else if (!(resValue instanceof ArrayResourceValue)) {
return new String[]{
- resolveReference(resValue.getValue(), resValue.isFramework())};
+ resolveReference(resources, resValue.getValue(), resValue.isFramework())};
}
ArrayResourceValue arv = ((ArrayResourceValue) resValue);
- return fillValues(arv, new String[arv.getElementCount()]);
+ return fillValues(resources, arv, new String[arv.getElementCount()]);
}
/**
@@ -278,25 +277,26 @@
* always Strings. The ideal signature for the method should be <T super String>, but java
* generics don't support it.
*/
- <T extends CharSequence> T[] fillValues(ArrayResourceValue resValue, T[] values) {
+ static <T extends CharSequence> T[] fillValues(Resources resources, ArrayResourceValue resValue,
+ T[] values) {
int i = 0;
for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
@SuppressWarnings("unchecked")
- T s = (T) resolveReference(iterator.next(), resValue.isFramework());
+ T s = (T) resolveReference(resources, iterator.next(), resValue.isFramework());
values[i] = s;
}
return values;
}
- @Override
- public int[] getIntArray(int id) throws NotFoundException {
- ResourceValue rv = getArrayResourceValue(id);
+ @LayoutlibDelegate
+ static int[] getIntArray(Resources resources, int id) throws NotFoundException {
+ ResourceValue rv = getArrayResourceValue(resources, id);
if (rv == null) {
// Error already logged by getArrayResourceValue.
return new int[0];
} else if (!(rv instanceof ArrayResourceValue)) {
// This is an older IDE that can only give us the first element of the array.
- String firstValue = resolveReference(rv.getValue(), rv.isFramework());
+ String firstValue = resolveReference(resources, rv.getValue(), rv.isFramework());
try {
return new int[]{getInt(firstValue)};
} catch (NumberFormatException e) {
@@ -310,7 +310,7 @@
int[] values = new int[resValue.getElementCount()];
int i = 0;
for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
- String element = resolveReference(iterator.next(), resValue.isFramework());
+ String element = resolveReference(resources, iterator.next(), resValue.isFramework());
try {
values[i] = getInt(element);
} catch (NumberFormatException e) {
@@ -330,11 +330,13 @@
* method returns the ResourceValue. This happens on older versions of the IDE, which did not
* parse the array resources properly.
* <p/>
+ *
* @throws NotFoundException if no resource if found
*/
@Nullable
- private ResourceValue getArrayResourceValue(int id) throws NotFoundException {
- Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+ private static ResourceValue getArrayResourceValue(Resources resources, int id)
+ throws NotFoundException {
+ Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
if (v != null) {
ResourceValue resValue = v.getSecond();
@@ -360,19 +362,20 @@
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
@NonNull
- private String resolveReference(@NonNull String ref, boolean forceFrameworkOnly) {
+ private static String resolveReference(Resources resources, @NonNull String ref,
+ boolean forceFrameworkOnly) {
if (ref.startsWith(SdkConstants.PREFIX_RESOURCE_REF) || ref.startsWith
(SdkConstants.PREFIX_THEME_REF)) {
ResourceValue rv =
- mContext.getRenderResources().findResValue(ref, forceFrameworkOnly);
- rv = mContext.getRenderResources().resolveResValue(rv);
+ resources.mContext.getRenderResources().findResValue(ref, forceFrameworkOnly);
+ rv = resources.mContext.getRenderResources().resolveResValue(rv);
if (rv != null) {
return rv.getValue();
} else {
@@ -384,9 +387,9 @@
return ref;
}
- @Override
- public XmlResourceParser getLayout(int id) throws NotFoundException {
- Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static XmlResourceParser getLayout(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
if (v != null) {
ResourceValue value = v.getSecond();
@@ -394,8 +397,8 @@
try {
// check if the current parser can provide us with a custom parser.
- if (mPlatformResourceFlag[0] == false) {
- parser = mLayoutlibCallback.getParser(value);
+ if (!mPlatformResourceFlag[0]) {
+ parser = resources.mLayoutlibCallback.getParser(value);
}
// create a new one manually if needed.
@@ -409,7 +412,8 @@
}
if (parser != null) {
- return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+ return new BridgeXmlBlockParser(parser, resources.mContext,
+ mPlatformResourceFlag[0]);
}
} catch (XmlPullParserException e) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
@@ -422,19 +426,19 @@
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
- @Override
- public XmlResourceParser getAnimation(int id) throws NotFoundException {
- Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static XmlResourceParser getAnimation(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
if (v != null) {
ResourceValue value = v.getSecond();
- XmlPullParser parser = null;
+ XmlPullParser parser;
try {
File xml = new File(value.getValue());
@@ -443,7 +447,8 @@
// give that to our XmlBlockParser
parser = ParserFactory.create(xml);
- return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+ return new BridgeXmlBlockParser(parser, resources.mContext,
+ mPlatformResourceFlag[0]);
}
} catch (XmlPullParserException e) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
@@ -456,26 +461,31 @@
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
- @Override
- public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
- return mContext.obtainStyledAttributes(set, attrs);
+ @LayoutlibDelegate
+ static TypedArray obtainAttributes(Resources resources, AttributeSet set, int[] attrs) {
+ return resources.mContext.obtainStyledAttributes(set, attrs);
}
- @Override
- public TypedArray obtainTypedArray(int id) throws NotFoundException {
+ @LayoutlibDelegate
+ static TypedArray obtainAttributes(Resources resources, Resources.Theme theme, AttributeSet
+ set, int[] attrs) {
+ return Resources.obtainAttributes_Original(resources, theme, set, attrs);
+ }
+
+ @LayoutlibDelegate
+ static TypedArray obtainTypedArray(Resources resources, int id) throws NotFoundException {
throw new UnsupportedOperationException();
}
-
- @Override
- public float getDimension(int id) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static float getDimension(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
ResourceValue resValue = value.getSecond();
@@ -490,26 +500,26 @@
} else if (v.equals(BridgeConstants.WRAP_CONTENT)) {
return LayoutParams.WRAP_CONTENT;
}
-
+ TypedValue tmpValue = new TypedValue();
if (ResourceHelper.parseFloatAttribute(
- value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
- mTmpValue.type == TypedValue.TYPE_DIMENSION) {
- return mTmpValue.getDimension(getDisplayMetrics());
+ value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+ tmpValue.type == TypedValue.TYPE_DIMENSION) {
+ return tmpValue.getDimension(resources.getDisplayMetrics());
}
}
}
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return 0;
}
- @Override
- public int getDimensionPixelOffset(int id) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static int getDimensionPixelOffset(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
ResourceValue resValue = value.getSecond();
@@ -518,26 +528,27 @@
if (resValue != null) {
String v = resValue.getValue();
if (v != null) {
+ TypedValue tmpValue = new TypedValue();
if (ResourceHelper.parseFloatAttribute(
- value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
- mTmpValue.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelOffset(mTmpValue.data,
- getDisplayMetrics());
+ value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+ tmpValue.type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelOffset(tmpValue.data,
+ resources.getDisplayMetrics());
}
}
}
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return 0;
}
- @Override
- public int getDimensionPixelSize(int id) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static int getDimensionPixelSize(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
ResourceValue resValue = value.getSecond();
@@ -546,26 +557,27 @@
if (resValue != null) {
String v = resValue.getValue();
if (v != null) {
+ TypedValue tmpValue = new TypedValue();
if (ResourceHelper.parseFloatAttribute(
- value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
- mTmpValue.type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(mTmpValue.data,
- getDisplayMetrics());
+ value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+ tmpValue.type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelSize(tmpValue.data,
+ resources.getDisplayMetrics());
}
}
}
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return 0;
}
- @Override
- public int getInteger(int id) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static int getInteger(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
ResourceValue resValue = value.getSecond();
@@ -584,15 +596,15 @@
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return 0;
}
- @Override
- public boolean getBoolean(int id) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static boolean getBoolean(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
ResourceValue resValue = value.getSecond();
@@ -606,61 +618,62 @@
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return false;
}
- @Override
- public String getResourceEntryName(int resid) throws NotFoundException {
+ @LayoutlibDelegate
+ static String getResourceEntryName(Resources resources, int resid) throws NotFoundException {
throw new UnsupportedOperationException();
}
- @Override
- public String getResourceName(int resid) throws NotFoundException {
+ @LayoutlibDelegate
+ static String getResourceName(Resources resources, int resid) throws NotFoundException {
throw new UnsupportedOperationException();
}
- @Override
- public String getResourceTypeName(int resid) throws NotFoundException {
+ @LayoutlibDelegate
+ static String getResourceTypeName(Resources resources, int resid) throws NotFoundException {
throw new UnsupportedOperationException();
}
- @Override
- public String getString(int id, Object... formatArgs) throws NotFoundException {
- String s = getString(id);
+ @LayoutlibDelegate
+ static String getString(Resources resources, int id, Object... formatArgs)
+ throws NotFoundException {
+ String s = getString(resources, id);
if (s != null) {
return String.format(s, formatArgs);
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
- @Override
- public String getString(int id) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static String getString(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null && value.getSecond().getValue() != null) {
return value.getSecond().getValue();
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
- @Override
- public void getValue(int id, TypedValue outValue, boolean resolveRefs)
+ @LayoutlibDelegate
+ static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
ResourceValue resVal = value.getSecond();
@@ -673,7 +686,7 @@
}
if (resVal instanceof DensityBasedResourceValue) {
outValue.density =
- ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue();
+ ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue();
}
// else it's a string
@@ -684,18 +697,18 @@
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
}
- @Override
- public void getValue(String name, TypedValue outValue, boolean resolveRefs)
+ @LayoutlibDelegate
+ static void getValue(Resources resources, String name, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
throw new UnsupportedOperationException();
}
- @Override
- public XmlResourceParser getXml(int id) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static XmlResourceParser getXml(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
String v = value.getSecond().getValue();
@@ -707,7 +720,8 @@
try {
XmlPullParser parser = ParserFactory.create(f);
- return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+ return new BridgeXmlBlockParser(parser, resources.mContext,
+ mPlatformResourceFlag[0]);
} catch (XmlPullParserException e) {
NotFoundException newE = new NotFoundException();
newE.initCause(e);
@@ -722,25 +736,31 @@
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
- @Override
- public XmlResourceParser loadXmlResourceParser(String file, int id,
+ @LayoutlibDelegate
+ static XmlResourceParser loadXmlResourceParser(Resources resources, int id,
+ String type) throws NotFoundException {
+ return resources.loadXmlResourceParser_Original(id, type);
+ }
+
+ @LayoutlibDelegate
+ static XmlResourceParser loadXmlResourceParser(Resources resources, String file, int id,
int assetCookie, String type) throws NotFoundException {
// even though we know the XML file to load directly, we still need to resolve the
// id so that we can know if it's a platform or project resource.
// (mPlatformResouceFlag will get the result and will be used later).
- getResourceValue(id, mPlatformResourceFlag);
+ getResourceValue(resources, id, mPlatformResourceFlag);
File f = new File(file);
try {
XmlPullParser parser = ParserFactory.create(f);
- return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+ return new BridgeXmlBlockParser(parser, resources.mContext, mPlatformResourceFlag[0]);
} catch (XmlPullParserException e) {
NotFoundException newE = new NotFoundException();
newE.initCause(e);
@@ -752,9 +772,9 @@
}
}
- @Override
- public InputStream openRawResource(int id) throws NotFoundException {
- Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+ @LayoutlibDelegate
+ static InputStream openRawResource(Resources resources, int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
if (value != null) {
String path = value.getSecond().getValue();
@@ -781,15 +801,16 @@
}
// id was not found or not resolved. Throw a NotFoundException.
- throwException(id);
+ throwException(resources, id);
// this is not used since the method above always throws
return null;
}
- @Override
- public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
- getValue(id, value, true);
+ @LayoutlibDelegate
+ static InputStream openRawResource(Resources resources, int id, TypedValue value) throws
+ NotFoundException {
+ getValue(resources, id, value, true);
String path = value.string.toString();
@@ -813,23 +834,27 @@
throw new NotFoundException();
}
- @Override
- public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
+ @LayoutlibDelegate
+ static AssetFileDescriptor openRawResourceFd(Resources resources, int id) throws
+ NotFoundException {
throw new UnsupportedOperationException();
}
/**
- * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource type.
+ * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource
+ * type.
+ *
* @param id the id of the resource
+ *
* @throws NotFoundException
*/
- private void throwException(int id) throws NotFoundException {
+ private static void throwException(Resources resources, int id) throws NotFoundException {
// first get the String related to this id in the framework
Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
// if the name is unknown in the framework, get it from the custom view loader.
- if (resourceInfo == null && mLayoutlibCallback != null) {
- resourceInfo = mLayoutlibCallback.resolveResourceId(id);
+ if (resourceInfo == null && resources.mLayoutlibCallback != null) {
+ resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id);
}
String message;
@@ -845,7 +870,7 @@
throw new NotFoundException(message);
}
- private int getInt(String v) throws NumberFormatException {
+ private static int getInt(String v) throws NumberFormatException {
int radix = 10;
if (v.startsWith("0x")) {
v = v.substring(2);
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 8d5863b..8bd2a7a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -23,7 +23,7 @@
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.annotation.Nullable;
-import android.content.res.BridgeResources.NinePatchInputStream;
+import com.android.layoutlib.bridge.util.NinePatchInputStream;
import android.graphics.BitmapFactory.Options;
import android.graphics.Bitmap_Delegate.BitmapCreateFlags;
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index e3bb3e3..6d8ecd7 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -67,7 +67,7 @@
// ---- delegate manager ----
private static final DelegateManager<Bitmap_Delegate> sManager =
- new DelegateManager<Bitmap_Delegate>(Bitmap_Delegate.class);
+ new DelegateManager<>(Bitmap_Delegate.class);
private static long sFinalizer = -1;
// ---- delegate helper data ----
@@ -314,7 +314,7 @@
@LayoutlibDelegate
/*package*/ static boolean nativeRecycle(long nativeBitmap) {
- sManager.removeJavaReferenceFor(nativeBitmap);
+ // In our case reycle() is a no-op. We will let the finalizer to dispose the bitmap.
return true;
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 839c182..33296e1 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -227,6 +227,10 @@
mColorFilter = ColorFilter_Delegate.getDelegate(colorFilterPtr);
}
+ public void setShader(long shaderPtr) {
+ mShader = Shader_Delegate.getDelegate(shaderPtr);
+ }
+
/**
* Returns the {@link Shader} delegate or null if none have been set
*
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
new file mode 100644
index 0000000..200fe3b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
@@ -0,0 +1,284 @@
+/*
+ * 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.graphics.drawable;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper_Delegate;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
+import android.graphics.drawable.VectorDrawable_Delegate.VFullPath_Delegate;
+import android.graphics.drawable.VectorDrawable_Delegate.VGroup_Delegate;
+import android.graphics.drawable.VectorDrawable_Delegate.VNativeObject;
+import android.graphics.drawable.VectorDrawable_Delegate.VPathRenderer_Delegate;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link
+ * AnimatedVectorDrawable}
+ * <p>
+ * Through the layoutlib_create tool, the original methods of AnimatedVectorDrawable have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+@SuppressWarnings("unused")
+public class AnimatedVectorDrawable_Delegate {
+ private static DelegateManager<AnimatorSetHolder> sAnimatorSets = new
+ DelegateManager<>(AnimatorSetHolder.class);
+ private static DelegateManager<PropertySetter> sHolders = new
+ DelegateManager<>(PropertySetter.class);
+
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreateAnimatorSet() {
+ return sAnimatorSets.addNewDelegate(new AnimatorSetHolder());
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nAddAnimator(long setPtr, long propertyValuesHolder,
+ long nativeInterpolator, long startDelay, long duration, int repeatCount) {
+ PropertySetter holder = sHolders.getDelegate(propertyValuesHolder);
+ if (holder == null || holder.getValues() == null) {
+ return;
+ }
+
+ ObjectAnimator animator = new ObjectAnimator();
+ animator.setValues(holder.getValues());
+ animator.setInterpolator(
+ NativeInterpolatorFactoryHelper_Delegate.getDelegate(nativeInterpolator));
+ animator.setStartDelay(startDelay);
+ animator.setDuration(duration);
+ animator.setRepeatCount(repeatCount);
+ animator.setTarget(holder);
+ animator.setPropertyName(holder.getValues().getPropertyName());
+
+ AnimatorSetHolder set = sAnimatorSets.getDelegate(setPtr);
+ assert set != null;
+ set.addAnimator(animator);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
+ float startValue, float endValue) {
+ VGroup_Delegate group = VNativeObject.getDelegate(nativePtr);
+ Consumer<Float> setter = group.getPropertySetter(propertyId);
+
+ return sHolders.addNewDelegate(FloatPropertySetter.of(setter, startValue,
+ endValue));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
+ long endValuePtr) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "AnimatedVectorDrawable path " +
+ "animations are not supported.", null, null);
+ return 0;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
+ int startValue, int endValue) {
+ VFullPath_Delegate path = VNativeObject.getDelegate(nativePtr);
+ Consumer<Integer> setter = path.getIntPropertySetter(propertyId);
+
+ return sHolders.addNewDelegate(IntPropertySetter.of(setter, startValue,
+ endValue));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreatePathPropertyHolder(long nativePtr, int propertyId,
+ float startValue, float endValue) {
+ VFullPath_Delegate path = VNativeObject.getDelegate(nativePtr);
+ Consumer<Float> setter = path.getFloatPropertySetter(propertyId);
+
+ return sHolders.addNewDelegate(FloatPropertySetter.of(setter, startValue,
+ endValue));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
+ float endValue) {
+ VPathRenderer_Delegate renderer = VNativeObject.getDelegate(nativePtr);
+
+ return sHolders.addNewDelegate(FloatPropertySetter.of(renderer::setRootAlpha,
+ startValue,
+ endValue));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nSetPropertyHolderData(long nativePtr, float[] data, int length) {
+ PropertySetter setter = sHolders.getDelegate(nativePtr);
+ assert setter != null;
+
+ setter.setValues(data);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
+ AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
+ assert animatorSet != null;
+
+ animatorSet.start();
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
+ AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
+ assert animatorSet != null;
+
+ animatorSet.reverse();
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nEnd(long animatorSetPtr) {
+ AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
+ assert animatorSet != null;
+
+ animatorSet.end();
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nReset(long animatorSetPtr) {
+ AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
+ assert animatorSet != null;
+
+ animatorSet.end();
+ animatorSet.start();
+ }
+
+ private static class AnimatorSetHolder {
+ private ArrayList<Animator> mAnimators = new ArrayList<>();
+ private AnimatorSet mAnimatorSet = null;
+
+ private void addAnimator(@NonNull Animator animator) {
+ mAnimators.add(animator);
+ }
+
+ private void ensureAnimatorSet() {
+ if (mAnimatorSet == null) {
+ mAnimatorSet = new AnimatorSet();
+ mAnimatorSet.playTogether(mAnimators);
+ }
+ }
+
+ private void start() {
+ ensureAnimatorSet();
+
+ mAnimatorSet.start();
+ }
+
+ private void end() {
+ mAnimatorSet.end();
+ }
+
+ private void reset() {
+ end();
+ start();
+ }
+
+ private void reverse() {
+ mAnimatorSet.reverse();
+ }
+ }
+
+ /**
+ * Class that allows setting a value and holds the range of values for the given property.
+ *
+ * @param <T> the type of the property
+ */
+ private static class PropertySetter<T> {
+ final Consumer<T> mValueSetter;
+ private PropertyValuesHolder mValues;
+
+ private PropertySetter(@NonNull Consumer<T> valueSetter) {
+ mValueSetter = valueSetter;
+ }
+
+ /**
+ * Method to set an {@link Integer} value for this property. The default implementation of
+ * this method doesn't do anything. This method is accessed via reflection by the
+ * PropertyValuesHolder.
+ */
+ public void setIntValue(Integer value) {
+ }
+
+ /**
+ * Method to set an {@link Integer} value for this property. The default implementation of
+ * this method doesn't do anything. This method is accessed via reflection by the
+ * PropertyValuesHolder.
+ */
+ public void setFloatValue(Float value) {
+ }
+
+ void setValues(float... values) {
+ mValues = PropertyValuesHolder.ofFloat("floatValue", values);
+ }
+
+ @Nullable
+ PropertyValuesHolder getValues() {
+ return mValues;
+ }
+
+ void setValues(int... values) {
+ mValues = PropertyValuesHolder.ofInt("intValue", values);
+ }
+ }
+
+ private static class IntPropertySetter extends PropertySetter<Integer> {
+ private IntPropertySetter(Consumer<Integer> valueSetter) {
+ super(valueSetter);
+ }
+
+ private static PropertySetter of(Consumer<Integer> valueSetter, int... values) {
+ PropertySetter setter = new IntPropertySetter(valueSetter);
+ setter.setValues(values);
+
+ return setter;
+ }
+
+ public void setIntValue(Integer value) {
+ mValueSetter.accept(value);
+ }
+ }
+
+ private static class FloatPropertySetter extends PropertySetter<Float> {
+ private FloatPropertySetter(Consumer<Float> valueSetter) {
+ super(valueSetter);
+ }
+
+ private static PropertySetter of(Consumer<Float> valueSetter, float... values) {
+ PropertySetter setter = new FloatPropertySetter(valueSetter);
+ setter.setValues(values);
+
+ return setter;
+ }
+
+ public void setFloatValue(Float value) {
+ mValueSetter.accept(value);
+ }
+
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
new file mode 100644
index 0000000..3d78931
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
@@ -0,0 +1,28 @@
+/*
+ * 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.graphics.drawable;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
+
+public class AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate {
+ @LayoutlibDelegate
+ /*package*/ static boolean useLastSeenTarget(VectorDrawableAnimatorRT thisDrawableAnimator) {
+ return true;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index eef8235..90b84f8 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -19,6 +19,7 @@
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.annotation.NonNull;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
@@ -44,6 +45,7 @@
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
+import java.util.function.Consumer;
import static android.graphics.Canvas.CLIP_SAVE_FLAG;
import static android.graphics.Canvas.MATRIX_SAVE_FLAG;
@@ -68,14 +70,6 @@
private static final DelegateManager<VNativeObject> sPathManager =
new DelegateManager<>(VNativeObject.class);
- private static <T> T getDelegate(long nativePtr) {
- //noinspection unchecked
- T object = (T) sPathManager.getDelegate(nativePtr);
- assert object != null;
-
- return object;
- }
-
/**
* Obtains styled attributes from the theme, if available, or unstyled resources if the theme is
* null.
@@ -97,21 +91,21 @@
@LayoutlibDelegate
static long nCreateRenderer(long rootGroupPtr) {
- VGroup_Delegate rootGroup = getDelegate(rootGroupPtr);
+ VGroup_Delegate rootGroup = VNativeObject.getDelegate(rootGroupPtr);
return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rootGroup));
}
@LayoutlibDelegate
static void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
float viewportHeight) {
- VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr);
+ VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
nativePathRenderer.mViewportWidth = viewportWidth;
nativePathRenderer.mViewportHeight = viewportHeight;
}
@LayoutlibDelegate
static boolean nSetRootAlpha(long rendererPtr, float alpha) {
- VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr);
+ VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
nativePathRenderer.setRootAlpha(alpha);
return true;
@@ -119,7 +113,7 @@
@LayoutlibDelegate
static float nGetRootAlpha(long rendererPtr) {
- VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr);
+ VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
return nativePathRenderer.getRootAlpha();
}
@@ -132,8 +126,7 @@
@LayoutlibDelegate
static void nDraw(long rendererPtr, long canvasWrapperPtr,
long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache) {
- VPathRenderer_Delegate nativePathRenderer =
- getDelegate(rendererPtr);
+ VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
Canvas_Delegate.native_save(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
Canvas_Delegate.native_translate(canvasWrapperPtr, bounds.left, bounds.top);
@@ -159,7 +152,7 @@
@LayoutlibDelegate
static long nCreateFullPath(long nativeFullPathPtr) {
- VFullPath_Delegate original = getDelegate(nativeFullPathPtr);
+ VFullPath_Delegate original = VNativeObject.getDelegate(nativeFullPathPtr);
return sPathManager.addNewDelegate(new VFullPath_Delegate(original));
}
@@ -167,7 +160,7 @@
@LayoutlibDelegate
static boolean nGetFullPathProperties(long pathPtr, byte[] propertiesData,
int length) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
ByteBuffer properties = ByteBuffer.wrap(propertiesData);
properties.order(ByteOrder.nativeOrder());
@@ -194,7 +187,7 @@
int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart,
float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap,
int strokeLineJoin) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setStrokeWidth(strokeWidth);
path.setStrokeColor(strokeColor);
@@ -211,12 +204,16 @@
@LayoutlibDelegate
static void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr) {
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
+ path.setFillGradient(fillGradientPtr);
}
@LayoutlibDelegate
static void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr) {
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
+ path.setStrokeGradient(strokeGradientPtr);
}
@LayoutlibDelegate
@@ -226,7 +223,7 @@
@LayoutlibDelegate
static long nCreateClipPath(long clipPathPtr) {
- VClipPath_Delegate original = getDelegate(clipPathPtr);
+ VClipPath_Delegate original = VNativeObject.getDelegate(clipPathPtr);
return sPathManager.addNewDelegate(new VClipPath_Delegate(original));
}
@@ -237,21 +234,21 @@
@LayoutlibDelegate
static long nCreateGroup(long groupPtr) {
- VGroup_Delegate original = getDelegate(groupPtr);
+ VGroup_Delegate original = VNativeObject.getDelegate(groupPtr);
return sPathManager.addNewDelegate(
new VGroup_Delegate(original, new ArrayMap<String, Object>()));
}
@LayoutlibDelegate
static void nSetName(long nodePtr, String name) {
- VNativeObject group = getDelegate(nodePtr);
+ VNativeObject group = VNativeObject.getDelegate(nodePtr);
group.setName(name);
}
@LayoutlibDelegate
static boolean nGetGroupProperties(long groupPtr, float[] propertiesData,
int length) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
FloatBuffer properties = FloatBuffer.wrap(propertiesData);
@@ -268,7 +265,7 @@
@LayoutlibDelegate
static void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX,
float pivotY, float scaleX, float scaleY, float translateX, float translateY) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
group.setRotation(rotate);
group.setPivotX(pivotX);
@@ -281,13 +278,13 @@
@LayoutlibDelegate
static void nAddChild(long groupPtr, long nodePtr) {
- VGroup_Delegate group = getDelegate(groupPtr);
- group.mChildren.add(getDelegate(nodePtr));
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
+ group.mChildren.add(VNativeObject.getDelegate(nodePtr));
}
@LayoutlibDelegate
static void nSetPathString(long pathPtr, String pathString, int length) {
- VPath_Delegate path = getDelegate(pathPtr);
+ VPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setPathData(PathParser_Delegate.createNodesFromPathData(pathString));
}
@@ -300,187 +297,187 @@
// Setters and getters during animation.
@LayoutlibDelegate
static float nGetRotation(long groupPtr) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
return group.getRotation();
}
@LayoutlibDelegate
static void nSetRotation(long groupPtr, float rotation) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
group.setRotation(rotation);
}
@LayoutlibDelegate
static float nGetPivotX(long groupPtr) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
return group.getPivotX();
}
@LayoutlibDelegate
static void nSetPivotX(long groupPtr, float pivotX) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
group.setPivotX(pivotX);
}
@LayoutlibDelegate
static float nGetPivotY(long groupPtr) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
return group.getPivotY();
}
@LayoutlibDelegate
static void nSetPivotY(long groupPtr, float pivotY) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
group.setPivotY(pivotY);
}
@LayoutlibDelegate
static float nGetScaleX(long groupPtr) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
return group.getScaleX();
}
@LayoutlibDelegate
static void nSetScaleX(long groupPtr, float scaleX) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
group.setScaleX(scaleX);
}
@LayoutlibDelegate
static float nGetScaleY(long groupPtr) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
return group.getScaleY();
}
@LayoutlibDelegate
static void nSetScaleY(long groupPtr, float scaleY) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
group.setScaleY(scaleY);
}
@LayoutlibDelegate
static float nGetTranslateX(long groupPtr) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
return group.getTranslateX();
}
@LayoutlibDelegate
static void nSetTranslateX(long groupPtr, float translateX) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
group.setTranslateX(translateX);
}
@LayoutlibDelegate
static float nGetTranslateY(long groupPtr) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
return group.getTranslateY();
}
@LayoutlibDelegate
static void nSetTranslateY(long groupPtr, float translateY) {
- VGroup_Delegate group = getDelegate(groupPtr);
+ VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
group.setTranslateY(translateY);
}
@LayoutlibDelegate
static void nSetPathData(long pathPtr, long pathDataPtr) {
- VPath_Delegate path = getDelegate(pathPtr);
+ VPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setPathData(PathParser_Delegate.getDelegate(pathDataPtr).getPathDataNodes());
}
@LayoutlibDelegate
static float nGetStrokeWidth(long pathPtr) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
return path.getStrokeWidth();
}
@LayoutlibDelegate
static void nSetStrokeWidth(long pathPtr, float width) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setStrokeWidth(width);
}
@LayoutlibDelegate
static int nGetStrokeColor(long pathPtr) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
return path.getStrokeColor();
}
@LayoutlibDelegate
static void nSetStrokeColor(long pathPtr, int strokeColor) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setStrokeColor(strokeColor);
}
@LayoutlibDelegate
static float nGetStrokeAlpha(long pathPtr) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
return path.getStrokeAlpha();
}
@LayoutlibDelegate
static void nSetStrokeAlpha(long pathPtr, float alpha) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setStrokeAlpha(alpha);
}
@LayoutlibDelegate
static int nGetFillColor(long pathPtr) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
return path.getFillColor();
}
@LayoutlibDelegate
static void nSetFillColor(long pathPtr, int fillColor) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setFillColor(fillColor);
}
@LayoutlibDelegate
static float nGetFillAlpha(long pathPtr) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
return path.getFillAlpha();
}
@LayoutlibDelegate
static void nSetFillAlpha(long pathPtr, float fillAlpha) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setFillAlpha(fillAlpha);
}
@LayoutlibDelegate
static float nGetTrimPathStart(long pathPtr) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
return path.getTrimPathStart();
}
@LayoutlibDelegate
static void nSetTrimPathStart(long pathPtr, float trimPathStart) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setTrimPathStart(trimPathStart);
}
@LayoutlibDelegate
static float nGetTrimPathEnd(long pathPtr) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
return path.getTrimPathEnd();
}
@LayoutlibDelegate
static void nSetTrimPathEnd(long pathPtr, float trimPathEnd) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setTrimPathEnd(trimPathEnd);
}
@LayoutlibDelegate
static float nGetTrimPathOffset(long pathPtr) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
return path.getTrimPathOffset();
}
@LayoutlibDelegate
static void nSetTrimPathOffset(long pathPtr, float trimPathOffset) {
- VFullPath_Delegate path = getDelegate(pathPtr);
+ VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
path.setTrimPathOffset(trimPathOffset);
}
@@ -492,7 +489,16 @@
* not need it
* </ol>
*/
- private interface VNativeObject {
+ interface VNativeObject {
+ @NonNull
+ static <T> T getDelegate(long nativePtr) {
+ //noinspection unchecked
+ T vNativeObject = (T) sPathManager.getDelegate(nativePtr);
+
+ assert vNativeObject != null;
+ return vNativeObject;
+ }
+
void setName(String name);
}
@@ -511,7 +517,7 @@
}
}
- private static class VFullPath_Delegate extends VPath_Delegate {
+ static class VFullPath_Delegate extends VPath_Delegate {
// These constants need to be kept in sync with their values in VectorDrawable.VFullPath
private static final int STROKE_WIDTH_INDEX = 0;
private static final int STROKE_COLOR_INDEX = 1;
@@ -533,6 +539,38 @@
private static final int LINEJOIN_ROUND = 1;
private static final int LINEJOIN_BEVEL = 2;
+ @NonNull
+ public Consumer<Float> getFloatPropertySetter(int propertyIdx) {
+ switch (propertyIdx) {
+ case STROKE_ALPHA_INDEX:
+ return this::setStrokeAlpha;
+ case FILL_ALPHA_INDEX:
+ return this::setFillAlpha;
+ case TRIM_PATH_START_INDEX:
+ return this::setTrimPathStart;
+ case TRIM_PATH_END_INDEX:
+ return this::setTrimPathEnd;
+ case TRIM_PATH_OFFSET_INDEX:
+ return this::setTrimPathOffset;
+ }
+
+ throw new IllegalArgumentException("Invalid VFullPath_Delegate property index "
+ + propertyIdx);
+ }
+
+ @NonNull
+ public Consumer<Integer> getIntPropertySetter(int propertyIdx) {
+ switch (propertyIdx) {
+ case STROKE_COLOR_INDEX:
+ return this::setStrokeColor;
+ case FILL_COLOR_INDEX:
+ return this::setFillColor;
+ }
+
+ throw new IllegalArgumentException("Invalid VFullPath_Delegate property index "
+ + propertyIdx);
+ }
+
/////////////////////////////////////////////////////
// Variables below need to be copied (deep copy if applicable) for mutation.
@@ -540,6 +578,8 @@
float mStrokeWidth = 0;
int mFillColor = Color.TRANSPARENT;
+ long mStrokeGradient = 0;
+ long mFillGradient = 0;
float mStrokeAlpha = 1.0f;
float mFillAlpha = 1.0f;
float mTrimPathStart = 0;
@@ -569,6 +609,9 @@
mStrokeLineCap = copy.mStrokeLineCap;
mStrokeLineJoin = copy.mStrokeLineJoin;
mStrokeMiterlimit = copy.mStrokeMiterlimit;
+
+ mStrokeGradient = copy.mStrokeGradient;
+ mFillGradient = copy.mFillGradient;
}
private int getStrokeLineCap() {
@@ -637,7 +680,7 @@
return mStrokeColor;
}
- private void setStrokeColor(int strokeColor) {
+ private void setStrokeColor(int strokeColor) {
mStrokeColor = strokeColor;
}
@@ -704,9 +747,17 @@
private float getStrokeMiterlimit() {
return mStrokeMiterlimit;
}
+
+ private void setStrokeGradient(long gradientPtr) {
+ mStrokeGradient = gradientPtr;
+ }
+
+ private void setFillGradient(long gradientPtr) {
+ mFillGradient = gradientPtr;
+ }
}
- private static class VGroup_Delegate implements VNativeObject {
+ static class VGroup_Delegate implements VNativeObject {
// This constants need to be kept in sync with their definitions in VectorDrawable.Group
private static final int ROTATE_INDEX = 0;
private static final int PIVOT_X_INDEX = 1;
@@ -716,6 +767,28 @@
private static final int TRANSLATE_X_INDEX = 5;
private static final int TRANSLATE_Y_INDEX = 6;
+ public Consumer<Float> getPropertySetter(int propertyIdx) {
+ switch (propertyIdx) {
+ case ROTATE_INDEX:
+ return this::setRotation;
+ case PIVOT_X_INDEX:
+ return this::setPivotX;
+ case PIVOT_Y_INDEX:
+ return this::setPivotY;
+ case SCALE_X_INDEX:
+ return this::setScaleX;
+ case SCALE_Y_INDEX:
+ return this::setScaleY;
+ case TRANSLATE_X_INDEX:
+ return this::setTranslateX;
+ case TRANSLATE_Y_INDEX:
+ return this::setTranslateY;
+ }
+
+ throw new IllegalArgumentException("Invalid VGroup_Delegate property index "
+ + propertyIdx);
+ }
+
/////////////////////////////////////////////////////
// Variables below need to be copied (deep copy if applicable) for mutation.
final ArrayList<Object> mChildren = new ArrayList<>();
@@ -914,7 +987,7 @@
}
}
- private static class VPathRenderer_Delegate implements VNativeObject {
+ static class VPathRenderer_Delegate implements VNativeObject {
/* Right now the internal data structure is organized as a tree.
* Each node can be a group node, or a path.
* A group node can have groups or paths as children, but a path node has
@@ -950,7 +1023,7 @@
return mRootAlpha;
}
- private void setRootAlpha(float alpha) {
+ void setRootAlpha(float alpha) {
mRootAlpha = alpha;
}
@@ -1046,11 +1119,11 @@
final Paint fillPaint = mFillPaint;
fillPaint.setColor(applyAlpha(fullPath.mFillColor, fullPath.mFillAlpha));
Paint_Delegate fillPaintDelegate = Paint_Delegate.getDelegate(fillPaint
- .getNativeInstance
- ());
+ .getNativeInstance());
// mFillPaint can not be null at this point so we will have a delegate
assert fillPaintDelegate != null;
fillPaintDelegate.setColorFilter(filterPtr);
+ fillPaintDelegate.setShader(fullPath.mFillGradient);
Canvas_Delegate.native_drawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
.getNativeInstance());
}
@@ -1080,6 +1153,7 @@
strokePaintDelegate.setColorFilter(filterPtr);
final float finalStrokeScale = minScale * matrixScale;
strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
+ strokePaintDelegate.setShader(fullPath.mStrokeGradient);
Canvas_Delegate.native_drawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
.getNativeInstance());
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index d2103c8..7f41348 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -349,7 +349,7 @@
}
@Override
- public void notifyAppStopped(IBinder token) throws RemoteException {
+ public void notifyAppStopped(IBinder token, boolean stopped) throws RemoteException {
// TODO Auto-generated method stub
}
@@ -492,8 +492,7 @@
}
@Override
- public void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade) throws RemoteException {
+ public void keyguardGoingAway(int flags) throws RemoteException {
}
@Override
@@ -561,6 +560,10 @@
}
@Override
+ public void setDockedStackDividerTouchRegion(Rect touchableRegion) throws RemoteException {
+ }
+
+ @Override
public void requestAppKeyboardShortcuts(IResultReceiver receiver) throws RemoteException {
}
diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
new file mode 100644
index 0000000..0f39e80
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
@@ -0,0 +1,128 @@
+/*
+ * 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.view.animation;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.util.MathUtils;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnticipateInterpolator;
+import android.view.animation.AnticipateOvershootInterpolator;
+import android.view.animation.BaseInterpolator;
+import android.view.animation.BounceInterpolator;
+import android.view.animation.CycleInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.OvershootInterpolator;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link
+ * NativeInterpolatorFactoryHelper}
+ * <p>
+ * Through the layoutlib_create tool, the original methods of NativeInterpolatorFactoryHelper have
+ * been replaced by calls to methods of the same name in this delegate class.
+ */
+@SuppressWarnings("unused")
+public class NativeInterpolatorFactoryHelper_Delegate {
+ private static final DelegateManager<Interpolator> sManager = new DelegateManager<>
+ (Interpolator.class);
+
+ public static Interpolator getDelegate(long nativePtr) {
+ return sManager.getDelegate(nativePtr);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createAccelerateDecelerateInterpolator() {
+ return sManager.addNewDelegate(new AccelerateDecelerateInterpolator());
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createAccelerateInterpolator(float factor) {
+ return sManager.addNewDelegate(new AccelerateInterpolator(factor));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createAnticipateInterpolator(float tension) {
+ return sManager.addNewDelegate(new AnticipateInterpolator(tension));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createAnticipateOvershootInterpolator(float tension) {
+ return sManager.addNewDelegate(new AnticipateOvershootInterpolator(tension));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createBounceInterpolator() {
+ return sManager.addNewDelegate(new BounceInterpolator());
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createCycleInterpolator(float cycles) {
+ return sManager.addNewDelegate(new CycleInterpolator(cycles));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createDecelerateInterpolator(float factor) {
+ return sManager.addNewDelegate(new DecelerateInterpolator(factor));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createLinearInterpolator() {
+ return sManager.addNewDelegate(new LinearInterpolator());
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createOvershootInterpolator(float tension) {
+ return sManager.addNewDelegate(new OvershootInterpolator(tension));
+ }
+
+ private static class LutInterpolator extends BaseInterpolator {
+ private final float[] mValues;
+ private final int mSize;
+
+ private LutInterpolator(float[] values) {
+ mValues = values;
+ mSize = mValues.length;
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ float lutpos = input * mSize;
+ if (lutpos >= (mSize - 1)) {
+ return mValues[mSize - 1];
+ }
+
+ int ipart = (int) lutpos;
+ float weight = lutpos - ipart;
+
+ int i1 = ipart;
+ int i2 = Math.min(i1 + 1, mSize - 1);
+
+ assert i1 >= 0 && i2 >= 0 : "Negatives in the interpolation";
+
+ return MathUtils.lerp(mValues[i1], mValues[i2], weight);
+ }
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long createLutInterpolator(float[] values) {
+ return sManager.addNewDelegate(new LutInterpolator(values));
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 17ab2ff5..c7ae6fc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -52,11 +52,11 @@
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.BridgeAssetManager;
-import android.content.res.BridgeResources;
import android.content.res.BridgeTypedArray;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
+import android.content.res.Resources_Delegate;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
@@ -168,8 +168,8 @@
RTL_ATTRS.put("?android:attr/paddingRight", "paddingEnd");
RTL_ATTRS.put("?android:attr/layout_marginLeft", "layout_marginStart");
RTL_ATTRS.put("?android:attr/layout_marginRight", "layout_marginEnd");
- RTL_ATTRS.put("?android:attr/layout_toLeft", "layout_toStartOf");
- RTL_ATTRS.put("?android:attr/layout_toRight", "layout_toEndOf");
+ RTL_ATTRS.put("?android:attr/layout_toLeftOf", "layout_toStartOf");
+ RTL_ATTRS.put("?android:attr/layout_toRightOf", "layout_toEndOf");
RTL_ATTRS.put("?android:attr/layout_alignParentLeft", "layout_alignParentStart");
RTL_ATTRS.put("?android:attr/layout_alignParentRight", "layout_alignParentEnd");
RTL_ATTRS.put("?android:attr/drawableLeft", "drawableStart");
@@ -224,7 +224,7 @@
public void initResources() {
AssetManager assetManager = AssetManager.getSystem();
- mSystemResources = BridgeResources.initSystem(
+ mSystemResources = Resources_Delegate.initSystem(
this,
assetManager,
mMetrics,
@@ -237,7 +237,7 @@
* Disposes the {@link Resources} singleton.
*/
public void disposeResources() {
- BridgeResources.disposeSystem();
+ Resources_Delegate.disposeSystem();
}
public void setBridgeInflater(BridgeInflater inflater) {
@@ -706,8 +706,8 @@
List<Pair<String, Boolean>> attributeList = searchAttrs(attrs);
- BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
- isPlatformFile);
+ BridgeTypedArray ta =
+ Resources_Delegate.newTypeArray(mSystemResources, attrs.length, isPlatformFile);
// look for a custom style.
String customStyle = null;
@@ -941,7 +941,7 @@
List<Pair<String, Boolean>> attributes = searchAttrs(attrs);
- BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
+ BridgeTypedArray ta = Resources_Delegate.newTypeArray(mSystemResources, attrs.length,
false);
// for each attribute, get its name so that we can search it in the style
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index c72eeb1..494b3d2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -33,7 +33,11 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.ColorStateList;
+import android.content.res.ComplexColor;
+import android.content.res.ComplexColor_Accessor;
+import android.content.res.GradientColor;
import android.content.res.Resources.Theme;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
@@ -47,6 +51,7 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@@ -119,54 +124,128 @@
throw new NumberFormatException();
}
- public static ColorStateList getColorStateList(ResourceValue resValue, BridgeContext context) {
+ /**
+ * Returns a {@link ComplexColor} from the given {@link ResourceValue}
+ *
+ * @param resValue the value containing a color value or a file path to a complex color
+ * definition
+ * @param context the current context
+ * @param theme the theme to use when resolving the complex color
+ * @param allowGradients when false, only {@link ColorStateList} will be returned. If a {@link
+ * GradientColor} is found, null will be returned.
+ */
+ @Nullable
+ private static ComplexColor getInternalComplexColor(@NonNull ResourceValue resValue,
+ @NonNull BridgeContext context, @Nullable Theme theme, boolean allowGradients) {
String value = resValue.getValue();
- if (value != null && !RenderResources.REFERENCE_NULL.equals(value)) {
- // first check if the value is a file (xml most likely)
+ if (value == null || RenderResources.REFERENCE_NULL.equals(value)) {
+ return null;
+ }
+
+ // first check if the value is a file (xml most likely)
+ XmlPullParser parser = context.getLayoutlibCallback().getXmlFileParser(value);
+ if (parser == null) {
File f = new File(value);
if (f.isFile()) {
+ // let the framework inflate the color from the XML file, by
+ // providing an XmlPullParser
try {
- // let the framework inflate the ColorStateList from the XML file, by
- // providing an XmlPullParser
- XmlPullParser parser = ParserFactory.create(f);
-
- BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
- parser, context, resValue.isFramework());
- try {
- return ColorStateList.createFromXml(context.getResources(), blockParser);
- } finally {
- blockParser.ensurePopped();
- }
- } catch (XmlPullParserException e) {
- Bridge.getLog().error(LayoutLog.TAG_BROKEN,
- "Failed to configure parser for " + value, e, null /*data*/);
- // we'll return null below.
- } catch (Exception e) {
- // this is an error and not warning since the file existence is
- // checked before attempting to parse it.
+ parser = ParserFactory.create(f);
+ } catch (XmlPullParserException | FileNotFoundException e) {
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
"Failed to parse file " + value, e, null /*data*/);
-
- return null;
- }
- } else {
- // try to load the color state list from an int
- try {
- int color = ResourceHelper.getColor(value);
- return ColorStateList.valueOf(color);
- } catch (NumberFormatException e) {
- Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
- "Failed to convert " + value + " into a ColorStateList", e,
- null /*data*/);
- return null;
}
}
}
+ if (parser != null) {
+ try {
+ BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
+ parser, context, resValue.isFramework());
+ try {
+ // Advance the parser to the first element so we can detect if it's a
+ // color list or a gradient color
+ int type;
+ //noinspection StatementWithEmptyBody
+ while ((type = blockParser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ // Seek parser to start tag.
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ final String name = blockParser.getName();
+ if (allowGradients && "gradient".equals(name)) {
+ return ComplexColor_Accessor.createGradientColorFromXmlInner(
+ context.getResources(),
+ blockParser, blockParser,
+ theme);
+ } else if ("selector".equals(name)) {
+ return ComplexColor_Accessor.createColorStateListFromXmlInner(
+ context.getResources(),
+ blockParser, blockParser,
+ theme);
+ }
+ } finally {
+ blockParser.ensurePopped();
+ }
+ } catch (XmlPullParserException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Failed to configure parser for " + value, e, null /*data*/);
+ // we'll return null below.
+ } catch (Exception e) {
+ // this is an error and not warning since the file existence is
+ // checked before attempting to parse it.
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+ "Failed to parse file " + value, e, null /*data*/);
+
+ return null;
+ }
+ } else {
+ // try to load the color state list from an int
+ try {
+ int color = getColor(value);
+ return ColorStateList.valueOf(color);
+ } catch (NumberFormatException e) {
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
+ "Failed to convert " + value + " into a ColorStateList", e,
+ null /*data*/);
+ }
+ }
+
return null;
}
/**
+ * Returns a {@link ColorStateList} from the given {@link ResourceValue}
+ *
+ * @param resValue the value containing a color value or a file path to a complex color
+ * definition
+ * @param context the current context
+ */
+ @Nullable
+ public static ColorStateList getColorStateList(@NonNull ResourceValue resValue,
+ @NonNull BridgeContext context) {
+ return (ColorStateList) getInternalComplexColor(resValue, context, context.getTheme(),
+ false);
+ }
+
+ /**
+ * Returns a {@link ComplexColor} from the given {@link ResourceValue}
+ *
+ * @param resValue the value containing a color value or a file path to a complex color
+ * definition
+ * @param context the current context
+ */
+ @Nullable
+ public static ComplexColor getComplexColor(@NonNull ResourceValue resValue,
+ @NonNull BridgeContext context) {
+ return getInternalComplexColor(resValue, context, context.getTheme(), true);
+ }
+
+ /**
* Returns a drawable from the given value.
* @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
* or an hexadecimal color
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
new file mode 100644
index 0000000..96b795a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
@@ -0,0 +1,50 @@
+/*
+ * 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.layoutlib.bridge.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+
+/**
+ * Simpler wrapper around FileInputStream. This is used when the input stream represent
+ * not a normal bitmap but a nine patch.
+ * This is useful when the InputStream is created in a method but used in another that needs
+ * to know whether this is 9-patch or not, such as BitmapFactory.
+ */
+public class NinePatchInputStream extends FileInputStream {
+ private boolean mFakeMarkSupport = true;
+ public NinePatchInputStream(File file) throws FileNotFoundException {
+ super(file);
+ }
+
+ @Override
+ public boolean markSupported() {
+ if (mFakeMarkSupport) {
+ // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
+ return true;
+ }
+
+ return super.markSupported();
+ }
+
+ public void disableFakeMarkSupport() {
+ // disable fake mark support so that in case codec actually try to use them
+ // we don't lie to them.
+ mFakeMarkSupport = false;
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
index 72b87ab..47cb042 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/color/gradient.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/color/gradient.xml
new file mode 100644
index 0000000..fc0afa6
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/color/gradient.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.
+ -->
+
+
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:startX="10"
+ android:startY="10"
+ android:endX="50"
+ android:endY="50"
+ android:startColor="#ffff0000"
+ android:endColor="#ff00ff00" />
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml
index ffc70dc..5c19b08 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/multi_path.xml
@@ -53,6 +53,16 @@
android:trimPathStart="0.2"
android:trimPathEnd="0.8"
/>
+
+ <!--
+ Draw a line with gradient stroke color
+ -->
+ <path
+ android:strokeWidth="1"
+ android:strokeColor="#FF00FF"
+ android:fillColor="@color/gradient"
+ android:pathData="M-20,-20 l0, 10 l10, 0 l0, -10 l-10,0 "
+ />
</group>
</vector>
\ No newline at end of file
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 5b99a6b..3b376123 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -367,6 +367,10 @@
ClassVisitor cv = cw;
+ // FIXME Generify
+ if ("android/content/res/Resources".equals(className)) {
+ cv = new FieldInjectorAdapter(cv);
+ }
if (mReplaceMethodCallsClasses.contains(className)) {
cv = new ReplaceMethodCallsAdapter(cv, className);
}
@@ -445,4 +449,5 @@
}
return buffer.toByteArray();
}
+
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 8c3bd2f..bd37665 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -155,6 +155,31 @@
*/
public final static String[] DELEGATE_METHODS = new String[] {
"android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
+ "android.content.res.Resources#getAnimation",
+ "android.content.res.Resources#getBoolean",
+ "android.content.res.Resources#getColor",
+ "android.content.res.Resources#getColorStateList",
+ "android.content.res.Resources#getDimension",
+ "android.content.res.Resources#getDimensionPixelOffset",
+ "android.content.res.Resources#getDimensionPixelSize",
+ "android.content.res.Resources#getDrawable",
+ "android.content.res.Resources#getIntArray",
+ "android.content.res.Resources#getInteger",
+ "android.content.res.Resources#getLayout",
+ "android.content.res.Resources#getResourceEntryName",
+ "android.content.res.Resources#getResourceName",
+ "android.content.res.Resources#getResourceTypeName",
+ "android.content.res.Resources#getString",
+ "android.content.res.Resources#getStringArray",
+ "android.content.res.Resources#getText",
+ "android.content.res.Resources#getTextArray",
+ "android.content.res.Resources#getValue",
+ "android.content.res.Resources#getXml",
+ "android.content.res.Resources#loadXmlResourceParser",
+ "android.content.res.Resources#obtainAttributes",
+ "android.content.res.Resources#obtainTypedArray",
+ "android.content.res.Resources#openRawResource",
+ "android.content.res.Resources#openRawResourceFd",
"android.content.res.Resources$Theme#obtainStyledAttributes",
"android.content.res.Resources$Theme#resolveAttribute",
"android.content.res.Resources$Theme#resolveAttributes",
@@ -164,6 +189,7 @@
"android.content.res.TypedArray#obtain",
"android.graphics.BitmapFactory#finishDecode",
"android.graphics.BitmapFactory#setDensityFromOptions",
+ "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
"android.graphics.drawable.GradientDrawable#buildRing",
"android.graphics.Typeface#getSystemFontConfigLocation",
"android.graphics.Typeface#makeFamilyFromParsed",
@@ -269,6 +295,7 @@
"android.graphics.SweepGradient",
"android.graphics.Typeface",
"android.graphics.Xfermode",
+ "android.graphics.drawable.AnimatedVectorDrawable",
"android.graphics.drawable.VectorDrawable",
"android.os.SystemClock",
"android.os.SystemProperties",
@@ -276,6 +303,7 @@
"android.text.StaticLayout",
"android.util.PathParser",
"android.view.Display",
+ "com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
"libcore.icu.ICU",
};
@@ -313,7 +341,7 @@
// Use android.icu.text versions of DateFormat and SimpleDateFormat since the
// original ones do not match the Android implementation
"java.text.DateFormat", "android.icu.text.DateFormat",
- "java.text.SimpleDateFormat", "android.icu.text.SimpleDateFormat"
+ "java.text.SimpleDateFormat", "android.icu.text.SimpleDateFormat",
};
private final static String[] EXCLUDED_CLASSES =
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java
new file mode 100644
index 0000000..4608a84
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Injects fields in a class.
+ * <p>
+ * TODO: Generify
+ */
+public class FieldInjectorAdapter extends ClassVisitor {
+ public FieldInjectorAdapter(ClassVisitor cv) {
+ super(Opcodes.ASM4, cv);
+ }
+
+ @Override
+ public void visitEnd() {
+ super.visitField(Opcodes.ACC_PUBLIC, "mLayoutlibCallback",
+ "Lcom/android/ide/common/rendering/api/LayoutlibCallback;", null, null);
+ super.visitField(Opcodes.ACC_PUBLIC, "mContext",
+ "Lcom/android/layoutlib/bridge/android/BridgeContext;", null, null);
+ super.visitEnd();
+ }
+}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index a46aaec..a9259fa 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -305,9 +305,12 @@
*/
public static class InformationElement {
public static final int EID_SSID = 0;
+ public static final int EID_SUPPORTED_RATES = 1;
public static final int EID_TIM = 5;
public static final int EID_BSS_LOAD = 11;
+ public static final int EID_ERP = 42;
public static final int EID_RSN = 48;
+ public static final int EID_EXTENDED_SUPPORTED_RATES = 50;
public static final int EID_HT_OPERATION = 61;
public static final int EID_INTERWORKING = 107;
public static final int EID_ROAMING_CONSORTIUM = 111;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index a5bfd3c..823fd26 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -21,7 +21,6 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
import android.net.DhcpInfo;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -666,17 +665,15 @@
private final int mTargetSdkVersion;
private static final int INVALID_KEY = 0;
- private static int sListenerKey = 1;
- private static final SparseArray sListenerMap = new SparseArray();
- private static final Object sListenerMapLock = new Object();
+ private int mListenerKey = 1;
+ private final SparseArray mListenerMap = new SparseArray();
+ private final Object mListenerMapLock = new Object();
- private static AsyncChannel sAsyncChannel;
- private static CountDownLatch sConnected;
- private static ConnectivityManager sCM;
+ private AsyncChannel mAsyncChannel;
+ private CountDownLatch mConnected;
- private static final Object sThreadRefLock = new Object();
- private static int sThreadRefCount;
- private static HandlerThread sHandlerThread;
+ /* TODO(b/27432949): Use a common connectivity thread for this. */
+ private HandlerThread mHandlerThread;
/**
* Create a new WifiManager instance.
@@ -1482,7 +1479,7 @@
*/
public void getTxPacketCount(TxPacketCountListener listener) {
validateChannel();
- sAsyncChannel.sendMessage(RSSI_PKTCNT_FETCH, 0, putListener(listener));
+ mAsyncChannel.sendMessage(RSSI_PKTCNT_FETCH, 0, putListener(listener));
}
/**
@@ -1846,25 +1843,34 @@
public void onFailure(int reason);
}
- private static class ServiceHandler extends Handler {
+ // Ensure that multiple ServiceHandler threads do not interleave message dispatch.
+ private static final Object sServiceHandlerDispatchLock = new Object();
+
+ private class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message message) {
+ synchronized (sServiceHandlerDispatchLock) {
+ dispatchMessageToListeners(message);
+ }
+ }
+
+ private void dispatchMessageToListeners(Message message) {
Object listener = removeListener(message.arg2);
switch (message.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
} else {
Log.e(TAG, "Failed to set up channel connection");
// This will cause all further async API calls on the WifiManager
// to fail and throw an exception
- sAsyncChannel = null;
+ mAsyncChannel = null;
}
- sConnected.countDown();
+ mConnected.countDown();
break;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
// Ignore
@@ -1873,7 +1879,7 @@
Log.e(TAG, "Channel connection lost");
// This will cause all further async API calls on the WifiManager
// to fail and throw an exception
- sAsyncChannel = null;
+ mAsyncChannel = null;
getLooper().quit();
break;
/* ActionListeners grouped together */
@@ -1899,8 +1905,8 @@
WpsResult result = (WpsResult) message.obj;
((WpsCallback) listener).onStarted(result.pin);
//Listener needs to stay until completion or failure
- synchronized(sListenerMapLock) {
- sListenerMap.put(message.arg2, listener);
+ synchronized (mListenerMapLock) {
+ mListenerMap.put(message.arg2, listener);
}
}
break;
@@ -1945,54 +1951,50 @@
}
}
- private static int putListener(Object listener) {
+ private int putListener(Object listener) {
if (listener == null) return INVALID_KEY;
int key;
- synchronized (sListenerMapLock) {
+ synchronized (mListenerMapLock) {
do {
- key = sListenerKey++;
+ key = mListenerKey++;
} while (key == INVALID_KEY);
- sListenerMap.put(key, listener);
+ mListenerMap.put(key, listener);
}
return key;
}
- private static Object removeListener(int key) {
+ private Object removeListener(int key) {
if (key == INVALID_KEY) return null;
- synchronized (sListenerMapLock) {
- Object listener = sListenerMap.get(key);
- sListenerMap.remove(key);
+ synchronized (mListenerMapLock) {
+ Object listener = mListenerMap.get(key);
+ mListenerMap.remove(key);
return listener;
}
}
private void init() {
- synchronized (sThreadRefLock) {
- if (++sThreadRefCount == 1) {
- Messenger messenger = getWifiServiceMessenger();
- if (messenger == null) {
- sAsyncChannel = null;
- return;
- }
+ Messenger messenger = getWifiServiceMessenger();
+ if (messenger == null) {
+ mAsyncChannel = null;
+ return;
+ }
- sHandlerThread = new HandlerThread("WifiManager");
- sAsyncChannel = new AsyncChannel();
- sConnected = new CountDownLatch(1);
+ mHandlerThread = new HandlerThread("WifiManager");
+ mAsyncChannel = new AsyncChannel();
+ mConnected = new CountDownLatch(1);
- sHandlerThread.start();
- Handler handler = new ServiceHandler(sHandlerThread.getLooper());
- sAsyncChannel.connect(mContext, handler, messenger);
- try {
- sConnected.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "interrupted wait at init");
- }
- }
+ mHandlerThread.start();
+ Handler handler = new ServiceHandler(mHandlerThread.getLooper());
+ mAsyncChannel.connect(mContext, handler, messenger);
+ try {
+ mConnected.await();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "interrupted wait at init");
}
}
private void validateChannel() {
- if (sAsyncChannel == null) throw new IllegalStateException(
+ if (mAsyncChannel == null) throw new IllegalStateException(
"No permission to access and change wifi or a bad initialization");
}
@@ -2017,7 +2019,7 @@
validateChannel();
// Use INVALID_NETWORK_ID for arg1 when passing a config object
// arg1 is used to pass network id when the network already exists
- sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
+ mAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
putListener(listener), config);
}
@@ -2037,7 +2039,7 @@
public void connect(int networkId, ActionListener listener) {
if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
validateChannel();
- sAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
+ mAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
}
/**
@@ -2061,7 +2063,7 @@
public void save(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
validateChannel();
- sAsyncChannel.sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
+ mAsyncChannel.sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
}
/**
@@ -2080,7 +2082,7 @@
public void forget(int netId, ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
validateChannel();
- sAsyncChannel.sendMessage(FORGET_NETWORK, netId, putListener(listener));
+ mAsyncChannel.sendMessage(FORGET_NETWORK, netId, putListener(listener));
}
/**
@@ -2095,7 +2097,7 @@
public void disable(int netId, ActionListener listener) {
if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
validateChannel();
- sAsyncChannel.sendMessage(DISABLE_NETWORK, netId, putListener(listener));
+ mAsyncChannel.sendMessage(DISABLE_NETWORK, netId, putListener(listener));
}
/**
@@ -2124,7 +2126,7 @@
public void startWps(WpsInfo config, WpsCallback listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
validateChannel();
- sAsyncChannel.sendMessage(START_WPS, 0, putListener(listener), config);
+ mAsyncChannel.sendMessage(START_WPS, 0, putListener(listener), config);
}
/**
@@ -2136,7 +2138,7 @@
*/
public void cancelWps(WpsCallback listener) {
validateChannel();
- sAsyncChannel.sendMessage(CANCEL_WPS, 0, putListener(listener));
+ mAsyncChannel.sendMessage(CANCEL_WPS, 0, putListener(listener));
}
/**
@@ -2601,10 +2603,8 @@
protected void finalize() throws Throwable {
try {
- synchronized (sThreadRefLock) {
- if (--sThreadRefCount == 0 && sAsyncChannel != null) {
- sAsyncChannel.disconnect();
- }
+ if (mAsyncChannel != null) {
+ mAsyncChannel.disconnect();
}
} finally {
super.finalize();
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 69e179d..c5e7bff 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -169,6 +169,13 @@
public int band;
/** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */
public ChannelSpec[] channels;
+ /**
+ * list of networkId's of hidden networks to scan for.
+ * These Id's should correspond to the wpa_supplicant's networkId's and will be used
+ * in connectivity scans using wpa_supplicant.
+ * {@hide}
+ * */
+ public int[] hiddenNetworkIds;
/** period of background scan; in millisecond, 0 => single shot scan */
public int periodInMs;
/** must have a valid REPORT_EVENT value */
@@ -192,6 +199,11 @@
* for a given period
*/
public int stepCount;
+ /**
+ * Flag to indicate if the scan settings are targeted for PNO scan.
+ * {@hide}
+ */
+ public boolean isPnoScan;
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
@@ -207,10 +219,9 @@
dest.writeInt(maxScansToCache);
dest.writeInt(maxPeriodInMs);
dest.writeInt(stepCount);
-
+ dest.writeInt(isPnoScan ? 1 : 0);
if (channels != null) {
dest.writeInt(channels.length);
-
for (int i = 0; i < channels.length; i++) {
dest.writeInt(channels[i].frequency);
dest.writeInt(channels[i].dwellTimeMS);
@@ -219,13 +230,13 @@
} else {
dest.writeInt(0);
}
+ dest.writeIntArray(hiddenNetworkIds);
}
/** Implement the Parcelable interface {@hide} */
public static final Creator<ScanSettings> CREATOR =
new Creator<ScanSettings>() {
public ScanSettings createFromParcel(Parcel in) {
-
ScanSettings settings = new ScanSettings();
settings.band = in.readInt();
settings.periodInMs = in.readInt();
@@ -234,17 +245,17 @@
settings.maxScansToCache = in.readInt();
settings.maxPeriodInMs = in.readInt();
settings.stepCount = in.readInt();
+ settings.isPnoScan = in.readInt() == 1;
int num_channels = in.readInt();
settings.channels = new ChannelSpec[num_channels];
for (int i = 0; i < num_channels; i++) {
int frequency = in.readInt();
-
ChannelSpec spec = new ChannelSpec(frequency);
spec.dwellTimeMS = in.readInt();
spec.passive = in.readInt() == 1;
settings.channels[i] = spec;
}
-
+ settings.hiddenNetworkIds = in.createIntArray();
return settings;
}
@@ -436,6 +447,158 @@
};
}
+ /** {@hide} */
+ public static final String PNO_PARAMS_PNO_SETTINGS_KEY = "PnoSettings";
+ /** {@hide} */
+ public static final String PNO_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
+ /**
+ * PNO scan configuration parameters to be sent to {@link #startPnoScan}.
+ * Note: This structure needs to be in sync with |wifi_epno_params| struct in gscan HAL API.
+ * {@hide}
+ */
+ public static class PnoSettings implements Parcelable {
+ /**
+ * Pno network to be added to the PNO scan filtering.
+ * {@hide}
+ */
+ public static class PnoNetwork {
+ /*
+ * Pno flags bitmask to be set in {@link #PnoNetwork.flags}
+ */
+ /** Whether directed scan needs to be performed (for hidden SSIDs) */
+ public static final byte FLAG_DIRECTED_SCAN = (1 << 0);
+ /** Whether PNO event shall be triggered if the network is found on A band */
+ public static final byte FLAG_A_BAND = (1 << 1);
+ /** Whether PNO event shall be triggered if the network is found on G band */
+ public static final byte FLAG_G_BAND = (1 << 2);
+ /**
+ * Whether strict matching is required
+ * If required then the firmware must store the network's SSID and not just a hash
+ */
+ public static final byte FLAG_STRICT_MATCH = (1 << 3);
+ /**
+ * If this SSID should be considered the same network as the currently connected
+ * one for scoring.
+ */
+ public static final byte FLAG_SAME_NETWORK = (1 << 4);
+
+ /*
+ * Code for matching the beacon AUTH IE - additional codes. Bitmask to be set in
+ * {@link #PnoNetwork.authBitField}
+ */
+ /** Open Network */
+ public static final byte AUTH_CODE_OPEN = (1 << 0);
+ /** WPA_PSK or WPA2PSK */
+ public static final byte AUTH_CODE_PSK = (1 << 1);
+ /** any EAPOL */
+ public static final byte AUTH_CODE_EAPOL = (1 << 2);
+
+ /** SSID of the network */
+ public String ssid;
+ /** Network ID in wpa_supplicant */
+ public int networkId;
+ /** Assigned priority for the network */
+ public int priority;
+ /** Bitmask of the FLAG_XXX */
+ public byte flags;
+ /** Bitmask of the ATUH_XXX */
+ public byte authBitField;
+
+ /**
+ * default constructor for PnoNetwork
+ */
+ public PnoNetwork(String ssid) {
+ this.ssid = ssid;
+ flags = 0;
+ authBitField = 0;
+ }
+ }
+
+ /** Connected vs Disconnected PNO flag {@hide} */
+ public boolean isConnected;
+ /** Minimum 5GHz RSSI for a BSSID to be considered */
+ public int min5GHzRssi;
+ /** Minimum 2.4GHz RSSI for a BSSID to be considered */
+ public int min24GHzRssi;
+ /** Maximum score that a network can have before bonuses */
+ public int initialScoreMax;
+ /**
+ * Only report when there is a network's score this much higher
+ * than the current connection.
+ */
+ public int currentConnectionBonus;
+ /** score bonus for all networks with the same network flag */
+ public int sameNetworkBonus;
+ /** score bonus for networks that are not open */
+ public int secureBonus;
+ /** 5GHz RSSI score bonus (applied to all 5GHz networks) */
+ public int band5GHzBonus;
+ /** Pno Network filter list */
+ public PnoNetwork[] networkList;
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(isConnected ? 1 : 0);
+ dest.writeInt(min5GHzRssi);
+ dest.writeInt(min24GHzRssi);
+ dest.writeInt(initialScoreMax);
+ dest.writeInt(currentConnectionBonus);
+ dest.writeInt(sameNetworkBonus);
+ dest.writeInt(secureBonus);
+ dest.writeInt(band5GHzBonus);
+ if (networkList != null) {
+ dest.writeInt(networkList.length);
+ for (int i = 0; i < networkList.length; i++) {
+ dest.writeString(networkList[i].ssid);
+ dest.writeInt(networkList[i].networkId);
+ dest.writeInt(networkList[i].priority);
+ dest.writeByte(networkList[i].flags);
+ dest.writeByte(networkList[i].authBitField);
+ }
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<PnoSettings> CREATOR =
+ new Creator<PnoSettings>() {
+ public PnoSettings createFromParcel(Parcel in) {
+ PnoSettings settings = new PnoSettings();
+ settings.isConnected = in.readInt() == 1;
+ settings.min5GHzRssi = in.readInt();
+ settings.min24GHzRssi = in.readInt();
+ settings.initialScoreMax = in.readInt();
+ settings.currentConnectionBonus = in.readInt();
+ settings.sameNetworkBonus = in.readInt();
+ settings.secureBonus = in.readInt();
+ settings.band5GHzBonus = in.readInt();
+ int numNetworks = in.readInt();
+ settings.networkList = new PnoNetwork[numNetworks];
+ for (int i = 0; i < numNetworks; i++) {
+ String ssid = in.readString();
+ PnoNetwork network = new PnoNetwork(ssid);
+ network.networkId = in.readInt();
+ network.priority = in.readInt();
+ network.flags = in.readByte();
+ network.authBitField = in.readByte();
+ settings.networkList[i] = network;
+ }
+ return settings;
+ }
+
+ public PnoSettings[] newArray(int size) {
+ return new PnoSettings[size];
+ }
+ };
+
+ }
+
/**
* interface to get scan events on; specify this on {@link #startBackgroundScan} or
* {@link #startScan}
@@ -456,6 +619,18 @@
public void onFullResult(ScanResult fullScanResult);
}
+ /**
+ * interface to get PNO scan events on; specify this on {@link #startDisconnectedPnoScan} and
+ * {@link #startConnectedPnoScan}.
+ * {@hide}
+ */
+ public interface PnoScanListener extends ScanListener {
+ /**
+ * Invoked when one of the PNO networks are found in scan results.
+ */
+ void onPnoNetworkFound(ScanResult[] results);
+ }
+
/** start wifi scan in background
* @param settings specifies various parameters for the scan; for more information look at
* {@link ScanSettings}
@@ -521,6 +696,75 @@
sAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
}
+ private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) {
+ // Bundle up both the settings and send it across.
+ Bundle pnoParams = new Bundle();
+ if (pnoParams == null) return;
+ // Set the PNO scan flag.
+ scanSettings.isPnoScan = true;
+ pnoParams.putParcelable(PNO_PARAMS_SCAN_SETTINGS_KEY, scanSettings);
+ pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings);
+ sAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
+ }
+ /**
+ * Start wifi connected PNO scan
+ * @param scanSettings specifies various parameters for the scan; for more information look at
+ * {@link ScanSettings}
+ * @param pnoSettings specifies various parameters for PNO; for more information look at
+ * {@link PnoSettings}
+ * @param listener specifies the object to report events to. This object is also treated as a
+ * key for this scan, and must also be specified to cancel the scan. Multiple
+ * scans should also not share this object.
+ * {@hide}
+ */
+ public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
+ PnoScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null");
+ int key = addListener(listener);
+ if (key == INVALID_KEY) return;
+ validateChannel();
+ pnoSettings.isConnected = true;
+ startPnoScan(scanSettings, pnoSettings, key);
+ }
+ /**
+ * Start wifi disconnected PNO scan
+ * @param scanSettings specifies various parameters for the scan; for more information look at
+ * {@link ScanSettings}
+ * @param pnoSettings specifies various parameters for PNO; for more information look at
+ * {@link PnoSettings}
+ * @param listener specifies the object to report events to. This object is also treated as a
+ * key for this scan, and must also be specified to cancel the scan. Multiple
+ * scans should also not share this object.
+ * {@hide}
+ */
+ public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
+ PnoScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null");
+ int key = addListener(listener);
+ if (key == INVALID_KEY) return;
+ validateChannel();
+ pnoSettings.isConnected = false;
+ startPnoScan(scanSettings, pnoSettings, key);
+ }
+ /**
+ * Stop an ongoing wifi PNO scan
+ * @param pnoSettings specifies various parameters for PNO; for more information look at
+ * {@link PnoSettings}
+ * @param listener specifies which scan to cancel; must be same object as passed in {@link
+ * #startPnoScan}
+ * TODO(rpius): Check if we can remove pnoSettings param in stop.
+ * {@hide}
+ */
+ public void stopPnoScan(PnoSettings pnoSettings, ScanListener listener) {
+ Preconditions.checkNotNull(listener, "listener cannot be null");
+ int key = removeListener(listener);
+ if (key == INVALID_KEY) return;
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_STOP_PNO_SCAN, 0, key, pnoSettings);
+ }
+
/** specifies information about an access point of interest */
public static class BssidInfo {
/** bssid of the access point; in XX:XX:XX:XX:XX:XX format */
@@ -824,6 +1068,12 @@
public static final int CMD_STOP_SINGLE_SCAN = BASE + 22;
/** @hide */
public static final int CMD_SINGLE_SCAN_COMPLETED = BASE + 23;
+ /** @hide */
+ public static final int CMD_START_PNO_SCAN = BASE + 24;
+ /** @hide */
+ public static final int CMD_STOP_PNO_SCAN = BASE + 25;
+ /** @hide */
+ public static final int CMD_PNO_NETWORK_FOUND = BASE + 26;
private Context mContext;
private IWifiScanner mService;
@@ -1110,6 +1360,10 @@
if (DBG) Log.d(TAG, "removing listener for single scan");
removeListener(msg.arg2);
break;
+ case CMD_PNO_NETWORK_FOUND:
+ ((PnoScanListener) listener).onPnoNetworkFound(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
default:
if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
return;