Merge "Add Search View UI tests"
diff --git a/Android.mk b/Android.mk
index c1c74ea..960fb3c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -341,10 +341,12 @@
media/java/android/media/IAudioRoutesObserver.aidl \
media/java/android/media/IMediaHTTPConnection.aidl \
media/java/android/media/IMediaHTTPService.aidl \
+ media/java/android/media/IMediaResourceMonitor.aidl \
media/java/android/media/IMediaRouterClient.aidl \
media/java/android/media/IMediaRouterService.aidl \
media/java/android/media/IMediaScannerListener.aidl \
media/java/android/media/IMediaScannerService.aidl \
+ media/java/android/media/IRecordingConfigDispatcher.aidl \
media/java/android/media/IRemoteDisplayCallback.aidl \
media/java/android/media/IRemoteDisplayProvider.aidl \
media/java/android/media/IRemoteVolumeController.aidl \
@@ -407,10 +409,14 @@
telephony/java/com/android/internal/telephony/ISms.aidl \
telephony/java/com/android/internal/telephony/ISub.aidl \
telephony/java/com/android/internal/telephony/ITelephony.aidl \
+ telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl \
telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \
+ wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl \
+ wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \
+ wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
wifi/java/android/net/wifi/IWifiScanner.aidl \
wifi/java/android/net/wifi/IRttManager.aidl \
@@ -421,7 +427,9 @@
core/java/android/service/quicksettings/IQSTileService.aidl \
# FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
-LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
+LOCAL_AIDL_INCLUDES += \
+ $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \
+ frameworks/native/aidl/binder
LOCAL_INTERMEDIATE_SOURCES := \
$(framework_res_source_path)/android/R.java \
@@ -479,6 +487,11 @@
frameworks/base/media/java/android/media/tv/TvTrackInfo.aidl \
frameworks/base/media/java/android/media/browse/MediaBrowser.aidl \
frameworks/base/wifi/java/android/net/wifi/ScanSettings.aidl \
+ frameworks/base/wifi/java/android/net/wifi/nan/ConfigRequest.aidl \
+ frameworks/base/wifi/java/android/net/wifi/nan/PublishData.aidl \
+ frameworks/base/wifi/java/android/net/wifi/nan/SubscribeData.aidl \
+ frameworks/base/wifi/java/android/net/wifi/nan/PublishSettings.aidl \
+ frameworks/base/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl \
@@ -521,7 +534,6 @@
frameworks/base/core/java/android/os/ParcelUuid.aidl \
frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
frameworks/base/core/java/android/os/ResultReceiver.aidl \
- frameworks/base/core/java/android/os/PersistableBundle.aidl \
frameworks/base/core/java/android/os/WorkSource.aidl \
frameworks/base/core/java/android/os/DropBoxManager.aidl \
frameworks/base/core/java/android/os/Bundle.aidl \
@@ -629,6 +641,7 @@
frameworks/base/core/java/android/bluetooth/BluetoothDevice.aidl \
frameworks/base/core/java/android/database/CursorWindow.aidl \
frameworks/base/core/java/android/service/quicksettings/Tile.aidl \
+ frameworks/native/aidl/binder/android/os/PersistableBundle.aidl \
gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
$(gen): PRIVATE_SRC_FILES := $(aidl_files)
diff --git a/api/current.txt b/api/current.txt
index b0eb006..e75ab1c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -524,6 +524,7 @@
field public static final int expandableListViewStyle = 16842863; // 0x101006f
field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
field public static final int exported = 16842768; // 0x1010010
+ field public static final int externalService = 16844047; // 0x101050f
field public static final int extraTension = 16843371; // 0x101026b
field public static final int extractNativeLibs = 16844010; // 0x10104ea
field public static final int factor = 16843219; // 0x10101d3
@@ -2750,15 +2751,12 @@
method public android.os.Bundle addAccountFromCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle confirmCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle editProperties(android.accounts.AccountAuthenticatorResponse, java.lang.String);
- method public android.os.Bundle finishSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public android.os.Bundle getAccountCredentialsForCloning(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
method public android.os.Bundle getAccountRemovalAllowed(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle getAuthToken(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract java.lang.String getAuthTokenLabel(java.lang.String);
method public final android.os.IBinder getIBinder();
method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
- method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
- method public android.os.Bundle startUpdateCredentialsSession(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
}
@@ -2796,7 +2794,6 @@
method public void clearPassword(android.accounts.Account);
method public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(java.lang.String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
- method public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public static android.accounts.AccountManager get(android.content.Context);
method public android.accounts.Account[] getAccounts();
method public android.accounts.Account[] getAccountsByType(java.lang.String);
@@ -2824,8 +2821,6 @@
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
method public void setPassword(android.accounts.Account, java.lang.String);
method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
- method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
- method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
@@ -2842,8 +2837,6 @@
field public static final java.lang.String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
field public static final java.lang.String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
field public static final java.lang.String KEY_ACCOUNT_NAME = "authAccount";
- field public static final java.lang.String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
- field public static final java.lang.String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
field public static final java.lang.String KEY_ACCOUNT_TYPE = "accountType";
field public static final java.lang.String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
field public static final java.lang.String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
@@ -3505,6 +3498,7 @@
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public android.net.Uri onProvideReferrer();
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
@@ -4189,6 +4183,7 @@
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPrepareOptionsMenu(android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public void onRestoreInstanceState(android.os.Bundle);
method public android.os.Bundle onSaveInstanceState();
method public boolean onSearchRequested(android.view.SearchEvent);
@@ -5786,6 +5781,7 @@
method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> getActiveAdmins();
+ method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
method public boolean getAutoTimeRequired();
@@ -5793,6 +5789,7 @@
method public boolean getCameraDisabled(android.content.ComponentName);
method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+ method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
method public java.lang.String getDeviceOwnerLockScreenInfo();
@@ -5849,6 +5846,7 @@
method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
method public boolean resetPassword(java.lang.String, int);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
+ method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
@@ -5857,6 +5855,7 @@
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+ method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -5903,6 +5902,7 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
+ field public static final java.lang.String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
@@ -7201,6 +7201,15 @@
field public static final int TYPE_SCO = 2; // 0x2
}
+ public class OobData implements android.os.Parcelable {
+ ctor public OobData();
+ method public int describeContents();
+ method public byte[] getSecurityManagerTk();
+ method public void setSecurityManagerTk(byte[]);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+ }
+
}
package android.bluetooth.le {
@@ -12784,6 +12793,7 @@
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
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 {
@@ -20368,8 +20378,6 @@
field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
- field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
- field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -22556,7 +22564,7 @@
method public int[] getObjectHandles(int, int, int);
method public android.mtp.MtpObjectInfo getObjectInfo(int);
method public long getParent(int);
- method public int getPartialObject(int, int, int, byte[]) throws java.io.IOException;
+ method public long getPartialObject(int, long, long, byte[]) throws java.io.IOException;
method public long getStorageId(int);
method public int[] getStorageIds();
method public android.mtp.MtpStorageInfo getStorageInfo(int);
@@ -22579,7 +22587,16 @@
public class MtpEvent {
ctor public MtpEvent();
+ method public int getDevicePropCode();
method public int getEventCode();
+ method public int getObjectFormatCode();
+ method public int getObjectHandle();
+ method public int getObjectPropCode();
+ method public int getParameter1();
+ method public int getParameter2();
+ method public int getParameter3();
+ method public int getStorageId();
+ method public int getTransactionId();
}
public final class MtpObjectInfo {
@@ -29371,6 +29388,22 @@
field public static final java.lang.String _ID = "_id";
}
+ public class BlockedNumberContract {
+ method public static boolean isBlocked(android.content.Context, java.lang.String);
+ field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
+ field public static final android.net.Uri AUTHORITY_URI;
+ }
+
+ public static class BlockedNumberContract.BlockedNumbers {
+ field public static final java.lang.String COLUMN_E164_NUMBER = "e164_number";
+ field public static final java.lang.String COLUMN_ID = "_id";
+ field public static final java.lang.String COLUMN_ORIGINAL_NUMBER = "original_number";
+ field public static final java.lang.String COLUMN_STRIPPED_NUMBER = "stripped_number";
+ field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_numbers";
+ field public static final android.net.Uri CONTENT_URI;
+ }
+
public class Browser {
ctor public Browser();
method public static final void sendString(android.content.Context, java.lang.String);
@@ -33492,6 +33525,7 @@
method public boolean onMenuOpened(int, android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public boolean onSearchRequested(android.view.SearchEvent);
method public boolean onSearchRequested();
method public void onWakeUp();
@@ -33586,6 +33620,7 @@
method public abstract void onUnsubscribe(android.net.Uri);
field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+ field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
@@ -33713,17 +33748,24 @@
method public java.lang.CharSequence getContentDescription();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getLabel();
+ method public int getState();
method public void setContentDescription(java.lang.CharSequence);
method public void setIcon(android.graphics.drawable.Icon);
method public void setLabel(java.lang.CharSequence);
+ method public void setState(int);
method public void updateTile();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
+ field public static final int STATE_ACTIVE = 2; // 0x2
+ field public static final int STATE_INACTIVE = 1; // 0x1
+ field public static final int STATE_UNAVAILABLE = 0; // 0x0
}
public class TileService extends android.app.Service {
ctor public TileService();
method public final android.service.quicksettings.Tile getQsTile();
+ method public final boolean isLocked();
+ method public final boolean isSecure();
method public android.os.IBinder onBind(android.content.Intent);
method public void onClick();
method public void onStartListening();
@@ -33732,6 +33774,8 @@
method public void onTileRemoved();
method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
method public final void showDialog(android.app.Dialog);
+ method public final void startActivityAndCollapse(android.content.Intent);
+ method public final void unlockAndRun(java.lang.Runnable);
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
field public static final int TILE_MODE_ACTIVE = 2; // 0x2
field public static final int TILE_MODE_PASSIVE = 1; // 0x1
@@ -38196,7 +38240,8 @@
ctor public SuggestionSpan(android.os.Parcel);
method public int describeContents();
method public int getFlags();
- method public java.lang.String getLocale();
+ method public deprecated java.lang.String getLocale();
+ method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
method public void setFlags(int);
@@ -40380,6 +40425,27 @@
method public void startTracking(android.view.KeyEvent, java.lang.Object);
}
+ public final class KeyboardShortcutGroup implements android.os.Parcelable {
+ ctor public KeyboardShortcutGroup(java.lang.CharSequence, java.util.List<android.view.KeyboardShortcutInfo>);
+ ctor public KeyboardShortcutGroup(java.lang.CharSequence);
+ method public void addItem(android.view.KeyboardShortcutInfo);
+ method public int describeContents();
+ method public java.util.List<android.view.KeyboardShortcutInfo> getItems();
+ method public java.lang.CharSequence getLabel();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutGroup> CREATOR;
+ }
+
+ public final class KeyboardShortcutInfo implements android.os.Parcelable {
+ ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int);
+ method public int describeContents();
+ method public char getBaseCharacter();
+ method public java.lang.CharSequence getLabel();
+ method public int getModifiers();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutInfo> CREATOR;
+ }
+
public abstract class LayoutInflater {
ctor protected LayoutInflater(android.content.Context);
ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context);
@@ -42381,6 +42447,7 @@
method public abstract boolean onMenuOpened(int, android.view.Menu);
method public abstract void onPanelClosed(int, android.view.Menu);
method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
+ method public abstract void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public abstract boolean onSearchRequested();
method public abstract boolean onSearchRequested(android.view.SearchEvent);
method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -43581,6 +43648,7 @@
}
public final class InputMethodManager {
+ method public void dispatchKeyEventFromInputMethod(android.view.View, android.view.KeyEvent);
method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
method public android.view.inputmethod.InputMethodSubtype getCurrentInputMethodSubtype();
method public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodList();
@@ -49542,6 +49610,8 @@
public class InternalError extends java.lang.VirtualMachineError {
ctor public InternalError();
ctor public InternalError(java.lang.String);
+ ctor public InternalError(java.lang.String, java.lang.Throwable);
+ ctor public InternalError(java.lang.Throwable);
}
public class InterruptedException extends java.lang.Exception {
@@ -50314,6 +50384,8 @@
public abstract class VirtualMachineError extends java.lang.Error {
ctor public VirtualMachineError();
ctor public VirtualMachineError(java.lang.String);
+ ctor public VirtualMachineError(java.lang.String, java.lang.Throwable);
+ ctor public VirtualMachineError(java.lang.Throwable);
}
public final class Void {
@@ -54056,6 +54128,7 @@
method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
+ method public final java.security.cert.CertPathChecker getRevocationChecker();
}
public class CertPathBuilderException extends java.security.GeneralSecurityException {
@@ -54073,6 +54146,13 @@
public abstract class CertPathBuilderSpi {
ctor public CertPathBuilderSpi();
method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+ method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+ }
+
+ public abstract interface CertPathChecker {
+ method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+ method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
+ method public abstract boolean isForwardCheckingSupported();
}
public abstract interface CertPathParameters implements java.lang.Cloneable {
@@ -54087,6 +54167,7 @@
method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
+ method public final java.security.cert.CertPathChecker getRevocationChecker();
method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
}
@@ -54123,6 +54204,7 @@
public abstract class CertPathValidatorSpi {
ctor public CertPathValidatorSpi();
+ method public java.security.cert.CertPathChecker engineGetRevocationChecker();
method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
}
@@ -54281,9 +54363,10 @@
method public java.security.cert.CertPath getCertPath();
}
- public abstract class PKIXCertPathChecker implements java.lang.Cloneable {
+ public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
ctor protected PKIXCertPathChecker();
method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+ method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
method public java.lang.Object clone();
method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
@@ -54343,6 +54426,30 @@
enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
}
+ public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+ ctor protected PKIXRevocationChecker();
+ method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+ method public java.net.URI getOcspResponder();
+ method public java.security.cert.X509Certificate getOcspResponderCert();
+ method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses();
+ method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+ method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+ method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+ method public void setOcspResponder(java.net.URI);
+ method public void setOcspResponderCert(java.security.cert.X509Certificate);
+ method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>);
+ method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+ }
+
+ public static final class PKIXRevocationChecker.Option extends java.lang.Enum {
+ method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String);
+ method public static final java.security.cert.PKIXRevocationChecker.Option[] values();
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+ }
+
public abstract interface PolicyNode {
method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
method public abstract int getDepth();
@@ -54502,6 +54609,7 @@
method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
method public abstract int getVersion();
+ method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
}
public abstract interface X509Extension {
diff --git a/api/system-current.txt b/api/system-current.txt
index bd7dc97..49ff948 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -619,6 +619,7 @@
field public static final int expandableListViewStyle = 16842863; // 0x101006f
field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
field public static final int exported = 16842768; // 0x1010010
+ field public static final int externalService = 16844047; // 0x101050f
field public static final int extraTension = 16843371; // 0x101026b
field public static final int extractNativeLibs = 16844010; // 0x10104ea
field public static final int factor = 16843219; // 0x10101d3
@@ -3611,6 +3612,7 @@
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public android.net.Uri onProvideReferrer();
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
@@ -4310,6 +4312,7 @@
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPrepareOptionsMenu(android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public void onRestoreInstanceState(android.os.Bundle);
method public android.os.Bundle onSaveInstanceState();
method public boolean onSearchRequested(android.view.SearchEvent);
@@ -5912,6 +5915,7 @@
method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> getActiveAdmins();
+ method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
method public boolean getAutoTimeRequired();
@@ -5919,6 +5923,7 @@
method public boolean getCameraDisabled(android.content.ComponentName);
method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+ method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
method public deprecated java.lang.String getDeviceInitializerApp();
@@ -5984,6 +5989,7 @@
method public boolean resetPassword(java.lang.String, int);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
+ method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
@@ -5992,6 +5998,7 @@
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+ method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -6038,6 +6045,7 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
+ field public static final java.lang.String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final java.lang.String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
@@ -7425,6 +7433,15 @@
field public static final int TYPE_SCO = 2; // 0x2
}
+ public class OobData implements android.os.Parcelable {
+ ctor public OobData();
+ method public int describeContents();
+ method public byte[] getSecurityManagerTk();
+ method public void setSecurityManagerTk(byte[]);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+ }
+
}
package android.bluetooth.le {
@@ -13139,6 +13156,7 @@
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
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 {
@@ -21667,8 +21685,6 @@
field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
- field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
- field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -24103,7 +24119,7 @@
method public int[] getObjectHandles(int, int, int);
method public android.mtp.MtpObjectInfo getObjectInfo(int);
method public long getParent(int);
- method public int getPartialObject(int, int, int, byte[]) throws java.io.IOException;
+ method public long getPartialObject(int, long, long, byte[]) throws java.io.IOException;
method public long getStorageId(int);
method public int[] getStorageIds();
method public android.mtp.MtpStorageInfo getStorageInfo(int);
@@ -24126,7 +24142,16 @@
public class MtpEvent {
ctor public MtpEvent();
+ method public int getDevicePropCode();
method public int getEventCode();
+ method public int getObjectFormatCode();
+ method public int getObjectHandle();
+ method public int getObjectPropCode();
+ method public int getParameter1();
+ method public int getParameter2();
+ method public int getParameter3();
+ method public int getStorageId();
+ method public int getTransactionId();
}
public final class MtpObjectInfo {
@@ -24207,6 +24232,7 @@
method public android.net.Network[] getAllNetworks();
method public deprecated boolean getBackgroundDataSetting();
method public android.net.Network getBoundNetworkForProcess();
+ method public java.lang.String getCaptivePortalServerUrl();
method public android.net.ProxyInfo getDefaultProxy();
method public android.net.LinkProperties getLinkProperties(android.net.Network);
method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
@@ -31375,6 +31401,22 @@
field public static final java.lang.String _ID = "_id";
}
+ public class BlockedNumberContract {
+ method public static boolean isBlocked(android.content.Context, java.lang.String);
+ field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
+ field public static final android.net.Uri AUTHORITY_URI;
+ }
+
+ public static class BlockedNumberContract.BlockedNumbers {
+ field public static final java.lang.String COLUMN_E164_NUMBER = "e164_number";
+ field public static final java.lang.String COLUMN_ID = "_id";
+ field public static final java.lang.String COLUMN_ORIGINAL_NUMBER = "original_number";
+ field public static final java.lang.String COLUMN_STRIPPED_NUMBER = "stripped_number";
+ field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_numbers";
+ field public static final android.net.Uri CONTENT_URI;
+ }
+
public class Browser {
ctor public Browser();
method public static final void sendString(android.content.Context, java.lang.String);
@@ -35629,6 +35671,7 @@
method public boolean onMenuOpened(int, android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public boolean onSearchRequested(android.view.SearchEvent);
method public boolean onSearchRequested();
method public void onWakeUp();
@@ -35723,6 +35766,7 @@
method public abstract void onUnsubscribe(android.net.Uri);
field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+ field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
@@ -35882,17 +35926,24 @@
method public java.lang.CharSequence getContentDescription();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getLabel();
+ method public int getState();
method public void setContentDescription(java.lang.CharSequence);
method public void setIcon(android.graphics.drawable.Icon);
method public void setLabel(java.lang.CharSequence);
+ method public void setState(int);
method public void updateTile();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
+ field public static final int STATE_ACTIVE = 2; // 0x2
+ field public static final int STATE_INACTIVE = 1; // 0x1
+ field public static final int STATE_UNAVAILABLE = 0; // 0x0
}
public class TileService extends android.app.Service {
ctor public TileService();
method public final android.service.quicksettings.Tile getQsTile();
+ method public final boolean isLocked();
+ method public final boolean isSecure();
method public android.os.IBinder onBind(android.content.Intent);
method public void onClick();
method public void onStartListening();
@@ -35902,6 +35953,8 @@
method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
method public final void setStatusIcon(android.graphics.drawable.Icon, java.lang.String);
method public final void showDialog(android.app.Dialog);
+ method public final void startActivityAndCollapse(android.content.Intent);
+ method public final void unlockAndRun(java.lang.Runnable);
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
field public static final int TILE_MODE_ACTIVE = 2; // 0x2
field public static final int TILE_MODE_PASSIVE = 1; // 0x1
@@ -37913,6 +37966,7 @@
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
field public static final java.lang.String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool";
field public static final java.lang.String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool";
+ field public static final java.lang.String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
@@ -40545,7 +40599,8 @@
ctor public SuggestionSpan(android.os.Parcel);
method public int describeContents();
method public int getFlags();
- method public java.lang.String getLocale();
+ method public deprecated java.lang.String getLocale();
+ method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
method public void setFlags(int);
@@ -42729,6 +42784,27 @@
method public void startTracking(android.view.KeyEvent, java.lang.Object);
}
+ public final class KeyboardShortcutGroup implements android.os.Parcelable {
+ ctor public KeyboardShortcutGroup(java.lang.CharSequence, java.util.List<android.view.KeyboardShortcutInfo>);
+ ctor public KeyboardShortcutGroup(java.lang.CharSequence);
+ method public void addItem(android.view.KeyboardShortcutInfo);
+ method public int describeContents();
+ method public java.util.List<android.view.KeyboardShortcutInfo> getItems();
+ method public java.lang.CharSequence getLabel();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutGroup> CREATOR;
+ }
+
+ public final class KeyboardShortcutInfo implements android.os.Parcelable {
+ ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int);
+ method public int describeContents();
+ method public char getBaseCharacter();
+ method public java.lang.CharSequence getLabel();
+ method public int getModifiers();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutInfo> CREATOR;
+ }
+
public abstract class LayoutInflater {
ctor protected LayoutInflater(android.content.Context);
ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context);
@@ -44731,6 +44807,7 @@
method public abstract boolean onMenuOpened(int, android.view.Menu);
method public abstract void onPanelClosed(int, android.view.Menu);
method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
+ method public abstract void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public abstract boolean onSearchRequested();
method public abstract boolean onSearchRequested(android.view.SearchEvent);
method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -45933,6 +46010,7 @@
}
public final class InputMethodManager {
+ method public void dispatchKeyEventFromInputMethod(android.view.View, android.view.KeyEvent);
method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
method public android.view.inputmethod.InputMethodSubtype getCurrentInputMethodSubtype();
method public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodList();
@@ -52207,6 +52285,8 @@
public class InternalError extends java.lang.VirtualMachineError {
ctor public InternalError();
ctor public InternalError(java.lang.String);
+ ctor public InternalError(java.lang.String, java.lang.Throwable);
+ ctor public InternalError(java.lang.Throwable);
}
public class InterruptedException extends java.lang.Exception {
@@ -52979,6 +53059,8 @@
public abstract class VirtualMachineError extends java.lang.Error {
ctor public VirtualMachineError();
ctor public VirtualMachineError(java.lang.String);
+ ctor public VirtualMachineError(java.lang.String, java.lang.Throwable);
+ ctor public VirtualMachineError(java.lang.Throwable);
}
public final class Void {
@@ -56721,6 +56803,7 @@
method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
+ method public final java.security.cert.CertPathChecker getRevocationChecker();
}
public class CertPathBuilderException extends java.security.GeneralSecurityException {
@@ -56738,6 +56821,13 @@
public abstract class CertPathBuilderSpi {
ctor public CertPathBuilderSpi();
method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+ method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+ }
+
+ public abstract interface CertPathChecker {
+ method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+ method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
+ method public abstract boolean isForwardCheckingSupported();
}
public abstract interface CertPathParameters implements java.lang.Cloneable {
@@ -56752,6 +56842,7 @@
method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
+ method public final java.security.cert.CertPathChecker getRevocationChecker();
method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
}
@@ -56788,6 +56879,7 @@
public abstract class CertPathValidatorSpi {
ctor public CertPathValidatorSpi();
+ method public java.security.cert.CertPathChecker engineGetRevocationChecker();
method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
}
@@ -56946,9 +57038,10 @@
method public java.security.cert.CertPath getCertPath();
}
- public abstract class PKIXCertPathChecker implements java.lang.Cloneable {
+ public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
ctor protected PKIXCertPathChecker();
method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+ method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
method public java.lang.Object clone();
method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
@@ -57008,6 +57101,30 @@
enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
}
+ public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+ ctor protected PKIXRevocationChecker();
+ method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+ method public java.net.URI getOcspResponder();
+ method public java.security.cert.X509Certificate getOcspResponderCert();
+ method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses();
+ method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+ method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+ method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+ method public void setOcspResponder(java.net.URI);
+ method public void setOcspResponderCert(java.security.cert.X509Certificate);
+ method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>);
+ method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+ }
+
+ public static final class PKIXRevocationChecker.Option extends java.lang.Enum {
+ method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String);
+ method public static final java.security.cert.PKIXRevocationChecker.Option[] values();
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+ }
+
public abstract interface PolicyNode {
method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
method public abstract int getDepth();
@@ -57167,6 +57284,7 @@
method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
method public abstract int getVersion();
+ method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
}
public abstract interface X509Extension {
diff --git a/api/test-current.txt b/api/test-current.txt
index 13d98f4..68f8a41 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -524,6 +524,7 @@
field public static final int expandableListViewStyle = 16842863; // 0x101006f
field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
field public static final int exported = 16842768; // 0x1010010
+ field public static final int externalService = 16844047; // 0x101050f
field public static final int extraTension = 16843371; // 0x101026b
field public static final int extractNativeLibs = 16844010; // 0x10104ea
field public static final int factor = 16843219; // 0x10101d3
@@ -2750,15 +2751,12 @@
method public android.os.Bundle addAccountFromCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle confirmCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle editProperties(android.accounts.AccountAuthenticatorResponse, java.lang.String);
- method public android.os.Bundle finishSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public android.os.Bundle getAccountCredentialsForCloning(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
method public android.os.Bundle getAccountRemovalAllowed(android.accounts.AccountAuthenticatorResponse, android.accounts.Account) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle getAuthToken(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract java.lang.String getAuthTokenLabel(java.lang.String);
method public final android.os.IBinder getIBinder();
method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException;
- method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException;
- method public android.os.Bundle startUpdateCredentialsSession(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException;
field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry";
}
@@ -2796,7 +2794,6 @@
method public void clearPassword(android.accounts.Account);
method public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(java.lang.String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
- method public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public static android.accounts.AccountManager get(android.content.Context);
method public android.accounts.Account[] getAccounts();
method public android.accounts.Account[] getAccountsByType(java.lang.String);
@@ -2824,8 +2821,6 @@
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
method public void setPassword(android.accounts.Account, java.lang.String);
method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
- method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
- method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator";
field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
@@ -2842,8 +2837,6 @@
field public static final java.lang.String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse";
field public static final java.lang.String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse";
field public static final java.lang.String KEY_ACCOUNT_NAME = "authAccount";
- field public static final java.lang.String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
- field public static final java.lang.String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
field public static final java.lang.String KEY_ACCOUNT_TYPE = "accountType";
field public static final java.lang.String KEY_ANDROID_PACKAGE_NAME = "androidPackageName";
field public static final java.lang.String KEY_AUTHENTICATOR_TYPES = "authenticator_types";
@@ -3505,6 +3498,7 @@
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public android.net.Uri onProvideReferrer();
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
@@ -4189,6 +4183,7 @@
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPrepareOptionsMenu(android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public void onRestoreInstanceState(android.os.Bundle);
method public android.os.Bundle onSaveInstanceState();
method public boolean onSearchRequested(android.view.SearchEvent);
@@ -5788,6 +5783,7 @@
method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> getActiveAdmins();
+ method public java.lang.String getAlwaysOnVpnPackage(android.content.ComponentName);
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
method public java.lang.String getApplicationRestrictionsManagingPackage(android.content.ComponentName);
method public boolean getAutoTimeRequired();
@@ -5795,6 +5791,7 @@
method public boolean getCameraDisabled(android.content.ComponentName);
method public java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
+ method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
method public java.lang.String getDeviceOwnerLockScreenInfo();
@@ -5851,6 +5848,7 @@
method public boolean removeUser(android.content.ComponentName, android.os.UserHandle);
method public boolean resetPassword(java.lang.String, int);
method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
+ method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String);
@@ -5859,6 +5857,7 @@
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+ method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean);
method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
@@ -5905,6 +5904,7 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_PROVISIONED = "android.app.action.MANAGED_PROFILE_PROVISIONED";
field public static final java.lang.String ACTION_PROVISION_MANAGED_DEVICE = "android.app.action.PROVISION_MANAGED_DEVICE";
field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.PROVISION_MANAGED_PROFILE";
+ field public static final java.lang.String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
@@ -7203,6 +7203,15 @@
field public static final int TYPE_SCO = 2; // 0x2
}
+ public class OobData implements android.os.Parcelable {
+ ctor public OobData();
+ method public int describeContents();
+ method public byte[] getSecurityManagerTk();
+ method public void setSecurityManagerTk(byte[]);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.OobData> CREATOR;
+ }
+
}
package android.bluetooth.le {
@@ -12792,6 +12801,7 @@
method public void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
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 {
@@ -20376,8 +20386,6 @@
field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
- field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
- field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -22564,7 +22572,7 @@
method public int[] getObjectHandles(int, int, int);
method public android.mtp.MtpObjectInfo getObjectInfo(int);
method public long getParent(int);
- method public int getPartialObject(int, int, int, byte[]) throws java.io.IOException;
+ method public long getPartialObject(int, long, long, byte[]) throws java.io.IOException;
method public long getStorageId(int);
method public int[] getStorageIds();
method public android.mtp.MtpStorageInfo getStorageInfo(int);
@@ -22587,7 +22595,16 @@
public class MtpEvent {
ctor public MtpEvent();
+ method public int getDevicePropCode();
method public int getEventCode();
+ method public int getObjectFormatCode();
+ method public int getObjectHandle();
+ method public int getObjectPropCode();
+ method public int getParameter1();
+ method public int getParameter2();
+ method public int getParameter3();
+ method public int getStorageId();
+ method public int getTransactionId();
}
public final class MtpObjectInfo {
@@ -29383,6 +29400,22 @@
field public static final java.lang.String _ID = "_id";
}
+ public class BlockedNumberContract {
+ method public static boolean isBlocked(android.content.Context, java.lang.String);
+ field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
+ field public static final android.net.Uri AUTHORITY_URI;
+ }
+
+ public static class BlockedNumberContract.BlockedNumbers {
+ field public static final java.lang.String COLUMN_E164_NUMBER = "e164_number";
+ field public static final java.lang.String COLUMN_ID = "_id";
+ field public static final java.lang.String COLUMN_ORIGINAL_NUMBER = "original_number";
+ field public static final java.lang.String COLUMN_STRIPPED_NUMBER = "stripped_number";
+ field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_numbers";
+ field public static final android.net.Uri CONTENT_URI;
+ }
+
public class Browser {
ctor public Browser();
method public static final void sendString(android.content.Context, java.lang.String);
@@ -33506,6 +33539,7 @@
method public boolean onMenuOpened(int, android.view.Menu);
method public void onPanelClosed(int, android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public boolean onSearchRequested(android.view.SearchEvent);
method public boolean onSearchRequested();
method public void onWakeUp();
@@ -33600,6 +33634,7 @@
method public abstract void onUnsubscribe(android.net.Uri);
field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
+ field public static final java.lang.String META_DATA_RULE_INSTANCE_LIMIT = "android.service.zen.automatic.ruleInstanceLimit";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
@@ -33727,17 +33762,24 @@
method public java.lang.CharSequence getContentDescription();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getLabel();
+ method public int getState();
method public void setContentDescription(java.lang.CharSequence);
method public void setIcon(android.graphics.drawable.Icon);
method public void setLabel(java.lang.CharSequence);
+ method public void setState(int);
method public void updateTile();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.quicksettings.Tile> CREATOR;
+ field public static final int STATE_ACTIVE = 2; // 0x2
+ field public static final int STATE_INACTIVE = 1; // 0x1
+ field public static final int STATE_UNAVAILABLE = 0; // 0x0
}
public class TileService extends android.app.Service {
ctor public TileService();
method public final android.service.quicksettings.Tile getQsTile();
+ method public final boolean isLocked();
+ method public final boolean isSecure();
method public android.os.IBinder onBind(android.content.Intent);
method public void onClick();
method public void onStartListening();
@@ -33746,6 +33788,8 @@
method public void onTileRemoved();
method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
method public final void showDialog(android.app.Dialog);
+ method public final void startActivityAndCollapse(android.content.Intent);
+ method public final void unlockAndRun(java.lang.Runnable);
field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
field public static final int TILE_MODE_ACTIVE = 2; // 0x2
field public static final int TILE_MODE_PASSIVE = 1; // 0x1
@@ -38212,7 +38256,8 @@
ctor public SuggestionSpan(android.os.Parcel);
method public int describeContents();
method public int getFlags();
- method public java.lang.String getLocale();
+ method public deprecated java.lang.String getLocale();
+ method public java.util.Locale getLocaleObject();
method public int getSpanTypeId();
method public java.lang.String[] getSuggestions();
method public void setFlags(int);
@@ -40396,6 +40441,27 @@
method public void startTracking(android.view.KeyEvent, java.lang.Object);
}
+ public final class KeyboardShortcutGroup implements android.os.Parcelable {
+ ctor public KeyboardShortcutGroup(java.lang.CharSequence, java.util.List<android.view.KeyboardShortcutInfo>);
+ ctor public KeyboardShortcutGroup(java.lang.CharSequence);
+ method public void addItem(android.view.KeyboardShortcutInfo);
+ method public int describeContents();
+ method public java.util.List<android.view.KeyboardShortcutInfo> getItems();
+ method public java.lang.CharSequence getLabel();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutGroup> CREATOR;
+ }
+
+ public final class KeyboardShortcutInfo implements android.os.Parcelable {
+ ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int);
+ method public int describeContents();
+ method public char getBaseCharacter();
+ method public java.lang.CharSequence getLabel();
+ method public int getModifiers();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutInfo> CREATOR;
+ }
+
public abstract class LayoutInflater {
ctor protected LayoutInflater(android.content.Context);
ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context);
@@ -42397,6 +42463,7 @@
method public abstract boolean onMenuOpened(int, android.view.Menu);
method public abstract void onPanelClosed(int, android.view.Menu);
method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu);
+ method public abstract void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu);
method public abstract boolean onSearchRequested();
method public abstract boolean onSearchRequested(android.view.SearchEvent);
method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams);
@@ -43597,6 +43664,7 @@
}
public final class InputMethodManager {
+ method public void dispatchKeyEventFromInputMethod(android.view.View, android.view.KeyEvent);
method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
method public android.view.inputmethod.InputMethodSubtype getCurrentInputMethodSubtype();
method public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodList();
@@ -49558,6 +49626,8 @@
public class InternalError extends java.lang.VirtualMachineError {
ctor public InternalError();
ctor public InternalError(java.lang.String);
+ ctor public InternalError(java.lang.String, java.lang.Throwable);
+ ctor public InternalError(java.lang.Throwable);
}
public class InterruptedException extends java.lang.Exception {
@@ -50330,6 +50400,8 @@
public abstract class VirtualMachineError extends java.lang.Error {
ctor public VirtualMachineError();
ctor public VirtualMachineError(java.lang.String);
+ ctor public VirtualMachineError(java.lang.String, java.lang.Throwable);
+ ctor public VirtualMachineError(java.lang.Throwable);
}
public final class Void {
@@ -54072,6 +54144,7 @@
method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static java.security.cert.CertPathBuilder getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
+ method public final java.security.cert.CertPathChecker getRevocationChecker();
}
public class CertPathBuilderException extends java.security.GeneralSecurityException {
@@ -54089,6 +54162,13 @@
public abstract class CertPathBuilderSpi {
ctor public CertPathBuilderSpi();
method public abstract java.security.cert.CertPathBuilderResult engineBuild(java.security.cert.CertPathParameters) throws java.security.cert.CertPathBuilderException, java.security.InvalidAlgorithmParameterException;
+ method public java.security.cert.CertPathChecker engineGetRevocationChecker();
+ }
+
+ public abstract interface CertPathChecker {
+ method public abstract void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
+ method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
+ method public abstract boolean isForwardCheckingSupported();
}
public abstract interface CertPathParameters implements java.lang.Cloneable {
@@ -54103,6 +54183,7 @@
method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.lang.String) throws java.security.NoSuchAlgorithmException, java.security.NoSuchProviderException;
method public static java.security.cert.CertPathValidator getInstance(java.lang.String, java.security.Provider) throws java.security.NoSuchAlgorithmException;
method public final java.security.Provider getProvider();
+ method public final java.security.cert.CertPathChecker getRevocationChecker();
method public final java.security.cert.CertPathValidatorResult validate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
}
@@ -54139,6 +54220,7 @@
public abstract class CertPathValidatorSpi {
ctor public CertPathValidatorSpi();
+ method public java.security.cert.CertPathChecker engineGetRevocationChecker();
method public abstract java.security.cert.CertPathValidatorResult engineValidate(java.security.cert.CertPath, java.security.cert.CertPathParameters) throws java.security.cert.CertPathValidatorException, java.security.InvalidAlgorithmParameterException;
}
@@ -54297,9 +54379,10 @@
method public java.security.cert.CertPath getCertPath();
}
- public abstract class PKIXCertPathChecker implements java.lang.Cloneable {
+ public abstract class PKIXCertPathChecker implements java.security.cert.CertPathChecker java.lang.Cloneable {
ctor protected PKIXCertPathChecker();
method public abstract void check(java.security.cert.Certificate, java.util.Collection<java.lang.String>) throws java.security.cert.CertPathValidatorException;
+ method public void check(java.security.cert.Certificate) throws java.security.cert.CertPathValidatorException;
method public java.lang.Object clone();
method public abstract java.util.Set<java.lang.String> getSupportedExtensions();
method public abstract void init(boolean) throws java.security.cert.CertPathValidatorException;
@@ -54359,6 +54442,30 @@
enum_constant public static final java.security.cert.PKIXReason UNRECOGNIZED_CRIT_EXT;
}
+ public abstract class PKIXRevocationChecker extends java.security.cert.PKIXCertPathChecker {
+ ctor protected PKIXRevocationChecker();
+ method public java.util.List<java.security.cert.Extension> getOcspExtensions();
+ method public java.net.URI getOcspResponder();
+ method public java.security.cert.X509Certificate getOcspResponderCert();
+ method public java.util.Map<java.security.cert.X509Certificate, byte[]> getOcspResponses();
+ method public java.util.Set<java.security.cert.PKIXRevocationChecker.Option> getOptions();
+ method public abstract java.util.List<java.security.cert.CertPathValidatorException> getSoftFailExceptions();
+ method public void setOcspExtensions(java.util.List<java.security.cert.Extension>);
+ method public void setOcspResponder(java.net.URI);
+ method public void setOcspResponderCert(java.security.cert.X509Certificate);
+ method public void setOcspResponses(java.util.Map<java.security.cert.X509Certificate, byte[]>);
+ method public void setOptions(java.util.Set<java.security.cert.PKIXRevocationChecker.Option>);
+ }
+
+ public static final class PKIXRevocationChecker.Option extends java.lang.Enum {
+ method public static java.security.cert.PKIXRevocationChecker.Option valueOf(java.lang.String);
+ method public static final java.security.cert.PKIXRevocationChecker.Option[] values();
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option NO_FALLBACK;
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option ONLY_END_ENTITY;
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option PREFER_CRLS;
+ enum_constant public static final java.security.cert.PKIXRevocationChecker.Option SOFT_FAIL;
+ }
+
public abstract interface PolicyNode {
method public abstract java.util.Iterator<? extends java.security.cert.PolicyNode> getChildren();
method public abstract int getDepth();
@@ -54518,6 +54625,7 @@
method public javax.security.auth.x500.X500Principal getSubjectX500Principal();
method public abstract byte[] getTBSCertificate() throws java.security.cert.CertificateEncodingException;
method public abstract int getVersion();
+ method public void verify(java.security.PublicKey, java.security.Provider) throws java.security.cert.CertificateException, java.security.InvalidKeyException, java.security.NoSuchAlgorithmException, java.security.SignatureException;
}
public abstract interface X509Extension {
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index a312e3f..690e674 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.Intent;
import android.Manifest;
+import android.annotation.SystemApi;
import android.util.Log;
import java.util.Arrays;
@@ -762,7 +763,9 @@
* @throws NetworkErrorException if the authenticator could not honor the
* request due to a network error
* @see #finishSession(AccountAuthenticatorResponse, String, Bundle)
+ * @hide
*/
+ @SystemApi
public Bundle startAddAccountSession(
final AccountAuthenticatorResponse response,
final String accountType,
@@ -818,7 +821,9 @@
* @throws NetworkErrorException if the authenticator could not honor the
* request due to a network error
* @see #finishSession(AccountAuthenticatorResponse, String, Bundle)
+ * @hide
*/
+ @SystemApi
public Bundle startUpdateCredentialsSession(
final AccountAuthenticatorResponse response,
final Account account,
@@ -870,7 +875,9 @@
* </ul>
* @throws NetworkErrorException
* @see #startAddAccountSession and #startUpdateCredentialsSession
+ * @hide
*/
+ @SystemApi
public Bundle finishSession(
final AccountAuthenticatorResponse response,
final String accountType,
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index ada1ac2..2449ee5 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.Size;
+import android.annotation.SystemApi;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -244,14 +245,18 @@
* Bundle key used for a {@link Bundle} in result from
* {@link #startAddAccountSession} and friends which returns session data
* for installing an account later.
+ * @hide
*/
+ @SystemApi
public static final String KEY_ACCOUNT_SESSION_BUNDLE = "accountSessionBundle";
/**
* Bundle key used for the {@link String} account status token in result
* from {@link #startAddAccountSession} and friends which returns
* information about a particular account.
+ * @hide
*/
+ @SystemApi
public static final String KEY_ACCOUNT_STATUS_TOKEN = "accountStatusToken";
public static final String ACTION_AUTHENTICATOR_INTENT =
@@ -2667,7 +2672,9 @@
* trouble
* </ul>
* @see #finishSession
+ * @hide
*/
+ @SystemApi
public AccountManagerFuture<Bundle> startAddAccountSession(
final String accountType,
final String authTokenType,
@@ -2749,7 +2756,9 @@
* trouble
* </ul>
* @see #finishSession
+ * @hide
*/
+ @SystemApi
public AccountManagerFuture<Bundle> startUpdateCredentialsSession(
final Account account,
final String authTokenType,
@@ -2818,7 +2827,9 @@
* trouble
* </ul>
* @see #startAddAccountSession and #startUpdateCredentialsSession
+ * @hide
*/
+ @SystemApi
public AccountManagerFuture<Bundle> finishSession(
final Bundle sessionBundle,
final Activity activity,
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 7f9a5d3..e721de9 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -17,6 +17,7 @@
package android.animation;
import android.annotation.CallSuper;
+import android.annotation.IntDef;
import android.os.Looper;
import android.os.Trace;
import android.util.AndroidRuntimeException;
@@ -25,6 +26,8 @@
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
@@ -234,6 +237,11 @@
* Public constants
*/
+ /** @hide */
+ @IntDef({RESTART, REVERSE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RepeatMode {}
+
/**
* When the animation reaches the end and <code>repeatCount</code> is INFINITE
* or a positive value, the animation restarts from the beginning.
@@ -807,7 +815,7 @@
*
* @param value {@link #RESTART} or {@link #REVERSE}
*/
- public void setRepeatMode(int value) {
+ public void setRepeatMode(@RepeatMode int value) {
mRepeatMode = value;
}
@@ -816,6 +824,7 @@
*
* @return either one of {@link #REVERSE} or {@link #RESTART}
*/
+ @RepeatMode
public int getRepeatMode() {
return mRepeatMode;
}
diff --git a/core/java/android/annotation/IntDef.java b/core/java/android/annotation/IntDef.java
index a18bfa5..434a9c7 100644
--- a/core/java/android/annotation/IntDef.java
+++ b/core/java/android/annotation/IntDef.java
@@ -42,7 +42,7 @@
* For a flag, set the flag attribute:
* <pre><code>
* @IntDef(
- * flag = true
+ * flag = true,
* value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
* </code></pre>
*
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 34527c2..e312596 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -62,6 +62,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
@@ -71,6 +72,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.UserHandle;
@@ -78,23 +80,28 @@
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
+import android.transition.Scene;
+import android.transition.TransitionManager;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SuperNotCalledException;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
-import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
@@ -103,10 +110,17 @@
import android.view.ViewManager;
import android.view.ViewRootImpl;
import android.view.Window;
+import android.view.Window.WindowControllerCallback;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
+import android.widget.Toolbar;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ToolbarActionBar;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.PhoneWindow;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -116,6 +130,8 @@
import java.util.HashMap;
import java.util.List;
+import static java.lang.Character.MIN_VALUE;
+
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -1594,6 +1610,30 @@
public void onProvideAssistContent(AssistContent outContent) {
}
+ @Override
+ public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+ if (menu == null) {
+ return;
+ }
+ KeyboardShortcutGroup group = null;
+ int menuSize = menu.size();
+ for (int i = 0; i < menuSize; ++i) {
+ final MenuItem item = menu.getItem(i);
+ final CharSequence title = item.getTitle();
+ final char alphaShortcut = item.getAlphabeticShortcut();
+ if (title != null && alphaShortcut != MIN_VALUE) {
+ if (group == null) {
+ group = new KeyboardShortcutGroup(null /* no label */);
+ }
+ group.addItem(new KeyboardShortcutInfo(
+ title, alphaShortcut, KeyEvent.META_CTRL_ON));
+ }
+ }
+ if (group != null) {
+ data.add(group);
+ }
+ }
+
/**
* Ask to have the current assistant shown to the user. This only works if the calling
* activity is the current foreground activity. It is the same as calling
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9540ae1..2175a9e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -592,6 +592,14 @@
public static boolean isAlwaysOnTop(int stackId) {
return stackId == PINNED_STACK_ID;
}
+
+ /**
+ * Returns true if the top task in the task is allowed to return home when finished and
+ * there are other tasks in the stack.
+ */
+ public static boolean allowTopTaskToReturnHome(int stackId) {
+ return stackId != PINNED_STACK_ID;
+ }
}
/**
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 624131e..94b4e7f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2842,6 +2842,14 @@
reply.writeNoException();
return true;
}
+ case IS_APP_FOREGROUND_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int userHandle = data.readInt();
+ final boolean isForeground = isAppForeground(userHandle);
+ reply.writeNoException();
+ reply.writeInt(isForeground ? 1 : 0);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -6639,5 +6647,18 @@
reply.recycle();
}
+ @Override
+ public boolean isAppForeground(int uid) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(uid);
+ mRemote.transact(IS_APP_FOREGROUND_TRANSACTION, data, reply, 0);
+ final boolean isForeground = reply.readInt() == 1 ? true : false;
+ data.recycle();
+ reply.recycle();
+ return isForeground;
+ };
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 81e00ff..4e55c89 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2015,7 +2015,7 @@
private static final String ONE_COUNT_COLUMN_HEADER = "%21s %8s";
// Formatting for checkin service - update version if row format changes
- private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3;
+ private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 4;
static void printRow(PrintWriter pw, String format, Object...objs) {
pw.println(String.format(format, objs));
@@ -2091,6 +2091,25 @@
pw.print(memInfo.otherPrivateClean); pw.print(',');
pw.print(memInfo.getTotalPrivateClean()); pw.print(',');
+ // Heap info - swapped out
+ pw.print(memInfo.nativeSwappedOut); pw.print(',');
+ pw.print(memInfo.dalvikSwappedOut); pw.print(',');
+ pw.print(memInfo.otherSwappedOut); pw.print(',');
+ pw.print(memInfo.getTotalSwappedOut()); pw.print(',');
+
+ // Heap info - swapped out pss
+ if (memInfo.hasSwappedOutPss) {
+ pw.print(memInfo.nativeSwappedOutPss); pw.print(',');
+ pw.print(memInfo.dalvikSwappedOutPss); pw.print(',');
+ pw.print(memInfo.otherSwappedOutPss); pw.print(',');
+ pw.print(memInfo.getTotalSwappedOutPss()); pw.print(',');
+ } else {
+ pw.print("N/A,");
+ pw.print("N/A,");
+ pw.print("N/A,");
+ pw.print("N/A,");
+ }
+
// Heap info - other areas
for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(',');
@@ -2100,6 +2119,12 @@
pw.print(memInfo.getOtherSharedClean(i)); pw.print(',');
pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(',');
pw.print(memInfo.getOtherPrivateClean(i)); pw.print(',');
+ pw.print(memInfo.getOtherSwappedOut(i)); pw.print(',');
+ if (memInfo.hasSwappedOutPss) {
+ pw.print(memInfo.getOtherSwappedOutPss(i)); pw.print(',');
+ } else {
+ pw.print("N/A,");
+ }
}
return;
}
@@ -2107,35 +2132,44 @@
if (!dumpSummaryOnly) {
if (dumpFullInfo) {
printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private",
- "Shared", "Private", "Swapped", "Heap", "Heap", "Heap");
+ "Shared", "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap",
+ "Heap", "Heap", "Heap");
printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty",
- "Clean", "Clean", "Dirty", "Size", "Alloc", "Free");
+ "Clean", "Clean", "Dirty",
+ "Size", "Alloc", "Free");
printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------",
"------", "------", "------", "------", "------", "------");
printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss,
memInfo.nativeSwappablePss, memInfo.nativeSharedDirty,
memInfo.nativePrivateDirty, memInfo.nativeSharedClean,
- memInfo.nativePrivateClean, memInfo.nativeSwappedOut,
+ memInfo.nativePrivateClean, memInfo.hasSwappedOutPss ?
+ memInfo.nativeSwappedOut : memInfo.nativeSwappedOutPss,
nativeMax, nativeAllocated, nativeFree);
printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty,
memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean,
- memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut,
+ memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss ?
+ memInfo.dalvikSwappedOut : memInfo.dalvikSwappedOutPss,
dalvikMax, dalvikAllocated, dalvikFree);
} else {
printRow(pw, HEAP_COLUMN, "", "Pss", "Private",
- "Private", "Swapped", "Heap", "Heap", "Heap");
+ "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap",
+ "Heap", "Heap", "Heap");
printRow(pw, HEAP_COLUMN, "", "Total", "Dirty",
"Clean", "Dirty", "Size", "Alloc", "Free");
printRow(pw, HEAP_COLUMN, "", "------", "------", "------",
"------", "------", "------", "------", "------");
printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss,
memInfo.nativePrivateDirty,
- memInfo.nativePrivateClean, memInfo.nativeSwappedOut,
+ memInfo.nativePrivateClean,
+ memInfo.hasSwappedOutPss ? memInfo.nativeSwappedOutPss :
+ memInfo.nativeSwappedOut,
nativeMax, nativeAllocated, nativeFree);
printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss,
memInfo.dalvikPrivateDirty,
- memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut,
+ memInfo.dalvikPrivateClean,
+ memInfo.hasSwappedOutPss ? memInfo.dalvikSwappedOutPss :
+ memInfo.dalvikSwappedOut,
dalvikMax, dalvikAllocated, dalvikFree);
}
@@ -2146,6 +2180,7 @@
int otherSharedClean = memInfo.otherSharedClean;
int otherPrivateClean = memInfo.otherPrivateClean;
int otherSwappedOut = memInfo.otherSwappedOut;
+ int otherSwappedOutPss = memInfo.otherSwappedOutPss;
for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
final int myPss = memInfo.getOtherPss(i);
@@ -2155,16 +2190,22 @@
final int mySharedClean = memInfo.getOtherSharedClean(i);
final int myPrivateClean = memInfo.getOtherPrivateClean(i);
final int mySwappedOut = memInfo.getOtherSwappedOut(i);
+ final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i);
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
- || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) {
+ || mySharedClean != 0 || myPrivateClean != 0
+ || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
if (dumpFullInfo) {
printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
- mySharedClean, myPrivateClean, mySwappedOut, "", "", "");
+ mySharedClean, myPrivateClean,
+ memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+ "", "", "");
} else {
printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
myPss, myPrivateDirty,
- myPrivateClean, mySwappedOut, "", "", "");
+ myPrivateClean,
+ memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+ "", "", "");
}
otherPss -= myPss;
otherSwappablePss -= mySwappablePss;
@@ -2173,26 +2214,32 @@
otherSharedClean -= mySharedClean;
otherPrivateClean -= myPrivateClean;
otherSwappedOut -= mySwappedOut;
+ otherSwappedOutPss -= mySwappedOutPss;
}
}
if (dumpFullInfo) {
printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss,
otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean,
- otherSwappedOut, "", "", "");
+ memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut,
+ "", "", "");
printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(),
memInfo.getTotalSwappablePss(),
memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(),
memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(),
- memInfo.getTotalSwappedOut(), nativeMax+dalvikMax,
- nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
+ memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOut() :
+ memInfo.getTotalSwappedOutPss(),
+ nativeMax+dalvikMax, nativeAllocated+dalvikAllocated,
+ nativeFree+dalvikFree);
} else {
printRow(pw, HEAP_COLUMN, "Unknown", otherPss,
- otherPrivateDirty, otherPrivateClean, otherSwappedOut,
+ otherPrivateDirty, otherPrivateClean,
+ memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut,
"", "", "");
printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(),
memInfo.getTotalPrivateDirty(),
memInfo.getTotalPrivateClean(),
+ memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() :
memInfo.getTotalSwappedOut(),
nativeMax+dalvikMax,
nativeAllocated+dalvikAllocated, nativeFree+dalvikFree);
@@ -2211,16 +2258,22 @@
final int mySharedClean = memInfo.getOtherSharedClean(i);
final int myPrivateClean = memInfo.getOtherPrivateClean(i);
final int mySwappedOut = memInfo.getOtherSwappedOut(i);
+ final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i);
if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0
- || mySharedClean != 0 || myPrivateClean != 0) {
+ || mySharedClean != 0 || myPrivateClean != 0
+ || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) {
if (dumpFullInfo) {
printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
myPss, mySwappablePss, mySharedDirty, myPrivateDirty,
- mySharedClean, myPrivateClean, mySwappedOut, "", "", "");
+ mySharedClean, myPrivateClean,
+ memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+ "", "", "");
} else {
printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i),
myPss, myPrivateDirty,
- myPrivateClean, mySwappedOut, "", "", "");
+ myPrivateClean,
+ memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut,
+ "", "", "");
}
}
}
@@ -2246,9 +2299,15 @@
printRow(pw, ONE_COUNT_COLUMN,
"System:", memInfo.getSummarySystem());
pw.println(" ");
- printRow(pw, TWO_COUNT_COLUMNS,
- "TOTAL:", memInfo.getSummaryTotalPss(),
- "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap());
+ if (memInfo.hasSwappedOutPss) {
+ printRow(pw, TWO_COUNT_COLUMNS,
+ "TOTAL:", memInfo.getSummaryTotalPss(),
+ "TOTAL SWAP PSS:", memInfo.getSummaryTotalSwapPss());
+ } else {
+ printRow(pw, TWO_COUNT_COLUMNS,
+ "TOTAL:", memInfo.getSummaryTotalPss(),
+ "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap());
+ }
}
public void registerOnActivityPausedListener(Activity activity,
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index e28fb20..af840d0 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -894,7 +894,9 @@
final View decor = getDecor();
if (decor != null) {
final ViewRootImpl viewRoot = decor.getViewRootImpl();
- viewRoot.setPausedForTransition(false);
+ if (viewRoot != null) {
+ viewRoot.setPausedForTransition(false);
+ }
}
onTransitionsComplete();
}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 6e8e2c4..79461b4 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -21,9 +21,8 @@
import android.annotation.IdRes;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
-import android.annotation.StringRes;
-
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.content.ComponentName;
import android.content.Context;
@@ -44,11 +43,11 @@
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
@@ -60,8 +59,10 @@
import com.android.internal.R;
import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.PhoneWindow;
import java.lang.ref.WeakReference;
+import java.util.List;
/**
* Base class for Dialogs.
@@ -1081,6 +1082,13 @@
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+ }
+
+ /**
* @return The activity associated with this dialog, or null if there is no associated activity.
*/
private ComponentName getAssociatedActivity() {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index a73ad09..e163b1c 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -409,9 +409,6 @@
// If set this fragment is being removed from its activity.
boolean mRemoving;
- // True if the fragment is in the resumed state.
- boolean mResumed;
-
// Set to true if this fragment was instantiated from a layout file.
boolean mFromLayout;
@@ -928,7 +925,7 @@
* for the duration of {@link #onResume()} and {@link #onPause()} as well.
*/
final public boolean isResumed() {
- return mResumed;
+ return mState >= RESUMED;
}
/**
@@ -1630,7 +1627,6 @@
mWho = null;
mAdded = false;
mRemoving = false;
- mResumed = false;
mFromLayout = false;
mInLayout = false;
mRestored = false;
@@ -2113,7 +2109,6 @@
writer.print(" mBackStackNesting="); writer.println(mBackStackNesting);
writer.print(prefix); writer.print("mAdded="); writer.print(mAdded);
writer.print(" mRemoving="); writer.print(mRemoving);
- writer.print(" mResumed="); writer.print(mResumed);
writer.print(" mFromLayout="); writer.print(mFromLayout);
writer.print(" mInLayout="); writer.println(mInLayout);
writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
@@ -2208,6 +2203,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.noteStateNotSaved();
}
+ mState = CREATED;
mCalled = false;
onCreate(savedInstanceState);
if (!mCalled) {
@@ -2238,6 +2234,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.noteStateNotSaved();
}
+ mState = ACTIVITY_CREATED;
mCalled = false;
onActivityCreated(savedInstanceState);
if (!mCalled) {
@@ -2254,6 +2251,7 @@
mChildFragmentManager.noteStateNotSaved();
mChildFragmentManager.execPendingActions();
}
+ mState = STARTED;
mCalled = false;
onStart();
if (!mCalled) {
@@ -2273,6 +2271,7 @@
mChildFragmentManager.noteStateNotSaved();
mChildFragmentManager.execPendingActions();
}
+ mState = RESUMED;
mCalled = false;
onResume();
if (!mCalled) {
@@ -2389,6 +2388,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.dispatchPause();
}
+ mState = STARTED;
mCalled = false;
onPause();
if (!mCalled) {
@@ -2401,6 +2401,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.dispatchStop();
}
+ mState = STOPPED;
mCalled = false;
onStop();
if (!mCalled) {
@@ -2428,6 +2429,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.dispatchDestroyView();
}
+ mState = CREATED;
mCalled = false;
onDestroyView();
if (!mCalled) {
@@ -2443,6 +2445,7 @@
if (mChildFragmentManager != null) {
mChildFragmentManager.dispatchDestroy();
}
+ mState = INITIALIZING;
mCalled = false;
onDestroy();
if (!mCalled) {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 696ccdb..84ae09d 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1004,7 +1004,6 @@
case Fragment.STARTED:
if (newState > Fragment.STARTED) {
if (DEBUG) Log.v(TAG, "moveto RESUMED: " + f);
- f.mResumed = true;
f.performResume();
// Get rid of this in case we saved it and never needed it.
f.mSavedFragmentState = null;
@@ -1017,7 +1016,6 @@
if (newState < Fragment.RESUMED) {
if (DEBUG) Log.v(TAG, "movefrom RESUMED: " + f);
f.performPause();
- f.mResumed = false;
}
case Fragment.STARTED:
if (newState < Fragment.STARTED) {
@@ -1096,6 +1094,8 @@
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
if (!f.mRetaining) {
f.performDestroy();
+ } else {
+ f.mState = Fragment.INITIALIZING;
}
f.mCalled = false;
@@ -1119,7 +1119,11 @@
}
}
- f.mState = newState;
+ if (f.mState != newState) {
+ Log.w(TAG, "moveToState: Fragment state for " + f + " not updated inline; "
+ + "expected state " + newState + " found " + f.mState);
+ f.mState = newState;
+ }
}
void moveToState(Fragment f) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 1ae91a6..10885f2 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -589,6 +589,8 @@
public void setVrMode(IBinder token, boolean enabled) throws RemoteException;
+ public boolean isAppForeground(int uid) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -960,4 +962,5 @@
int SET_VR_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 359;
int GET_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 360;
int CLEAR_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 361;
+ int IS_APP_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 362;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 633f699..368b8ef 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -103,6 +103,7 @@
boolean updateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
boolean removeAutomaticZenRule(String id);
boolean removeAutomaticZenRules(String packageName);
+ int getRuleInstanceCount(in ComponentName owner);
byte[] getBackupPayload(int user);
void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 9a3c820..faf5b11 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -380,6 +380,18 @@
}
/**
+ * @hide
+ */
+ public int getRuleInstanceCount(ComponentName owner) {
+ INotificationManager service = getService();
+ try {
+ return service.getRuleInstanceCount(owner);
+ } catch (RemoteException e) {
+ }
+ return 0;
+ }
+
+ /**
* Returns AutomaticZenRules owned by the caller.
*
* <p>
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 89d52f2..f3b1175 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -78,6 +78,8 @@
import android.net.wifi.RttManager;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
+import android.net.wifi.nan.IWifiNanManager;
+import android.net.wifi.nan.WifiNanManager;
import android.net.wifi.p2p.IWifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.passpoint.IWifiPasspointManager;
@@ -499,6 +501,18 @@
return new WifiP2pManager(service);
}});
+ registerService(Context.WIFI_NAN_SERVICE, WifiNanManager.class,
+ new StaticServiceFetcher<WifiNanManager>() {
+ @Override
+ public WifiNanManager createService() {
+ IBinder b = ServiceManager.getService(Context.WIFI_NAN_SERVICE);
+ IWifiNanManager service = IWifiNanManager.Stub.asInterface(b);
+ if (service == null) {
+ return null;
+ }
+ return new WifiNanManager(service);
+ }});
+
registerService(Context.WIFI_SCANNING_SERVICE, WifiScanner.class,
new CachedServiceFetcher<WifiScanner>() {
@Override
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 13e27e2..bd10267 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -28,6 +28,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.view.IWindowManager;
import android.view.InputEvent;
import android.view.SurfaceControl;
@@ -167,9 +168,10 @@
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
+ int callingUserId = UserHandle.getCallingUserId();
final long identity = Binder.clearCallingIdentity();
try {
- IBinder token = mAccessibilityManager.getWindowToken(windowId);
+ IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId);
if (token == null) {
return false;
}
@@ -186,9 +188,10 @@
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
+ int callingUserId = UserHandle.getCallingUserId();
final long identity = Binder.clearCallingIdentity();
try {
- IBinder token = mAccessibilityManager.getWindowToken(windowId);
+ IBinder token = mAccessibilityManager.getWindowToken(windowId, callingUserId);
if (token == null) {
return null;
}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 4416415..56b4249 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -16,12 +16,16 @@
package android.app;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This class provides access to the system uimode services. These services
* allow applications to control UI modes of the device.
@@ -92,18 +96,26 @@
* when the user exits desk mode.
*/
public static String ACTION_EXIT_DESK_MODE = "android.app.action.EXIT_DESK_MODE";
-
- /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+
+ /** @hide */
+ @IntDef({MODE_NIGHT_AUTO, MODE_NIGHT_NO, MODE_NIGHT_YES})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NightMode {}
+
+ /**
+ * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
* automatically switch night mode on and off based on the time.
*/
public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_UNDEFINED >> 4;
- /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ /**
+ * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
* never run in night mode.
*/
public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4;
- /** Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
+ /**
+ * Constant for {@link #setNightMode(int)} and {@link #getNightMode()}:
* always run in night mode.
*/
public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4;
@@ -195,20 +207,28 @@
}
/**
- * Sets the night mode. Changes to the night mode are only effective when
- * the car or desk mode is enabled on a device.
- *
- * <p>The mode can be one of:
+ * Sets the night mode.
+ * <p>
+ * The mode can be one of:
* <ul>
- * <li><em>{@link #MODE_NIGHT_NO}<em> - sets the device into notnight
- * mode.</li>
- * <li><em>{@link #MODE_NIGHT_YES}</em> - sets the device into night mode.
- * </li>
- * <li><em>{@link #MODE_NIGHT_AUTO}</em> - automatic night/notnight switching
- * depending on the location and certain other sensors.</li>
+ * <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
+ * {@code notnight} mode</li>
+ * <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
+ * {@code night} mode</li>
+ * <li><em>{@link #MODE_NIGHT_AUTO}</em> automatically switches between
+ * {@code night} and {@code notnight} based on the device's current
+ * location and certain other sensors</li>
* </ul>
+ * <p>
+ * <strong>Note:</strong> On API 22 and below, changes to the night mode
+ * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
+ * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
+ * device. Starting in API 23, changes to night mode are always effective.
+ *
+ * @param mode the night mode to set
+ * @see #getNightMode()
*/
- public void setNightMode(int mode) {
+ public void setNightMode(@NightMode int mode) {
if (mService != null) {
try {
mService.setNightMode(mode);
@@ -219,11 +239,20 @@
}
/**
- * @return the currently configured night mode. May be one of
- * {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES},
- * {@link #MODE_NIGHT_AUTO}, or -1 on error.
+ * Returns the currently configured night mode.
+ * <p>
+ * May be one of:
+ * <ul>
+ * <li>{@link #MODE_NIGHT_NO}</li>
+ * <li>{@link #MODE_NIGHT_YES}</li>
+ * <li>{@link #MODE_NIGHT_AUTO}</li>
+ * <li>{@code -1} on error</li>
+ * </ul>
+ *
+ * @return the current night mode, or {@code -1} on error
+ * @see #setNightMode(int)
*/
- public int getNightMode() {
+ public @NightMode int getNightMode() {
if (mService != null) {
try {
return mService.getNightMode();
@@ -250,9 +279,13 @@
}
/**
- * @return If Night mode is locked or not. When Night mode is locked, changing Night mode
- * is only allowed to privileged system components and normal application's call
- * to change Night mode using {@link #setNightMode(int)} will silently fail.
+ * Returns whether night mode is locked or not.
+ * <p>
+ * When night mode is locked, only privileged system components may change
+ * night mode and calls from non-privileged applications to change night
+ * mode will fail silently.
+ *
+ * @return {@code true} if night mode is locked or {@code false} otherwise
*/
public boolean isNightModeLocked() {
if (mService != null) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 08e9b1e..45b23d0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -620,6 +620,21 @@
"android.app.extra.PROVISIONING_LOGO_URI";
/**
+ * A boolean extra indicating if user setup should be skipped, for when provisioning is started
+ * during setup-wizard.
+ *
+ * <p>If unspecified, defaults to {@code true} to match the behavior in
+ * {@link android.os.Build.VERSION_CODES#M} and earlier.
+ *
+ * <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE},
+ * {@link #ACTION_PROVISION_MANAGED_DEVICE} or {@link #ACTION_PROVISION_MANAGED_USER}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PROVISIONING_SKIP_USER_SETUP =
+ "android.app.extra.PROVISIONING_SKIP_USER_SETUP";
+
+ /**
* This MIME type is used for starting the Device Owner provisioning.
*
* <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -758,12 +773,28 @@
* have the user select a new password in order to meet the current
* constraints. Upon being resumed from this activity, you can check the new
* password characteristics to see if they are sufficient.
+ *
+ * If the intent is launched from within a managed profile with a profile
+ * owner built against {@link android.os.Build.VERSION_CODES#M} or before,
+ * this will trigger entering a new password for the parent of the profile.
+ * For all other cases it will trigger entering a new password for the user
+ * or profile it is launched from.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SET_NEW_PASSWORD
= "android.app.action.SET_NEW_PASSWORD";
/**
+ * Activity action: have the user enter a new password for the parent profile.
+ * If the intent is launched from within a managed profile, this will trigger
+ * entering a new password for the parent of the profile. In all other cases
+ * the behaviour is identical to {@link #ACTION_SET_NEW_PASSWORD}.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SET_NEW_PARENT_PROFILE_PASSWORD
+ = "android.app.action.SET_NEW_PARENT_PROFILE_PASSWORD";
+
+ /**
* Flag used by {@link #addCrossProfileIntentFilter} to allow activities in
* the parent profile to access intents sent from the managed profile.
* That is, when an app in the managed profile calls
@@ -941,6 +972,22 @@
}
/**
+ * Returns true if the Profile Challenge is available to use for the given profile user.
+ *
+ * @hide
+ */
+ public boolean isSeparateProfileChallengeAllowed(int userHandle) {
+ if (mService != null) {
+ try {
+ return mService.isSeparateProfileChallengeAllowed(userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ return false;
+ }
+
+ /**
* Constant for {@link #setPasswordQuality}: the policy has no requirements
* for the password. Note that quality constants are ordered so that higher
* values are more restrictive.
@@ -2420,6 +2467,53 @@
}
/**
+ * Called by a device or profile owner to configure an always-on VPN connection through a
+ * specific application for the current user.
+ * This connection is automatically granted and persisted after a reboot.
+ *
+ * <p>The designated package should declare a {@link android.net.VpnService} in its
+ * manifest guarded by {@link android.Manifest.permission#BIND_VPN_SERVICE},
+ * otherwise the call will fail.
+ *
+ * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
+ * to remove an existing always-on VPN configuration.
+ *
+ * @return {@code true} if the package is set as always-on VPN controller;
+ * {@code false} otherwise.
+ */
+ public boolean setAlwaysOnVpnPackage(@NonNull ComponentName admin,
+ @Nullable String vpnPackage) {
+ if (mService != null) {
+ try {
+ return mService.setAlwaysOnVpnPackage(admin, vpnPackage);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by a device or profile owner to read the name of the package administering an
+ * always-on VPN connection for the current user.
+ * If there is no such package, or the always-on VPN is provided by the system instead
+ * of by an application, {@code null} will be returned.
+ *
+ * @return Package name of VPN controller responsible for always-on VPN,
+ * or {@code null} if none is set.
+ */
+ public String getAlwaysOnVpnPackage(@NonNull ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getAlwaysOnVpnPackage(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ return null;
+ }
+
+ /**
* Called by an application that is administering the device to disable all cameras
* on the device, for this user. After setting this, no applications running as this user
* will be able to access any cameras on the device.
@@ -3540,6 +3634,66 @@
}
/**
+ * Called by a profile owner of a managed profile to set whether contacts search from
+ * the managed profile will be shown in the parent profile, for incoming calls.
+ *
+ * <p>The calling device admin must be a profile owner. If it is not, a
+ * security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param disabled If true contacts search in the managed profile is not displayed.
+ */
+ public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
+ boolean disabled) {
+ if (mService != null) {
+ try {
+ mService.setCrossProfileContactsSearchDisabled(admin, disabled);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ }
+
+ /**
+ * Called by a profile owner of a managed profile to determine whether or not contacts search
+ * has been disabled.
+ *
+ * <p>The calling device admin must be a profile owner. If it is not, a
+ * security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ */
+ public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getCrossProfileContactsSearchDisabled(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Determine whether or not contacts search has been disabled.
+ *
+ * @param userHandle The user for whom to check the contacts search permission
+ * @hide
+ */
+ public boolean getCrossProfileContactsSearchDisabled(@NonNull UserHandle userHandle) {
+ if (mService != null) {
+ try {
+ return mService
+ .getCrossProfileContactsSearchDisabledForUser(userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
+ }
+ }
+ return false;
+ }
+
+ /**
* Start Quick Contact on the managed profile for the user, if the policy allows.
* @hide
*/
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 754cb43..7771440 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -144,6 +144,9 @@
void setCertInstallerPackage(in ComponentName who, String installerPackage);
String getCertInstallerPackage(in ComponentName who);
+ boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage);
+ String getAlwaysOnVpnPackage(in ComponentName who);
+
void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
@@ -202,6 +205,9 @@
void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
boolean getCrossProfileCallerIdDisabled(in ComponentName who);
boolean getCrossProfileCallerIdDisabledForUser(int userId);
+ void setCrossProfileContactsSearchDisabled(in ComponentName who, boolean disabled);
+ boolean getCrossProfileContactsSearchDisabled(in ComponentName who);
+ boolean getCrossProfileContactsSearchDisabledForUser(int userId);
void startManagedQuickContact(String lookupKey, long contactId, long directoryId, in Intent originalIntent);
void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled);
@@ -253,4 +259,6 @@
String getShortSupportMessageForUser(in ComponentName admin, int userHandle);
String getLongSupportMessageForUser(in ComponentName admin, int userHandle);
+
+ boolean isSeparateProfileChallengeAllowed(int userHandle);
}
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index b899710..9ad35d4 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -91,6 +91,7 @@
private final long flexMillis;
private final long initialBackoffMillis;
private final int backoffPolicy;
+ private final int priority;
/**
* Unique job id associated with this class. This is assigned to your job by the scheduler.
@@ -113,6 +114,11 @@
return service;
}
+ /** @hide */
+ public int getPriority() {
+ return priority;
+ }
+
/**
* Whether this job needs the device to be plugged in.
*/
@@ -237,6 +243,7 @@
backoffPolicy = in.readInt();
hasEarlyConstraint = in.readInt() == 1;
hasLateConstraint = in.readInt() == 1;
+ priority = in.readInt();
}
private JobInfo(JobInfo.Builder b) {
@@ -256,6 +263,7 @@
backoffPolicy = b.mBackoffPolicy;
hasEarlyConstraint = b.mHasEarlyConstraint;
hasLateConstraint = b.mHasLateConstraint;
+ priority = b.mPriority;
}
@Override
@@ -281,6 +289,7 @@
out.writeInt(backoffPolicy);
out.writeInt(hasEarlyConstraint ? 1 : 0);
out.writeInt(hasLateConstraint ? 1 : 0);
+ out.writeInt(priority);
}
public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
@@ -305,6 +314,7 @@
private int mJobId;
private PersistableBundle mExtras = PersistableBundle.EMPTY;
private ComponentName mJobService;
+ private int mPriority;
// Requirements.
private boolean mRequiresCharging;
private boolean mRequiresDeviceIdle;
@@ -338,6 +348,14 @@
}
/**
+ * @hide
+ */
+ public Builder setPriority(int priority) {
+ mPriority = priority;
+ return this;
+ }
+
+ /**
* Set optional extras. This is persisted, so we only allow primitive types.
* @param extras Bundle containing extras you want the scheduler to hold on to for you.
*/
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index 7ee39f5..a0a60e8 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -28,10 +28,22 @@
*/
public class JobParameters implements Parcelable {
+ /** @hide */
+ public static final int REASON_CANCELED = 0;
+ /** @hide */
+ public static final int REASON_CONSTRAINTS_NOT_SATISFIED = 1;
+ /** @hide */
+ public static final int REASON_PREEMPT = 2;
+ /** @hide */
+ public static final int REASON_TIMEOUT = 3;
+ /** @hide */
+ public static final int REASON_DEVICE_IDLE = 4;
+
private final int jobId;
private final PersistableBundle extras;
private final IBinder callback;
private final boolean overrideDeadlineExpired;
+ private int stopReason; // Default value of stopReason is REASON_CANCELED
/** @hide */
public JobParameters(IBinder callback, int jobId, PersistableBundle extras,
@@ -50,6 +62,14 @@
}
/**
+ * Reason onStopJob() was called on this job.
+ * @hide
+ */
+ public int getStopReason() {
+ return stopReason;
+ }
+
+ /**
* @return The extras you passed in when constructing this job with
* {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
* never be null. If you did not set any extras this will be an empty bundle.
@@ -78,6 +98,12 @@
extras = in.readPersistableBundle();
callback = in.readStrongBinder();
overrideDeadlineExpired = in.readInt() == 1;
+ stopReason = in.readInt();
+ }
+
+ /** @hide */
+ public void setStopReason(int reason) {
+ stopReason = reason;
}
@Override
@@ -91,6 +117,7 @@
dest.writePersistableBundle(extras);
dest.writeStrongBinder(callback);
dest.writeInt(overrideDeadlineExpired ? 1 : 0);
+ dest.writeInt(stopReason);
}
public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index ee591d3..88ba874 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -16,7 +16,9 @@
package android.app.trust;
+import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -54,9 +56,12 @@
* Changes the lock status for the given user. This is only applicable to Managed Profiles,
* other users should be handled by Keyguard.
*
+ * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
* @param userId The id for the user to be locked/unlocked.
* @param locked The value for that user's locked state.
*/
+ @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
public void setDeviceLockedForUser(int userId, boolean locked) {
try {
mService.setDeviceLockedForUser(userId, locked);
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
old mode 100644
new mode 100755
index 2e27345..74302f2
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -371,6 +371,89 @@
}
/**
+ * Set priority of the profile
+ *
+ * <p> The device should already be paired.
+ * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
+ * {@link #PRIORITY_OFF},
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @param device Paired bluetooth device
+ * @param priority
+ * @return true if priority is set, false on error
+ * @hide
+ */
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) log("setPriority(" + device + ", " + priority + ")");
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ if (priority != BluetoothProfile.PRIORITY_OFF &&
+ priority != BluetoothProfile.PRIORITY_ON){
+ return false;
+ }
+ try {
+ return mService.setPriority(device, priority);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Get the priority of the profile.
+ *
+ * <p> The priority can be any of:
+ * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+ * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Bluetooth device
+ * @return priority of the device
+ * @hide
+ */
+ public int getPriority(BluetoothDevice device) {
+ if (VDBG) log("getPriority(" + device + ")");
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ try {
+ return mService.getPriority(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.PRIORITY_OFF;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.PRIORITY_OFF;
+ }
+
+ /**
+ * Check if A2DP profile is streaming music.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device BluetoothDevice device
+ */
+ public boolean isA2dpPlaying(BluetoothDevice device) {
+ if (mService != null && isEnabled()
+ && isValidDevice(device)) {
+ try {
+ return mService.isA2dpPlaying(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
* Helper for converting a state to a string.
*
* For debug use only - strings are not internationalized.
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index b53a8fc..444e429 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -20,6 +20,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -28,8 +30,8 @@
import java.util.List;
/**
- * This class provides the public APIs to control the Bluetooth AVRCP Controller
- * profile.
+ * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
+ * supports player information, playback support and track metadata.
*
*<p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
* Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
@@ -39,7 +41,7 @@
*/
public final class BluetoothAvrcpController implements BluetoothProfile {
private static final String TAG = "BluetoothAvrcpController";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
private static final boolean VDBG = false;
/**
@@ -61,7 +63,63 @@
* receive.
*/
public static final String ACTION_CONNECTION_STATE_CHANGED =
- "android.bluetooth.acrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
+ "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
+
+ /**
+ * Intent used to broadcast the change in metadata state of playing track on the AVRCP
+ * AG.
+ *
+ * <p>This intent will have the two extras:
+ * <ul>
+ * <li> {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.</li>
+ * <li> {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback
+ * state. </li>
+ * </ul>
+ */
+ public static final String ACTION_TRACK_EVENT =
+ "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT";
+
+
+ /**
+ * Intent used to broadcast the change in player application setting state on AVRCP AG.
+ *
+ * <p>This intent will have the following extras:
+ * <ul>
+ * <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
+ * most recent player setting. </li>
+ * </ul>
+ */
+ public static final String ACTION_PLAYER_SETTING =
+ "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";
+
+ public static final String EXTRA_METADATA =
+ "android.bluetooth.avrcp-controller.profile.extra.METADATA";
+
+ public static final String EXTRA_PLAYBACK =
+ "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK";
+
+ public static final String EXTRA_PLAYER_SETTING =
+ "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";
+
+ /*
+ * KeyCoded for Pass Through Commands
+ */
+ public static final int PASS_THRU_CMD_ID_PLAY = 0x44;
+ public static final int PASS_THRU_CMD_ID_PAUSE = 0x46;
+ public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41;
+ public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42;
+ public static final int PASS_THRU_CMD_ID_STOP = 0x45;
+ public static final int PASS_THRU_CMD_ID_FF = 0x49;
+ public static final int PASS_THRU_CMD_ID_REWIND = 0x48;
+ public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B;
+ public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C;
+ /* Key State Variables */
+ public static final int KEY_STATE_PRESSED = 0;
+ public static final int KEY_STATE_RELEASED = 1;
+ /* Group Navigation Key Codes */
+ public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00;
+ public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01;
+
private Context mContext;
private ServiceListener mServiceListener;
@@ -69,33 +127,33 @@
private BluetoothAdapter mAdapter;
final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG,"Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG,"",re);
- }
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ if (VDBG) Log.d(TAG,"Unbinding service...");
+ synchronized (mConnection) {
+ try {
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG,"",re);
}
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG,"Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG,"",re);
+ }
+ } else {
+ synchronized (mConnection) {
+ try {
+ if (mService == null) {
+ if (VDBG) Log.d(TAG,"Binding service...");
+ doBind();
}
+ } catch (Exception re) {
+ Log.e(TAG,"",re);
}
}
}
- };
+ }
+ };
/**
* Create a BluetoothAvrcpController proxy object for interacting with the local
@@ -223,6 +281,104 @@
if (mService == null) Log.w(TAG, "Proxy not attached to service");
}
+ /**
+ * Gets the player application settings.
+ *
+ * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
+ */
+ public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "getPlayerSettings");
+ BluetoothAvrcpPlayerSettings settings = null;
+ if (mService != null && isEnabled()) {
+ try {
+ settings = mService.getPlayerSettings(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
+ return null;
+ }
+ }
+ return settings;
+ }
+
+ /**
+ * Gets the metadata for the current track.
+ *
+ * This should be usually called when application UI needs to be updated, eg. when the track
+ * changes or immediately after connecting and getting the current state.
+ * @return the {@link MediaMetadata} or {@link null} if there is an error.
+ */
+ public MediaMetadata getMetadata(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "getMetadata");
+ MediaMetadata metadata = null;
+ if (mService != null && isEnabled()) {
+ try {
+ metadata = mService.getMetadata(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
+ return null;
+ }
+ }
+ return metadata;
+ }
+
+ /**
+ * Gets the playback state for current track.
+ *
+ * When the application is first connecting it can use current track state to get playback info.
+ * For all further updates it should listen to notifications.
+ * @return the {@link PlaybackState} or {@link null} if there is an error.
+ */
+ public PlaybackState getPlaybackState(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "getPlaybackState");
+ PlaybackState playbackState = null;
+ if (mService != null && isEnabled()) {
+ try {
+ playbackState = mService.getPlaybackState(device);
+ } catch (RemoteException e) {
+ Log.e(TAG,
+ "Error talking to BT service in getPlaybackState() " + e);
+ return null;
+ }
+ }
+ return playbackState;
+ }
+
+ /**
+ * Sets the player app setting for current player.
+ * returns true in case setting is supported by remote, false otherwise
+ */
+ public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
+ if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.setPlayerApplicationSetting(plAppSetting);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
+ return false;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /*
+ * Send Group Navigation Command to Remote.
+ * possible keycode values: next_grp, previous_grp defined above
+ */
+ public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
+ Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState);
+ if (mService != null && isEnabled()) {
+ try {
+ mService.sendGroupNavigationCmd(device, keyCode, keyState);
+ return;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
+ return;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ }
+
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl
new file mode 100644
index 0000000..590fd63
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+parcelable BluetoothAvrcpPlayerSettings;
diff --git a/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java
new file mode 100644
index 0000000..927cb56
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAvrcpPlayerSettings.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class used to identify settings associated with the player on AG.
+ *
+ * {@hide}
+ */
+public final class BluetoothAvrcpPlayerSettings implements Parcelable {
+ public static final String TAG = "BluetoothAvrcpPlayerSettings";
+
+ /**
+ * Equalizer setting.
+ */
+ public static final int SETTING_EQUALIZER = 0x01;
+
+ /**
+ * Repeat setting.
+ */
+ public static final int SETTING_REPEAT = 0x02;
+
+ /**
+ * Shuffle setting.
+ */
+ public static final int SETTING_SHUFFLE = 0x04;
+
+ /**
+ * Scan mode setting.
+ */
+ public static final int SETTING_SCAN = 0x08;
+
+ /**
+ * Invalid state.
+ *
+ * Used for returning error codes.
+ */
+ public static final int STATE_INVALID = -1;
+
+ /**
+ * OFF state.
+ *
+ * Denotes a general OFF state. Applies to all settings.
+ */
+ public static final int STATE_OFF = 0x00;
+
+ /**
+ * ON state.
+ *
+ * Applies to {@link SETTING_EQUALIZER}.
+ */
+ public static final int STATE_ON = 0x01;
+
+ /**
+ * Single track repeat.
+ *
+ * Applies only to {@link SETTING_REPEAT}.
+ */
+ public static final int STATE_SINGLE_TRACK = 0x02;
+
+ /**
+ * All track repeat/shuffle.
+ *
+ * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}.
+ */
+ public static final int STATE_ALL_TRACK = 0x03;
+
+ /**
+ * Group repeat/shuffle.
+ *
+ * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}.
+ */
+ public static final int STATE_GROUP = 0x04;
+
+ /**
+ * List of supported settings ORed.
+ */
+ private int mSettings;
+
+ /**
+ * Hash map of current capability values.
+ */
+ private Map<Integer, Integer> mSettingsValue = new HashMap<Integer, Integer>();
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mSettings);
+ out.writeInt(mSettingsValue.size());
+ for (int k : mSettingsValue.keySet()) {
+ out.writeInt(k);
+ out.writeInt(mSettingsValue.get(k));
+ }
+ }
+
+ public static final Parcelable.Creator<BluetoothAvrcpPlayerSettings> CREATOR
+ = new Parcelable.Creator<BluetoothAvrcpPlayerSettings>() {
+ public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) {
+ return new BluetoothAvrcpPlayerSettings(in);
+ }
+
+ public BluetoothAvrcpPlayerSettings[] newArray(int size) {
+ return new BluetoothAvrcpPlayerSettings[size];
+ }
+ };
+
+ private BluetoothAvrcpPlayerSettings(Parcel in) {
+ mSettings = in.readInt();
+ int numSettings = in.readInt();
+ for (int i = 0; i < numSettings; i++) {
+ mSettingsValue.put(in.readInt(), in.readInt());
+ }
+ }
+
+ /**
+ * Create a new player settings object.
+ *
+ * @param settings a ORed value of SETTINGS_* defined above.
+ */
+ public BluetoothAvrcpPlayerSettings(int settings) {
+ mSettings = settings;
+ }
+
+ /**
+ * Get the supported settings.
+ *
+ * @return int ORed value of supported settings.
+ */
+ public int getSettings() {
+ return mSettings;
+ }
+
+ /**
+ * Add a setting value.
+ *
+ * The setting must be part of possible settings in {@link getSettings()}.
+ * @param setting setting config.
+ * @param value value for the setting.
+ * @throws IllegalStateException if the setting is not supported.
+ */
+ public void addSettingValue(int setting, int value) {
+ if ((setting & mSettings) == 0) {
+ Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
+ throw new IllegalStateException("Setting not supported: " + setting);
+ }
+ mSettingsValue.put(setting, value);
+ }
+
+ /**
+ * Get a setting value.
+ *
+ * The setting must be part of possible settings in {@link getSettings()}.
+ * @param setting setting config.
+ * @return value value for the setting.
+ * @throws IllegalStateException if the setting is not supported.
+ */
+ public int getSettingValue(int setting) {
+ if ((setting & mSettings) == 0) {
+ Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
+ throw new IllegalStateException("Setting not supported: " + setting);
+ }
+ Integer i = mSettingsValue.get(setting);
+ if (i == null) return -1;
+ return i;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
old mode 100644
new mode 100755
index 54bf4af..4a38287
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -283,6 +283,8 @@
public static final int PROFILE_PANU = 4;
/** @hide */
public static final int PROFILE_NAP = 5;
+ /** @hide */
+ public static final int PROFILE_A2DP_SINK = 6;
/**
* Check class bits for possible bluetooth profile support.
@@ -310,6 +312,21 @@
default:
return false;
}
+ } else if (profile == PROFILE_A2DP_SINK) {
+ if (hasService(Service.CAPTURE)) {
+ return true;
+ }
+ // By the A2DP spec, srcs must indicate the CAPTURE service.
+ // However if some device that do not, we try to
+ // match on some other class bits.
+ switch (getDeviceClass()) {
+ case Device.AUDIO_VIDEO_HIFI_AUDIO:
+ case Device.AUDIO_VIDEO_SET_TOP_BOX:
+ case Device.AUDIO_VIDEO_VCR :
+ return true;
+ default:
+ return false;
+ }
} else if (profile == PROFILE_HEADSET) {
// The render service class is required by the spec for HFP, so is a
// pretty good signal
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index cd5c205..f43fb30 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -879,18 +879,16 @@
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
- * @param hash - Simple Secure pairing hash
- * @param randomizer - The random key obtained using OOB
+ * @param transport - Transport to use
+ * @param oobData - Out Of Band data
* @return false on immediate error, true if bonding will begin
*
* @hide
*/
- public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) {
- //TODO(BT)
- /*
+ public boolean createBondOutOfBand(int transport, OobData oobData) {
try {
- return sService.createBondOutOfBand(this, hash, randomizer);
- } catch (RemoteException e) {Log.e(TAG, "", e);}*/
+ return sService.createBondOutOfBand(this, transport, oobData);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 66f3418..74cb0f6 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -20,6 +20,7 @@
import android.bluetooth.IBluetoothStateChangeCallback;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.OobData;
import android.os.ParcelUuid;
import android.os.ParcelFileDescriptor;
@@ -56,6 +57,7 @@
BluetoothDevice[] getBondedDevices();
boolean createBond(in BluetoothDevice device, in int transport);
+ boolean createBondOutOfBand(in BluetoothDevice device, in int transport, in OobData oobData);
boolean cancelBondProcess(in BluetoothDevice device);
boolean removeBond(in BluetoothDevice device);
int getBondState(in BluetoothDevice device);
diff --git a/core/java/android/bluetooth/IBluetoothA2dpSink.aidl b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl
old mode 100644
new mode 100755
index b7c6476..d1458246
--- a/core/java/android/bluetooth/IBluetoothA2dpSink.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl
@@ -31,4 +31,7 @@
List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
int getConnectionState(in BluetoothDevice device);
BluetoothAudioConfig getAudioConfig(in BluetoothDevice device);
+ boolean setPriority(in BluetoothDevice device, int priority);
+ int getPriority(in BluetoothDevice device);
+ boolean isA2dpPlaying(in BluetoothDevice device);
}
diff --git a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
index f917a50..f1288d0 100644
--- a/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
+++ b/core/java/android/bluetooth/IBluetoothAvrcpController.aidl
@@ -16,7 +16,10 @@
package android.bluetooth;
+import android.bluetooth.BluetoothAvrcpPlayerSettings;
import android.bluetooth.BluetoothDevice;
+import android.media.MediaMetadata;
+import android.media.session.PlaybackState;
/**
* APIs for Bluetooth AVRCP controller service
@@ -28,4 +31,9 @@
List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
int getConnectionState(in BluetoothDevice device);
void sendPassThroughCmd(in BluetoothDevice device, int keyCode, int keyState);
+ BluetoothAvrcpPlayerSettings getPlayerSettings(in BluetoothDevice device);
+ MediaMetadata getMetadata(in BluetoothDevice device);
+ PlaybackState getPlaybackState(in BluetoothDevice device);
+ boolean setPlayerApplicationSetting(in BluetoothAvrcpPlayerSettings plAppSetting);
+ void sendGroupNavigationCmd(in BluetoothDevice device, int keyCode, int keyState);
}
diff --git a/core/java/android/bluetooth/OobData.aidl b/core/java/android/bluetooth/OobData.aidl
new file mode 100644
index 0000000..d831c64
--- /dev/null
+++ b/core/java/android/bluetooth/OobData.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.bluetooth;
+
+parcelable OobData;
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
new file mode 100644
index 0000000..01f72ef
--- /dev/null
+++ b/core/java/android/bluetooth/OobData.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import android.util.Log;
+
+/**
+ * Out Of Band Data for Bluetooth device.
+ */
+public class OobData implements Parcelable {
+ private byte[] securityManagerTk;
+
+ public byte[] getSecurityManagerTk() {
+ return securityManagerTk;
+ }
+
+ public void setSecurityManagerTk(byte[] securityManagerTk) {
+ this.securityManagerTk = securityManagerTk;
+ }
+
+ public OobData() { }
+
+ private OobData(Parcel in) {
+ securityManagerTk = in.createByteArray();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeByteArray(securityManagerTk);
+ }
+
+ public static final Parcelable.Creator<OobData> CREATOR
+ = new Parcelable.Creator<OobData>() {
+ public OobData createFromParcel(Parcel in) {
+ return new OobData(in);
+ }
+
+ public OobData[] newArray(int size) {
+ return new OobData[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 84f6f3d..fb27910 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -329,6 +329,15 @@
public static final int BIND_NOT_VISIBLE = 0x40000000;
/**
+ * Flag for {@link #bindService}: The service being bound is an
+ * {@link android.R.attr#isolatedProcess isolated},
+ * {@link android.R.attr#externalService external} service. This binds the service into the
+ * calling application's package, rather than the package in which the service is declared.
+ * @hide
+ */
+ public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
+
+ /**
* Returns an AssetManager instance for the application's package.
* <p>
* <strong>Note:</strong> Implementations of this method should return
@@ -2550,6 +2559,7 @@
NETWORK_STATS_SERVICE,
//@hide: NETWORK_POLICY_SERVICE,
WIFI_SERVICE,
+ WIFI_NAN_SERVICE,
WIFI_PASSPOINT_SERVICE,
WIFI_P2P_SERVICE,
WIFI_SCANNING_SERVICE,
@@ -3012,6 +3022,17 @@
public static final String WIFI_P2P_SERVICE = "wifip2p";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.net.wifi.nan.WifiNanManager} for handling management of
+ * Wi-Fi NAN discovery and connections.
+ *
+ * @see #getSystemService
+ * @see android.net.wifi.nan.WifiNanManager
+ * @hide PROPOSED_NAN_API
+ */
+ public static final String WIFI_NAN_SERVICE = "wifinan";
+
+ /**
* Use with {@link #getSystemService} to retrieve a {@link
* android.net.wifi.WifiScanner} for scanning the wifi universe
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 35cd2be..e7f886d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3034,6 +3034,24 @@
public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
/**
+ * Broadcast Action: Sent when media resource is granted.
+ * <p>
+ * {@link #EXTRA_PACKAGES} specifies the packages on the process holding the media resource
+ * granted.
+ * </p>
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ * </p>
+ * <p class="note">
+ * This requires {@link android.Manifest.permission#RECEIVE_MEDIA_RESOURCE_USAGE} permission.
+ * </p>
+ *
+ * @hide
+ */
+ public static final String ACTION_MEDIA_RESOURCE_GRANTED =
+ "android.intent.action.MEDIA_RESOURCE_GRANTED";
+
+ /**
* Activity Action: Allow the user to select and return one or more existing
* documents. When invoked, the system will display the various
* {@link DocumentsProvider} instances installed on the device, letting the
@@ -4077,6 +4095,34 @@
* Optional boolean extra indicating whether quiet mode has been switched on or off.
*/
public static final String EXTRA_QUIET_MODE = "android.intent.extra.QUIET_MODE";
+
+ /**
+ * Used as an int extra field in {@link #ACTION_MEDIA_RESOURCE_GRANTED}
+ * intents to specify the resource type granted. Possible values are
+ * {@link #EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC} or
+ * {@link #EXTRA_MEDIA_RESOURCE_TYPE_AUDIO_CODEC}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_MEDIA_RESOURCE_TYPE =
+ "android.intent.extra.MEDIA_RESOURCE_TYPE";
+
+ /**
+ * Used as an int value for {@link #EXTRA_MEDIA_RESOURCE_TYPE}
+ * to represent that a video codec is allowed to use.
+ *
+ * @hide
+ */
+ public static final int EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC = 0;
+
+ /**
+ * Used as an int value for {@link #EXTRA_MEDIA_RESOURCE_TYPE}
+ * to represent that a audio codec is allowed to use.
+ *
+ * @hide
+ */
+ public static final int EXTRA_MEDIA_RESOURCE_TYPE_AUDIO_CODEC = 1;
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 38abac7..ba4d14c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1902,6 +1902,16 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports Wi-Fi Aware (NAN)
+ * networking.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_WIFI_NAN = "android.hardware.wifi.nan";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: This is a device dedicated to showing UI
* on a vehicle headunit. A headunit here is defined to be inside a
* vehicle that may or may not be moving. A headunit uses either a
@@ -4413,6 +4423,15 @@
final PackageParser parser = new PackageParser();
final File apkFile = new File(archiveFilePath);
try {
+ if ((flags & (MATCH_ENCRYPTION_UNAWARE | MATCH_ENCRYPTION_AWARE)) != 0) {
+ // Caller expressed an explicit opinion about what encryption
+ // aware/unaware components they want to see, so fall through and
+ // give them what they want
+ } else {
+ // Caller expressed no opinion, so match everything
+ flags |= MATCH_ENCRYPTION_AWARE_AND_UNAWARE;
+ }
+
PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
if ((flags & GET_SIGNATURES) != 0) {
parser.collectCertificates(pkg, 0);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1349662..a6fec9f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -51,6 +51,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.TypedValue;
+import android.util.apk.ApkSignatureSchemeV2Verifier;
import android.util.jar.StrictJarFile;
import android.view.Gravity;
@@ -463,92 +464,60 @@
p.featureGroups.toArray(pi.featureGroups);
}
}
- if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
- int N = p.activities.size();
+ if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+ final int N = p.activities.size();
if (N > 0) {
- if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.activities = new ActivityInfo[N];
- } else {
- int num = 0;
- for (int i=0; i<N; i++) {
- if (p.activities.get(i).info.enabled) num++;
- }
- pi.activities = new ActivityInfo[num];
- }
- for (int i=0, j=0; i<N; i++) {
- final Activity activity = p.activities.get(i);
- if (activity.info.enabled
- || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
- state, userId);
+ int num = 0;
+ final ActivityInfo[] res = new ActivityInfo[N];
+ for (int i = 0; i < N; i++) {
+ final Activity a = p.activities.get(i);
+ if (state.isMatch(a.info, flags)) {
+ res[num++] = generateActivityInfo(a, flags, state, userId);
}
}
+ pi.activities = ArrayUtils.trimToSize(res, num);
}
}
- if ((flags&PackageManager.GET_RECEIVERS) != 0) {
- int N = p.receivers.size();
+ if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+ final int N = p.receivers.size();
if (N > 0) {
- if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.receivers = new ActivityInfo[N];
- } else {
- int num = 0;
- for (int i=0; i<N; i++) {
- if (p.receivers.get(i).info.enabled) num++;
- }
- pi.receivers = new ActivityInfo[num];
- }
- for (int i=0, j=0; i<N; i++) {
- final Activity activity = p.receivers.get(i);
- if (activity.info.enabled
- || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
- state, userId);
+ int num = 0;
+ final ActivityInfo[] res = new ActivityInfo[N];
+ for (int i = 0; i < N; i++) {
+ final Activity a = p.receivers.get(i);
+ if (state.isMatch(a.info, flags)) {
+ res[num++] = generateActivityInfo(a, flags, state, userId);
}
}
+ pi.receivers = ArrayUtils.trimToSize(res, num);
}
}
- if ((flags&PackageManager.GET_SERVICES) != 0) {
- int N = p.services.size();
+ if ((flags & PackageManager.GET_SERVICES) != 0) {
+ final int N = p.services.size();
if (N > 0) {
- if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.services = new ServiceInfo[N];
- } else {
- int num = 0;
- for (int i=0; i<N; i++) {
- if (p.services.get(i).info.enabled) num++;
- }
- pi.services = new ServiceInfo[num];
- }
- for (int i=0, j=0; i<N; i++) {
- final Service service = p.services.get(i);
- if (service.info.enabled
- || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.services[j++] = generateServiceInfo(p.services.get(i), flags,
- state, userId);
+ int num = 0;
+ final ServiceInfo[] res = new ServiceInfo[N];
+ for (int i = 0; i < N; i++) {
+ final Service s = p.services.get(i);
+ if (state.isMatch(s.info, flags)) {
+ res[num++] = generateServiceInfo(s, flags, state, userId);
}
}
+ pi.services = ArrayUtils.trimToSize(res, num);
}
}
- if ((flags&PackageManager.GET_PROVIDERS) != 0) {
- int N = p.providers.size();
+ if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+ final int N = p.providers.size();
if (N > 0) {
- if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.providers = new ProviderInfo[N];
- } else {
- int num = 0;
- for (int i=0; i<N; i++) {
- if (p.providers.get(i).info.enabled) num++;
- }
- pi.providers = new ProviderInfo[num];
- }
- for (int i=0, j=0; i<N; i++) {
- final Provider provider = p.providers.get(i);
- if (provider.info.enabled
- || (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags,
- state, userId);
+ int num = 0;
+ final ProviderInfo[] res = new ProviderInfo[N];
+ for (int i = 0; i < N; i++) {
+ final Provider pr = p.providers.get(i);
+ if (state.isMatch(pr.info, flags)) {
+ res[num++] = generateProviderInfo(pr, flags, state, userId);
}
}
+ pi.providers = ArrayUtils.trimToSize(res, num);
}
}
if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
@@ -1054,11 +1023,56 @@
final boolean requireCode = ((parseFlags & PARSE_ENFORCE_CODE) != 0) && hasCode;
final String apkPath = apkFile.getAbsolutePath();
+ // Try to verify the APK using APK Signature Scheme v2.
+ boolean verified = false;
+ {
+ Certificate[][] allSignersCerts = null;
+ Signature[] signatures = null;
+ try {
+ allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
+ signatures = convertToSignatures(allSignersCerts);
+ // APK verified using APK Signature Scheme v2.
+ verified = true;
+ } catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) {
+ // No APK Signature Scheme v2 signature found
+ } catch (Exception e) {
+ // APK Signature Scheme v2 signature was found but did not verify
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "Failed to collect certificates from " + apkPath
+ + " using APK Signature Scheme v2",
+ e);
+ }
+
+ if (verified) {
+ if (pkg.mCertificates == null) {
+ pkg.mCertificates = allSignersCerts;
+ pkg.mSignatures = signatures;
+ pkg.mSigningKeys = new ArraySet<>(allSignersCerts.length);
+ for (int i = 0; i < allSignersCerts.length; i++) {
+ Certificate[] signerCerts = allSignersCerts[i];
+ Certificate signerCert = signerCerts[0];
+ pkg.mSigningKeys.add(signerCert.getPublicKey());
+ }
+ } else {
+ if (!Signature.areExactMatch(pkg.mSignatures, signatures)) {
+ throw new PackageParserException(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ apkPath + " has mismatched certificates");
+ }
+ }
+ // Not yet done, because we need to confirm that AndroidManifest.xml exists and,
+ // if requested, that classes.dex exists.
+ }
+ }
+
boolean codeFound = false;
StrictJarFile jarFile = null;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
- jarFile = new StrictJarFile(apkPath);
+ jarFile = new StrictJarFile(
+ apkPath,
+ !verified // whether to verify JAR signature
+ );
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Always verify manifest, regardless of source
@@ -1068,6 +1082,16 @@
"Package " + apkPath + " has no manifest");
}
+ // Optimization: early termination when APK already verified
+ if (verified) {
+ if ((requireCode) && (jarFile.findEntry(BYTECODE_FILENAME) == null)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Package " + apkPath + " code is missing");
+ }
+ return;
+ }
+
+ // APK's integrity needs to be verified using JAR signature scheme.
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "buildVerifyList");
final List<ZipEntry> toVerify = new ArrayList<>();
toVerify.add(manifestEntry);
@@ -3918,6 +3942,11 @@
s.info.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS;
}
if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestService_externalService,
+ false)) {
+ s.info.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
+ }
+ if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestService_singleUser,
false)) {
s.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 91fdf7f..38e0044 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -17,9 +17,20 @@
package android.content.pm;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_AWARE;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import android.util.ArraySet;
+import com.android.internal.util.ArrayUtils;
+
/**
* Per-user state information about a package.
* @hide
@@ -58,12 +69,77 @@
hidden = o.hidden;
suspended = o.suspended;
lastDisableAppCaller = o.lastDisableAppCaller;
- disabledComponents = o.disabledComponents != null
- ? new ArraySet<>(o.disabledComponents) : null;
- enabledComponents = o.enabledComponents != null
- ? new ArraySet<>(o.enabledComponents) : null;
+ disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents);
+ enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
blockUninstall = o.blockUninstall;
domainVerificationStatus = o.domainVerificationStatus;
appLinkGeneration = o.appLinkGeneration;
}
+
+ /**
+ * Test if this package is installed.
+ */
+ public boolean isInstalled(int flags) {
+ return (this.installed && !this.hidden)
+ || (flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0;
+ }
+
+ /**
+ * Test if the given component is considered installed, enabled and a match
+ * for the given flags.
+ */
+ public boolean isMatch(ComponentInfo componentInfo, int flags) {
+ if (!isInstalled(flags)) return false;
+ if (!isEnabled(componentInfo, flags)) return false;
+
+ if ((flags & MATCH_SYSTEM_ONLY) != 0) {
+ if (!componentInfo.applicationInfo.isSystemApp()) {
+ return false;
+ }
+ }
+
+ final boolean matchesUnaware = ((flags & MATCH_ENCRYPTION_UNAWARE) != 0)
+ && !componentInfo.encryptionAware;
+ final boolean matchesAware = ((flags & MATCH_ENCRYPTION_AWARE) != 0)
+ && componentInfo.encryptionAware;
+ return matchesUnaware || matchesAware;
+ }
+
+ /**
+ * Test if the given component is considered enabled.
+ */
+ public boolean isEnabled(ComponentInfo componentInfo, int flags) {
+ if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
+ return true;
+ }
+
+ // First check if the overall package is disabled; if the package is
+ // enabled then fall through to check specific component
+ switch (this.enabled) {
+ case COMPONENT_ENABLED_STATE_DISABLED:
+ case COMPONENT_ENABLED_STATE_DISABLED_USER:
+ return false;
+ case COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+ if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) == 0) {
+ return false;
+ }
+ case COMPONENT_ENABLED_STATE_DEFAULT:
+ if (!componentInfo.applicationInfo.enabled) {
+ return false;
+ }
+ case COMPONENT_ENABLED_STATE_ENABLED:
+ break;
+ }
+
+ // Check if component has explicit state before falling through to
+ // the manifest default
+ if (ArrayUtils.contains(this.enabledComponents, componentInfo.name)) {
+ return true;
+ }
+ if (ArrayUtils.contains(this.disabledComponents, componentInfo.name)) {
+ return false;
+ }
+
+ return componentInfo.enabled;
+ }
}
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 74e5c2a..eecf0de 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -49,6 +49,14 @@
public static final int FLAG_ISOLATED_PROCESS = 0x0002;
/**
+ * Bit in {@link #flags}: If set, the service can be bound and run in the
+ * calling application's package, rather than the package in which it is
+ * declared. Set from {@link android.R.attr#externalService} attribute.
+ * @hide
+ */
+ public static final int FLAG_EXTERNAL_SERVICE = 0x0004;
+
+ /**
* Bit in {@link #flags}: If set, a single instance of the service will
* run for all users on the device. Set from the
* {@link android.R.attr#singleUser} attribute.
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index e875864..6935174 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -534,6 +534,24 @@
public static final String STRING_TYPE_WRIST_TILT_GESTURE = "android.sensor.wrist_tilt_gesture";
/**
+ * The current orientation of the device.
+ * <p>
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ *
+ * @hide Expected to be used internally for auto-rotate and speaker rotation.
+ *
+ */
+ public static final int TYPE_DEVICE_ORIENTATION = 27;
+
+ /**
+ * A constant string describing a device orientation sensor type.
+ *
+ * @hide
+ * @see #TYPE_DEVICE_ORIENTATION
+ */
+ public static final String STRING_TYPE_DEVICE_ORIENTATION = "android.sensor.device_orientation";
+
+ /**
* A constant describing all sensor types.
*/
public static final int TYPE_ALL = -1;
@@ -618,6 +636,7 @@
1, // SENSOR_TYPE_GLANCE_GESTURE
1, // SENSOR_TYPE_PICK_UP_GESTURE
1, // SENSOR_TYPE_WRIST_TILT_GESTURE
+ 1, // SENSOR_TYPE_DEVICE_ORIENTATION
};
/**
@@ -939,6 +958,9 @@
case TYPE_TEMPERATURE:
mStringType = STRING_TYPE_TEMPERATURE;
return true;
+ case TYPE_DEVICE_ORIENTATION:
+ mStringType = STRING_TYPE_DEVICE_ORIENTATION;
+ return true;
default:
return false;
}
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 906c2a19..9937b2c 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -483,6 +483,20 @@
* on it. In earlier versions, this used to be always 3 which has changed now. </p>
*
* @see GeomagneticField
+ *
+ * <h4> {@link android.hardware.Sensor#TYPE_DEVICE_ORIENTATION
+ * Sensor.TYPE_DEVICE_ORIENTATION}:</h4>
+ * The current device orientation will be available in values[0]. The only
+ * available values are:
+ * <ul>
+ * <li> 0: device is in default orientation (Y axis is vertical and points up)
+ * <li> 1: device is rotated 90 degrees counter-clockwise from default
+ * orientation (X axis is vertical and points up)
+ * <li> 2: device is rotated 180 degrees from default orientation (Y axis is
+ * vertical and points down)
+ * <li> 3: device is rotated 90 degrees clockwise from default orientation (X axis
+ * is vertical and points down)
+ * </ul>
*/
public final float[] values;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 6e02df1..c3a4954 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -89,7 +89,6 @@
new SparseArray<CaptureCallbackHolder>();
private int mRepeatingRequestId = REQUEST_ID_NONE;
- private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
// Map stream IDs to input/output configurations
private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
new SimpleEntry<>(REQUEST_ID_NONE, null);
@@ -916,11 +915,6 @@
int requestId = mRepeatingRequestId;
mRepeatingRequestId = REQUEST_ID_NONE;
- // Queue for deletion after in-flight requests finish
- if (mCaptureCallbackMap.get(requestId) != null) {
- mRepeatingRequestIdDeletedList.add(requestId);
- }
-
try {
LongParcelable lastFrameNumberRef = new LongParcelable();
mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f642f08..2826882 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1258,6 +1258,10 @@
*/
@CallSuper
public boolean onEvaluateInputViewShown() {
+ if (mSettingsObserver == null) {
+ Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here.");
+ return false;
+ }
if (mSettingsObserver.shouldShowImeWithHardKeyboard()) {
return true;
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index cabc6fa..c4f0847 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -17,8 +17,10 @@
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -685,6 +687,47 @@
}
/**
+ * Configures an always-on VPN connection through a specific application.
+ * This connection is automatically granted and persisted after a reboot.
+ *
+ * <p>The designated package should declare a {@link VpnService} in its
+ * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
+ * otherwise the call will fail.
+ *
+ * @param userId The identifier of the user to set an always-on VPN for.
+ * @param vpnPackage The package name for an installed VPN app on the device, or {@code null}
+ * to remove an existing always-on VPN configuration.
+
+ * @return {@code true} if the package is set as always-on VPN controller;
+ * {@code false} otherwise.
+ * @hide
+ */
+ public boolean setAlwaysOnVpnPackageForUser(int userId, @Nullable String vpnPackage) {
+ try {
+ return mService.setAlwaysOnVpnPackage(userId, vpnPackage);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the package name of the currently set always-on VPN application.
+ * If there is no always-on VPN set, or the VPN is provided by the system instead
+ * of by an app, {@code null} will be returned.
+ *
+ * @return Package name of VPN controller responsible for always-on VPN,
+ * or {@code null} if none is set.
+ * @hide
+ */
+ public String getAlwaysOnVpnPackageForUser(int userId) {
+ try {
+ return mService.getAlwaysOnVpnPackage(userId);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Returns details about the currently active default data network
* for a given uid. This is for internal use only to avoid spying
* other apps.
@@ -897,6 +940,24 @@
}
/**
+ * Gets the URL that should be used for resolving whether a captive portal is present.
+ * 1. This URL should respond with a 204 response to a GET request to indicate no captive
+ * portal is present.
+ * 2. This URL must be HTTP as redirect responses are used to find captive portal
+ * sign-in pages. Captive portals cannot respond to HTTPS requests with redirects.
+ *
+ * @hide
+ */
+ @SystemApi
+ public String getCaptivePortalServerUrl() {
+ try {
+ return mService.getCaptivePortalServerUrl();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Tells the underlying networking system that the caller wants to
* begin using the named feature. The interpretation of {@code feature}
* is completely up to each networking implementation.
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d4dd669..569468e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -117,6 +117,8 @@
VpnInfo[] getAllVpnInfo();
boolean updateLockdownVpn();
+ boolean setAlwaysOnVpnPackage(int userId, String packageName);
+ String getAlwaysOnVpnPackage(int userId);
int checkMobileProvisioning(int suggestedTimeOutMs);
@@ -165,4 +167,6 @@
in IBinder binder, String srcAddr, int srcPort, String dstAddr);
void stopKeepalive(in Network network, int slot);
+
+ String getCaptivePortalServerUrl();
}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index fdd34f5..e58744b 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -130,6 +130,9 @@
/** The dirty dalvik pages that have been swapped out. */
/** @hide We may want to expose this, eventually. */
public int dalvikSwappedOut;
+ /** The dirty dalvik pages that have been swapped out, proportional. */
+ /** @hide We may want to expose this, eventually. */
+ public int dalvikSwappedOutPss;
/** The proportional set size for the native heap. */
public int nativePss;
@@ -149,6 +152,9 @@
/** The dirty native pages that have been swapped out. */
/** @hide We may want to expose this, eventually. */
public int nativeSwappedOut;
+ /** The dirty native pages that have been swapped out, proportional. */
+ /** @hide We may want to expose this, eventually. */
+ public int nativeSwappedOutPss;
/** The proportional set size for everything else. */
public int otherPss;
@@ -168,6 +174,13 @@
/** The dirty pages used by anyting else that have been swapped out. */
/** @hide We may want to expose this, eventually. */
public int otherSwappedOut;
+ /** The dirty pages used by anyting else that have been swapped out, proportional. */
+ /** @hide We may want to expose this, eventually. */
+ public int otherSwappedOutPss;
+
+ /** Whether the kernel reports proportional swap usage */
+ /** @hide */
+ public boolean hasSwappedOutPss;
/** @hide */
public static final int HEAP_UNKNOWN = 0;
@@ -235,7 +248,7 @@
public static final int NUM_DVK_STATS = 8;
/** @hide */
- public static final int NUM_CATEGORIES = 7;
+ public static final int NUM_CATEGORIES = 8;
/** @hide */
public static final int offsetPss = 0;
@@ -251,6 +264,8 @@
public static final int offsetSharedClean = 5;
/** @hide */
public static final int offsetSwappedOut = 6;
+ /** @hide */
+ public static final int offsetSwappedOutPss = 7;
private int[] otherStats = new int[(NUM_OTHER_STATS+NUM_DVK_STATS)*NUM_CATEGORIES];
@@ -261,7 +276,7 @@
* Return total PSS memory usage in kB.
*/
public int getTotalPss() {
- return dalvikPss + nativePss + otherPss;
+ return dalvikPss + nativePss + otherPss + getTotalSwappedOutPss();
}
/**
@@ -274,7 +289,8 @@
}
/**
- * Return total PSS memory usage in kB.
+ * Return total PSS memory usage in kB mapping a file of one of the following extension:
+ * .so, .jar, .apk, .ttf, .dex, .odex, .oat, .art .
*/
public int getTotalSwappablePss() {
return dalvikSwappablePss + nativeSwappablePss + otherSwappablePss;
@@ -316,6 +332,14 @@
return dalvikSwappedOut + nativeSwappedOut + otherSwappedOut;
}
+ /**
+ * Return total swapped out memory in kB, proportional.
+ * @hide
+ */
+ public int getTotalSwappedOutPss() {
+ return dalvikSwappedOutPss + nativeSwappedOutPss + otherSwappedOutPss;
+ }
+
/** @hide */
public int getOtherPss(int which) {
return otherStats[which*NUM_CATEGORIES + offsetPss];
@@ -359,6 +383,11 @@
}
/** @hide */
+ public int getOtherSwappedOutPss(int which) {
+ return otherStats[which*NUM_CATEGORIES + offsetSwappedOutPss];
+ }
+
+ /** @hide */
public static String getOtherLabel(int which) {
switch (which) {
case OTHER_DALVIK_OTHER: return "Dalvik Other";
@@ -632,12 +661,24 @@
* know if the Swap memory is shared or private, so we don't know
* what to blame on the application and what on the system.
* For now, just lump all the Swap in one place.
+ * For kernels reporting SwapPss {@link #getSummaryTotalSwapPss()}
+ * will report the application proportional Swap.
* @hide
*/
public int getSummaryTotalSwap() {
return getTotalSwappedOut();
}
+ /**
+ * Total proportional Swap in KB.
+ * Notes:
+ * * Always 0 if {@link #hasSwappedOutPss} is false.
+ * @hide
+ */
+ public int getSummaryTotalSwapPss() {
+ return getTotalSwappedOutPss();
+ }
+
public int describeContents() {
return 0;
}
@@ -664,6 +705,8 @@
dest.writeInt(otherPrivateClean);
dest.writeInt(otherSharedClean);
dest.writeInt(otherSwappedOut);
+ dest.writeInt(hasSwappedOutPss ? 1 : 0);
+ dest.writeInt(otherSwappedOutPss);
dest.writeIntArray(otherStats);
}
@@ -689,6 +732,8 @@
otherPrivateClean = source.readInt();
otherSharedClean = source.readInt();
otherSwappedOut = source.readInt();
+ hasSwappedOutPss = source.readInt() != 0;
+ otherSwappedOutPss = source.readInt();
otherStats = source.createIntArray();
}
@@ -1563,11 +1608,12 @@
/**
* Retrieves the PSS memory used by the process as given by the
- * smaps. Optionally supply a long array of 1 entry to also
- * receive the uss of the process, and another array to also
- * retrieve the separate memtrack size. @hide
+ * smaps. Optionally supply a long array of 2 entries to also
+ * receive the Uss and SwapPss of the process, and another array to also
+ * retrieve the separate memtrack size.
+ * @hide
*/
- public static native long getPss(int pid, long[] outUss, long[] outMemtrack);
+ public static native long getPss(int pid, long[] outUssSwapPss, long[] outMemtrack);
/** @hide */
public static final int MEMINFO_TOTAL = 0;
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index 83efe80..ff9c0df 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -20,16 +20,17 @@
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
-import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
/**
* This class represents the unique id of a printer.
*/
public final class PrinterId implements Parcelable {
- private final ComponentName mServiceName;
+ private final @NonNull ComponentName mServiceName;
- private final String mLocalId;
+ private final @NonNull String mLocalId;
/**
* Creates a new instance.
@@ -39,14 +40,14 @@
*
* @hide
*/
- public PrinterId(ComponentName serviceName, String localId) {
+ public PrinterId(@NonNull ComponentName serviceName, @NonNull String localId) {
mServiceName = serviceName;
mLocalId = localId;
}
- private PrinterId(Parcel parcel) {
- mServiceName = parcel.readParcelable(null);
- mLocalId = parcel.readString();
+ private PrinterId(@NonNull Parcel parcel) {
+ mServiceName = Preconditions.checkNotNull((ComponentName) parcel.readParcelable(null));
+ mLocalId = Preconditions.checkNotNull(parcel.readString());
}
/**
@@ -56,7 +57,7 @@
*
* @hide
*/
- public ComponentName getServiceName() {
+ public @NonNull ComponentName getServiceName() {
return mServiceName;
}
@@ -93,14 +94,10 @@
return false;
}
PrinterId other = (PrinterId) object;
- if (mServiceName == null) {
- if (other.mServiceName != null) {
- return false;
- }
- } else if (!mServiceName.equals(other.mServiceName)) {
+ if (!mServiceName.equals(other.mServiceName)) {
return false;
}
- if (!TextUtils.equals(mLocalId, other.mLocalId)) {
+ if (!mLocalId.equals(other.mLocalId)) {
return false;
}
return true;
@@ -110,8 +107,7 @@
public int hashCode() {
final int prime = 31;
int hashCode = 1;
- hashCode = prime * hashCode + ((mServiceName != null)
- ? mServiceName.hashCode() : 1);
+ hashCode = prime * hashCode + mServiceName.hashCode();
hashCode = prime * hashCode + mLocalId.hashCode();
return hashCode;
}
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index afef9c0..0d2d9f4 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -33,6 +33,8 @@
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -43,6 +45,9 @@
* major components, printer properties such as name, id, status,
* description and printer capabilities which describe the various
* print modes a printer supports such as media sizes, margins, etc.
+ * <p>
+ * Once {@link PrinterInfo.Builder#build() built} the objects are immutable.
+ * </p>
*/
public final class PrinterInfo implements Parcelable {
@@ -62,60 +67,41 @@
/** Printer status: the printer is not available. */
public static final int STATUS_UNAVAILABLE = 3;
- private PrinterId mId;
+ private final @NonNull PrinterId mId;
/** Resource inside the printer's services's package to be used as an icon */
- private int mIconResourceId;
+ private final int mIconResourceId;
/** If a custom icon can be loaded for the printer */
- private boolean mHasCustomPrinterIcon;
+ private final boolean mHasCustomPrinterIcon;
/** The generation of the icon in the cache. */
- private int mCustomPrinterIconGen;
+ private final int mCustomPrinterIconGen;
/** Intent that launches the activity showing more information about the printer. */
- private PendingIntent mInfoIntent;
+ private final @Nullable PendingIntent mInfoIntent;
- private String mName;
+ private final @NonNull String mName;
- private int mStatus;
+ private final @Status int mStatus;
- private String mDescription;
+ private final @Nullable String mDescription;
- private PrinterCapabilitiesInfo mCapabilities;
+ private final @Nullable PrinterCapabilitiesInfo mCapabilities;
- private PrinterInfo() {
- /* do nothing */
- }
-
- private PrinterInfo(PrinterInfo prototype) {
- copyFrom(prototype);
- }
-
- /**
- * @hide
- */
- public void copyFrom(PrinterInfo other) {
- if (this == other) {
- return;
- }
- mId = other.mId;
- mName = other.mName;
- mStatus = other.mStatus;
- mDescription = other.mDescription;
- if (other.mCapabilities != null) {
- if (mCapabilities != null) {
- mCapabilities.copyFrom(other.mCapabilities);
- } else {
- mCapabilities = new PrinterCapabilitiesInfo(other.mCapabilities);
- }
- } else {
- mCapabilities = null;
- }
- mIconResourceId = other.mIconResourceId;
- mHasCustomPrinterIcon = other.mHasCustomPrinterIcon;
- mCustomPrinterIconGen = other.mCustomPrinterIconGen;
- mInfoIntent = other.mInfoIntent;
+ private PrinterInfo(@NonNull PrinterId printerId, @NonNull String name, @Status int status,
+ int iconResourceId, boolean hasCustomPrinterIcon, String description,
+ PendingIntent infoIntent, PrinterCapabilitiesInfo capabilities,
+ int customPrinterIconGen) {
+ mId = printerId;
+ mName = name;
+ mStatus = status;
+ mIconResourceId = iconResourceId;
+ mHasCustomPrinterIcon = hasCustomPrinterIcon;
+ mDescription = description;
+ mInfoIntent = infoIntent;
+ mCapabilities = capabilities;
+ mCustomPrinterIconGen = customPrinterIconGen;
}
/**
@@ -180,7 +166,7 @@
*
* @return The printer name.
*/
- public @Nullable String getName() {
+ public @NonNull String getName() {
return mName;
}
@@ -227,10 +213,51 @@
return mCapabilities;
}
+ /**
+ * Check if printerId is valid.
+ *
+ * @param printerId The printerId that might be valid
+ * @return The valid printerId
+ * @throws IllegalArgumentException if printerId is not valid.
+ */
+ private static @NonNull PrinterId checkPrinterId(PrinterId printerId) {
+ return Preconditions.checkNotNull(printerId, "printerId cannot be null.");
+ }
+
+ /**
+ * Check if status is valid.
+ *
+ * @param status The status that might be valid
+ * @return The valid status
+ * @throws IllegalArgumentException if status is not valid.
+ */
+ private static @Status int checkStatus(int status) {
+ if (!(status == STATUS_IDLE
+ || status == STATUS_BUSY
+ || status == STATUS_UNAVAILABLE)) {
+ throw new IllegalArgumentException("status is invalid.");
+ }
+
+ return status;
+ }
+
+ /**
+ * Check if name is valid.
+ *
+ * @param name The name that might be valid
+ * @return The valid name
+ * @throws IllegalArgumentException if name is not valid.
+ */
+ private static @NonNull String checkName(String name) {
+ return Preconditions.checkStringNotEmpty(name, "name cannot be empty.");
+ }
+
private PrinterInfo(Parcel parcel) {
- mId = parcel.readParcelable(null);
- mName = parcel.readString();
- mStatus = parcel.readInt();
+ // mName can be null due to unchecked set in Builder.setName and status can be invalid
+ // due to unchecked set in Builder.setStatus, hence we can only check mId for a valid state
+ mId = checkPrinterId((PrinterId) parcel.readParcelable(null));
+ mName = checkName(parcel.readString());
+ mStatus = checkStatus(parcel.readInt());
mDescription = parcel.readString();
mCapabilities = parcel.readParcelable(null);
mIconResourceId = parcel.readInt();
@@ -261,8 +288,8 @@
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((mId != null) ? mId.hashCode() : 0);
- result = prime * result + ((mName != null) ? mName.hashCode() : 0);
+ result = prime * result + mId.hashCode();
+ result = prime * result + mName.hashCode();
result = prime * result + mStatus;
result = prime * result + ((mDescription != null) ? mDescription.hashCode() : 0);
result = prime * result + ((mCapabilities != null) ? mCapabilities.hashCode() : 0);
@@ -282,15 +309,11 @@
* @hide
*/
public boolean equalsIgnoringStatus(PrinterInfo other) {
- if (mId == null) {
- if (other.mId != null) {
- return false;
- }
- } else if (!mId.equals(other.mId)) {
+ if (!mId.equals(other.mId)) {
return false;
}
- if (!TextUtils.equals(mName, other.mName)) {
- return false;
+ if (!mName.equals(other.mName)) {
+ return false;
}
if (!TextUtils.equals(mDescription, other.mDescription)) {
return false;
@@ -363,7 +386,15 @@
* Builder for creating of a {@link PrinterInfo}.
*/
public static final class Builder {
- private final PrinterInfo mPrototype;
+ private @NonNull PrinterId mPrinterId;
+ private @NonNull String mName;
+ private @Status int mStatus;
+ private int mIconResourceId;
+ private boolean mHasCustomPrinterIcon;
+ private String mDescription;
+ private PendingIntent mInfoIntent;
+ private PrinterCapabilitiesInfo mCapabilities;
+ private int mCustomPrinterIconGen;
/**
* Constructor.
@@ -375,19 +406,9 @@
* printer name is empty or the status is not a valid one.
*/
public Builder(@NonNull PrinterId printerId, @NonNull String name, @Status int status) {
- if (printerId == null) {
- throw new IllegalArgumentException("printerId cannot be null.");
- }
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name cannot be empty.");
- }
- if (!isValidStatus(status)) {
- throw new IllegalArgumentException("status is invalid.");
- }
- mPrototype = new PrinterInfo();
- mPrototype.mId = printerId;
- mPrototype.mName = name;
- mPrototype.mStatus = status;
+ mPrinterId = checkPrinterId(printerId);
+ mName = checkName(name);
+ mStatus = checkStatus(status);
}
/**
@@ -396,8 +417,15 @@
* @param other Other info from which to start building.
*/
public Builder(@NonNull PrinterInfo other) {
- mPrototype = new PrinterInfo();
- mPrototype.copyFrom(other);
+ mPrinterId = other.mId;
+ mName = other.mName;
+ mStatus = other.mStatus;
+ mIconResourceId = other.mIconResourceId;
+ mHasCustomPrinterIcon = other.mHasCustomPrinterIcon;
+ mDescription = other.mDescription;
+ mInfoIntent = other.mInfoIntent;
+ mCapabilities = other.mCapabilities;
+ mCustomPrinterIconGen = other.mCustomPrinterIconGen;
}
/**
@@ -405,13 +433,12 @@
*
* @param status The status.
* @return This builder.
- *
* @see PrinterInfo#STATUS_IDLE
* @see PrinterInfo#STATUS_BUSY
* @see PrinterInfo#STATUS_UNAVAILABLE
*/
public @NonNull Builder setStatus(@Status int status) {
- mPrototype.mStatus = status;
+ mStatus = checkStatus(status);
return this;
}
@@ -424,7 +451,8 @@
* @see PrinterInfo.Builder#setHasCustomPrinterIcon
*/
public @NonNull Builder setIconResourceId(@DrawableRes int iconResourceId) {
- mPrototype.mIconResourceId = iconResourceId;
+ mIconResourceId = Preconditions.checkArgumentNonnegative(iconResourceId,
+ "iconResourceId can't be negative");
return this;
}
@@ -442,7 +470,7 @@
* @return This builder.
*/
public @NonNull Builder setHasCustomPrinterIcon() {
- mPrototype.mHasCustomPrinterIcon = true;
+ mHasCustomPrinterIcon = true;
return this;
}
@@ -454,7 +482,7 @@
* @return This builder.
*/
public @NonNull Builder setName(@NonNull String name) {
- mPrototype.mName = name;
+ mName = checkName(name);
return this;
}
@@ -466,7 +494,7 @@
* @return This builder.
*/
public @NonNull Builder setDescription(@NonNull String description) {
- mPrototype.mDescription = description;
+ mDescription = description;
return this;
}
@@ -478,7 +506,7 @@
* @return This builder.
*/
public @NonNull Builder setInfoIntent(@NonNull PendingIntent infoIntent) {
- mPrototype.mInfoIntent = infoIntent;
+ mInfoIntent = infoIntent;
return this;
}
@@ -489,7 +517,7 @@
* @return This builder.
*/
public @NonNull Builder setCapabilities(@NonNull PrinterCapabilitiesInfo capabilities) {
- mPrototype.mCapabilities = capabilities;
+ mCapabilities = capabilities;
return this;
}
@@ -499,13 +527,9 @@
* @return A new {@link PrinterInfo}.
*/
public @NonNull PrinterInfo build() {
- return mPrototype;
- }
-
- private boolean isValidStatus(int status) {
- return (status == STATUS_IDLE
- || status == STATUS_BUSY
- || status == STATUS_UNAVAILABLE);
+ return new PrinterInfo(mPrinterId, mName, mStatus, mIconResourceId,
+ mHasCustomPrinterIcon, mDescription, mInfoIntent, mCapabilities,
+ mCustomPrinterIconGen);
}
/**
@@ -517,7 +541,7 @@
* @hide
*/
public @NonNull Builder incCustomPrinterIconGen() {
- mPrototype.mCustomPrinterIconGen++;
+ mCustomPrinterIconGen++;
return this;
}
}
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index d0037b7..3104492 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -30,6 +30,8 @@
import android.print.PrinterId;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -346,6 +348,7 @@
*/
public final PrinterId generatePrinterId(String localId) {
throwIfNotCalledOnMainThread();
+ localId = Preconditions.checkNotNull(localId, "localId cannot be null");
return new PrinterId(new ComponentName(getPackageName(),
getClass().getName()), localId);
}
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
new file mode 100644
index 0000000..2a9d4ae
--- /dev/null
+++ b/core/java/android/provider/BlockedNumberContract.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 android.provider;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * Constants and methods to access blocked phone numbers for incoming calls and texts.
+ *
+ * TODO javadoc
+ * - Proper javadoc tagging.
+ * - Code sample?
+ * - Describe who can access it.
+ */
+public class BlockedNumberContract {
+ private BlockedNumberContract() {
+ }
+
+ /** The authority for the contacts provider */
+ public static final String AUTHORITY = "com.android.blockednumber";
+
+ /** A content:// style uri to the authority for the contacts provider */
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ /**
+ * TODO javadoc
+ *
+ * Constants to interact with the blocked phone number list.
+ */
+ public static class BlockedNumbers {
+ private BlockedNumbers() {
+ }
+
+ /**
+ * TODO javadoc
+ *
+ * Content URI for the blocked numbers.
+ *
+ * Supported operations
+ * blocked
+ * - query
+ * - delete
+ * - insert
+ *
+ * blocked/ID
+ * - query (selection is not supported)
+ * - delete (selection is not supported)
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI,
+ "blocked");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} itself providing a directory of blocked phone
+ * numbers.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/blocked_numbers";
+
+ /**
+ * The MIME type of a blocked phone number under {@link #CONTENT_URI}.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/blocked_number";
+
+ /**
+ * Auto-generated ID field which monotonically increases.
+ * <p>TYPE: long</p>
+ */
+ public static final String COLUMN_ID = "_id";
+
+ /**
+ * Phone number to block.
+ * <p>Must be specified in {@code insert}.
+ * <p>TYPE: String</p>
+ */
+ public static final String COLUMN_ORIGINAL_NUMBER = "original_number";
+
+ /**
+ * Phone number to block. The system generates it from {@link #COLUMN_ORIGINAL_NUMBER}
+ * by removing all formatting characters.
+ * <p>Must NOT be specified in {@code insert}.
+ * <p>TYPE: String</p>
+ */
+ public static final String COLUMN_STRIPPED_NUMBER = "stripped_number";
+
+ /**
+ * Phone number to block. The system generates it from {@link #COLUMN_ORIGINAL_NUMBER}
+ * by removing all formatting characters.
+ * <p>Optional in {@code insert}. When not specified, the system tries to generate it
+ * assuming the current country. (Which will still be null if the number is not valid.)
+ * <p>TYPE: String</p>
+ */
+ public static final String COLUMN_E164_NUMBER = "e164_number";
+ }
+
+ /** @hide */
+ public static final String METHOD_IS_BLOCKED = "is_blocked";
+
+ /** @hide */
+ public static final String RES_NUMBER_IS_BLOCKED = "blocked";
+
+ /**
+ * Returns whether a given number is in the blocked list.
+ *
+ * TODO This should probably catch IllegalArgumentException to guard against the case where
+ * the provider is encrypted or the user is not running.
+ * (See addEntryAndRemoveExpiredEntries() in
+ * http://ag/#/c/844426/3/core/java/android/provider/CallLog.java)
+ */
+ public static boolean isBlocked(Context context, String phoneNumber) {
+ final Bundle res = context.getContentResolver().call(AUTHORITY_URI,
+ METHOD_IS_BLOCKED, phoneNumber, null);
+ return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+ }
+}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 10432b5..b547432 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -409,6 +409,20 @@
"directories_enterprise");
/**
+ * Access file provided by remote directory. It allows both personal and work remote
+ * directory, but not local and invisible diretory.
+ *
+ * It's supported only by a few specific places for referring to contact pictures in the
+ * remote directory. Contact picture URIs, e.g.
+ * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}, may contain this kind of URI.
+ *
+ * @hide
+ */
+ public static final Uri ENTERPRISE_FILE_URI = Uri.withAppendedPath(AUTHORITY_URI,
+ "directory_file_enterprise");
+
+
+ /**
* The MIME-type of {@link #CONTENT_URI} providing a directory of
* contact directories.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
old mode 100644
new mode 100755
index d4c9944..3042aa9
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -641,7 +641,7 @@
* provided by the platform for applications to operate correctly in the various power
* saving mode. This is only for unusual applications that need to deeply control their own
* execution, at the potential expense of the user's battery life. Note that these applications
- * greatly run the risk of showing to the user has how power consumers on their device.</p>
+ * greatly run the risk of showing to the user as high power consumers on their device.</p>
* <p>
* Input: The Intent's data URI must specify the application package name
* to be shown, with the "package" scheme. That is "package:com.my.app".
@@ -4429,6 +4429,13 @@
public static final String HTTP_PROXY = Global.HTTP_PROXY;
/**
+ * Package designated as always-on VPN provider.
+ *
+ * @hide
+ */
+ public static final String ALWAYS_ON_VPN_APP = "always_on_vpn_app";
+
+ /**
* Whether applications can be installed for this user via the system's
* {@link Intent#ACTION_INSTALL_PACKAGE} mechanism.
*
@@ -7409,6 +7416,9 @@
BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX = "bluetooth_a2dp_sink_priority_";
/** {@hide} */
public static final String
+ BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX = "bluetooth_a2dp_src_priority_";
+ /** {@hide} */
+ public static final String
BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX = "bluetooth_input_device_priority_";
/** {@hide} */
public static final String
@@ -7456,10 +7466,12 @@
* The following keys are supported:
*
* <pre>
- * idle_duration (long)
+ * idle_duration2 (long)
* wallclock_threshold (long)
* parole_interval (long)
* parole_duration (long)
+ *
+ * idle_duration (long) // This is deprecated and used to circumvent b/26355386.
* </pre>
*
* <p>
@@ -7509,6 +7521,14 @@
}
/**
+ * Get the key that retrieves a bluetooth a2dp src's priority.
+ * @hide
+ */
+ public static final String getBluetoothA2dpSrcPriorityKey(String address) {
+ return BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+ }
+
+ /**
* Get the key that retrieves a bluetooth Input Device's priority.
* @hide
*/
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index bb46e83..816ecde 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -15,9 +15,6 @@
*/
package android.service.dreams;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
import android.annotation.IdRes;
import android.annotation.LayoutRes;
import android.annotation.Nullable;
@@ -33,27 +30,32 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.MathUtils;
import android.util.Slog;
import android.view.ActionMode;
import android.view.Display;
import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
-import android.util.MathUtils;
+import com.android.internal.policy.PhoneWindow;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.DumpUtils.Dump;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
/**
* Extend this class to implement a custom dream (available to the user as a "Daydream").
*
@@ -365,6 +367,11 @@
@Override
public void onActionModeFinished(ActionMode mode) {
}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+ }
// end Window.Callback methods
// begin public api
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 88bd283..eff09d6 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -85,6 +85,13 @@
"android.service.zen.automatic.configurationActivity";
/**
+ * The name of the {@code meta-data} tag containing the maximum number of rule instances that
+ * can be created for this rule type. Omit or enter a value <= 0 to allow unlimited instances.
+ */
+ public static final String META_DATA_RULE_INSTANCE_LIMIT =
+ "android.service.zen.automatic.ruleInstanceLimit";
+
+ /**
* A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
*/
public static final String EXTRA_RULE_ID = "android.content.automatic.ruleId";
diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl
index 9991d41..4bfc948 100644
--- a/core/java/android/service/quicksettings/IQSService.aidl
+++ b/core/java/android/service/quicksettings/IQSService.aidl
@@ -27,5 +27,9 @@
void updateStatusIcon(in Tile tile, in Icon icon,
String contentDescription);
void onShowDialog(in Tile tile);
+ void onStartActivity(in Tile tile);
void setTileMode(in ComponentName component, int mode);
+ boolean isLocked();
+ boolean isSecure();
+ void startUnlockAndRun(in Tile tile);
}
diff --git a/core/java/android/service/quicksettings/IQSTileService.aidl b/core/java/android/service/quicksettings/IQSTileService.aidl
index 4997f75..bfde870 100644
--- a/core/java/android/service/quicksettings/IQSTileService.aidl
+++ b/core/java/android/service/quicksettings/IQSTileService.aidl
@@ -29,4 +29,5 @@
void onStartListening();
void onStopListening();
void onClick(IBinder wtoken);
+ void onUnlockComplete();
}
diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java
index 6104913..85f1955 100644
--- a/core/java/android/service/quicksettings/Tile.java
+++ b/core/java/android/service/quicksettings/Tile.java
@@ -36,10 +36,37 @@
private static final String TAG = "Tile";
+ /**
+ * This is the default state of any tile, until updated by the {@link TileService}.
+ * <p>
+ * An unavailable state indicates that for some reason this tile is not currently
+ * available to the user for some reason, and will have no click action. The tile's
+ * icon will be tinted differently to reflect this state.
+ */
+ public static final int STATE_UNAVAILABLE = 0;
+
+ /**
+ * This represents a tile that is currently in a disabled state but is still interactable.
+ *
+ * A disabled state indicates that the tile is not currently active (e.g. wifi disconnected or
+ * bluetooth disabled), but is still interactable by the user to modify this state. Tiles
+ * that have boolean states should use this to represent one of their states. The tile's
+ * icon will be tinted differently to reflect this state, but still be distinct from unavailable.
+ */
+ public static final int STATE_INACTIVE = 1;
+
+ /**
+ * This represents a tile that is currently active. (e.g. wifi is connected, bluetooth is on,
+ * cast is casting).
+ */
+ public static final int STATE_ACTIVE = 2;
+
private ComponentName mComponentName;
private Icon mIcon;
private CharSequence mLabel;
private CharSequence mContentDescription;
+ // Default to active until clients of the new API can update.
+ private int mState = STATE_ACTIVE;
private IQSService mService;
@@ -79,6 +106,29 @@
}
/**
+ * The current state of the tile.
+ *
+ * @see #STATE_UNAVAILABLE
+ * @see #STATE_INACTIVE
+ * @see #STATE_ACTIVE
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Sets the current state for the tile.
+ *
+ * Does not take effect until {@link #updateTile()} is called.
+ *
+ * @param state One of {@link #STATE_UNAVAILABLE}, {@link #STATE_INACTIVE},
+ * {@link #STATE_ACTIVE}
+ */
+ public void setState(int state) {
+ mState = state;
+ }
+
+ /**
* Gets the current icon for the tile.
*/
public Icon getIcon() {
@@ -165,6 +215,7 @@
} else {
dest.writeByte((byte) 0);
}
+ dest.writeInt(mState);
TextUtils.writeToParcel(mLabel, dest, flags);
TextUtils.writeToParcel(mContentDescription, dest, flags);
}
@@ -180,6 +231,7 @@
} else {
mIcon = null;
}
+ mState = source.readInt();
mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
}
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index 6b12193..c02465b 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -118,6 +118,7 @@
private Tile mTile;
private IBinder mToken;
private IQSService mService;
+ private Runnable mUnlockRunnable;
@Override
public void onDestroy() {
@@ -199,6 +200,8 @@
* This will collapse the Quick Settings panel and show the dialog.
*
* @param dialog Dialog to show.
+ *
+ * @see #isLocked()
*/
public final void showDialog(Dialog dialog) {
dialog.getWindow().getAttributes().token = mToken;
@@ -211,6 +214,67 @@
}
/**
+ * Prompts the user to unlock the device before executing the Runnable.
+ * <p>
+ * The user will be prompted for their current security method if applicable
+ * and if successful, runnable will be executed. The Runnable will not be
+ * executed if the user fails to unlock the device or cancels the operation.
+ */
+ public final void unlockAndRun(Runnable runnable) {
+ mUnlockRunnable = runnable;
+ try {
+ mService.startUnlockAndRun(mTile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Checks if the device is in a secure state.
+ *
+ * TileServices should detect when the device is secure and change their behavior
+ * accordingly.
+ *
+ * @return true if the device is secure.
+ */
+ public final boolean isSecure() {
+ try {
+ return mService.isSecure();
+ } catch (RemoteException e) {
+ return true;
+ }
+ }
+
+ /**
+ * Checks if the lock screen is showing.
+ *
+ * When a device is locked, then {@link #showDialog} will not present a dialog, as it will
+ * be under the lock screen. If the behavior of the Tile is safe to do while locked,
+ * then the user should use {@link #startActivity} to launch an activity on top of the lock
+ * screen, otherwise the tile should use {@link #unlockAndRun(Runnable)} to give the
+ * user their security challenge.
+ *
+ * @return true if the device is locked.
+ */
+ public final boolean isLocked() {
+ try {
+ return mService.isLocked();
+ } catch (RemoteException e) {
+ return true;
+ }
+ }
+
+ /**
+ * Start an activity while collapsing the panel.
+ */
+ public final void startActivityAndCollapse(Intent intent) {
+ startActivity(intent);
+ try {
+ mService.onStartActivity(mTile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Gets the {@link Tile} for this service.
* <p/>
* This tile may be used to get or set the current state for this
@@ -258,6 +322,11 @@
public void onClick(IBinder wtoken) throws RemoteException {
mHandler.obtainMessage(H.MSG_TILE_CLICKED, wtoken).sendToTarget();
}
+
+ @Override
+ public void onUnlockComplete() throws RemoteException{
+ mHandler.sendEmptyMessage(H.MSG_UNLOCK_COMPLETE);
+ }
};
}
@@ -269,6 +338,7 @@
private static final int MSG_TILE_REMOVED = 5;
private static final int MSG_TILE_CLICKED = 6;
private static final int MSG_SET_SERVICE = 7;
+ private static final int MSG_UNLOCK_COMPLETE = 8;
public H(Looper looper) {
super(looper);
@@ -323,6 +393,11 @@
mToken = (IBinder) msg.obj;
TileService.this.onClick();
break;
+ case MSG_UNLOCK_COMPLETE:
+ if (mUnlockRunnable != null) {
+ mUnlockRunnable.run();
+ }
+ break;
}
}
}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index f9387b3..2c4241b 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1116,24 +1116,30 @@
public int getOffsetForHorizontal(int line, float horiz) {
// TODO: use Paint.getOffsetForAdvance to avoid binary search
final int lineEndOffset = getLineEnd(line);
+ final int lineStartOffset = getLineStart(line);
+
+ Directions dirs = getLineDirections(line);
+
+ TextLine tl = TextLine.obtain();
+ // XXX: we don't care about tabs as we just use TextLine#getOffsetToLeftRightOf here.
+ tl.set(mPaint, mText, lineStartOffset, lineEndOffset, getParagraphDirection(line), dirs,
+ false, null);
+
final int max;
if (line == getLineCount() - 1) {
max = lineEndOffset;
} else {
- max = mPaint.getTextRunCursor(mText, 0, mText.length(),
- isRtlCharAt(lineEndOffset) ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR,
- lineEndOffset, Paint.CURSOR_BEFORE);
+ max = tl.getOffsetToLeftRightOf(lineEndOffset - lineStartOffset,
+ !isRtlCharAt(lineEndOffset - 1)) + lineStartOffset;
}
- final int min = getLineStart(line);
- Directions dirs = getLineDirections(line);
-
- int best = min;
+ int best = lineStartOffset;
float bestdist = Math.abs(getPrimaryHorizontal(best) - horiz);
for (int i = 0; i < dirs.mDirections.length; i += 2) {
- int here = min + dirs.mDirections[i];
+ int here = lineStartOffset + dirs.mDirections[i];
int there = here + (dirs.mDirections[i+1] & RUN_LENGTH_MASK);
- int swap = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0 ? -1 : 1;
+ boolean isRtl = (dirs.mDirections[i+1] & RUN_RTL_FLAG) != 0;
+ int swap = isRtl ? -1 : 1;
if (there > max)
there = max;
@@ -1153,23 +1159,23 @@
low = here + 1;
if (low < there) {
- low = getOffsetAtStartOf(low);
+ int aft = tl.getOffsetToLeftRightOf(low - lineStartOffset, isRtl) + lineStartOffset;
+ low = tl.getOffsetToLeftRightOf(aft - lineStartOffset, !isRtl) + lineStartOffset;
+ if (low >= here && low < there) {
+ float dist = Math.abs(getPrimaryHorizontal(low) - horiz);
+ if (aft < there) {
+ float other = Math.abs(getPrimaryHorizontal(aft) - horiz);
- float dist = Math.abs(getPrimaryHorizontal(low) - horiz);
-
- int aft = TextUtils.getOffsetAfter(mText, low);
- if (aft < there) {
- float other = Math.abs(getPrimaryHorizontal(aft) - horiz);
-
- if (other < dist) {
- dist = other;
- low = aft;
+ if (other < dist) {
+ dist = other;
+ low = aft;
+ }
}
- }
- if (dist < bestdist) {
- bestdist = dist;
- best = low;
+ if (dist < bestdist) {
+ bestdist = dist;
+ best = low;
+ }
}
}
@@ -1188,6 +1194,7 @@
best = max;
}
+ TextLine.recycle(tl);
return best;
}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index c42c791..2a52961 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -688,13 +688,14 @@
* @param bottom the bottom of the line
* @param fmi receives metrics information, can be null
* @param needWidth true if the width of the run is needed
+ * @param offset the offset for the purpose of measuring
* @return the signed width of the run based on the run direction; only
* valid if needWidth is true
*/
private float handleText(TextPaint wp, int start, int end,
int contextStart, int contextEnd, boolean runIsRtl,
Canvas c, float x, int top, int y, int bottom,
- FontMetricsInt fmi, boolean needWidth) {
+ FontMetricsInt fmi, boolean needWidth, int offset) {
// Get metrics first (even for empty strings or "0" width runs)
if (fmi != null) {
@@ -712,11 +713,11 @@
if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor != 0 || runIsRtl))) {
if (mCharsValid) {
ret = wp.getRunAdvance(mChars, start, end, contextStart, contextEnd,
- runIsRtl, end);
+ runIsRtl, offset);
} else {
int delta = mStart;
ret = wp.getRunAdvance(mText, delta + start, delta + end,
- delta + contextStart, delta + contextEnd, runIsRtl, delta + end);
+ delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
}
}
@@ -865,8 +866,8 @@
TextPaint wp = mWorkPaint;
wp.set(mPaint);
final int mlimit = measureLimit;
- return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top,
- y, bottom, fmi, needWidth || mlimit < measureLimit);
+ return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top,
+ y, bottom, fmi, needWidth || mlimit < measureLimit, mlimit);
}
mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit);
@@ -910,13 +911,14 @@
}
for (int j = i, jnext; j < mlimit; j = jnext) {
- jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) -
+ jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) -
mStart;
+ int offset = Math.min(jnext, mlimit);
wp.set(mPaint);
for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
// Intentionally using >= and <= as explained above
- if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) ||
+ if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + offset) ||
(mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue;
CharacterStyle span = mCharacterStyleSpanSet.spans[k];
@@ -928,7 +930,7 @@
wp.setHyphenEdit(0);
}
x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
- top, y, bottom, fmi, needWidth || jnext < measureLimit);
+ top, y, bottom, fmi, needWidth || jnext < measureLimit, offset);
}
}
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 6b449f9..1b00db2 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -16,6 +16,8 @@
package android.text.style;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -84,7 +86,15 @@
private int mFlags;
private final String[] mSuggestions;
- private final String mLocaleString;
+ /**
+ * Kept for compatibility for apps that rely on invalid locale strings e.g.
+ * {@code new Locale(" an ", " i n v a l i d ", "data")}, which cannot be handled by
+ * {@link #mLanguageTag}.
+ */
+ @NonNull
+ private final String mLocaleStringForCompatibility;
+ @NonNull
+ private final String mLanguageTag;
private final String mNotificationTargetClassName;
private final String mNotificationTargetPackageName;
private final int mHashCode;
@@ -130,14 +140,18 @@
final int N = Math.min(SUGGESTIONS_MAX_SIZE, suggestions.length);
mSuggestions = Arrays.copyOf(suggestions, N);
mFlags = flags;
+ final Locale sourceLocale;
if (locale != null) {
- mLocaleString = locale.toString();
+ sourceLocale = locale;
} else if (context != null) {
- mLocaleString = context.getResources().getConfiguration().locale.toString();
+ // TODO: Consider to context.getResources().getResolvedLocale() instead.
+ sourceLocale = context.getResources().getConfiguration().locale;
} else {
Log.e("SuggestionSpan", "No locale or context specified in SuggestionSpan constructor");
- mLocaleString = "";
+ sourceLocale = null;
}
+ mLocaleStringForCompatibility = sourceLocale == null ? "" : sourceLocale.toString();
+ mLanguageTag = sourceLocale == null ? "" : sourceLocale.toLanguageTag();
if (context != null) {
mNotificationTargetPackageName = context.getPackageName();
@@ -150,7 +164,8 @@
} else {
mNotificationTargetClassName = "";
}
- mHashCode = hashCodeInternal(mSuggestions, mLocaleString, mNotificationTargetClassName);
+ mHashCode = hashCodeInternal(mSuggestions, mLanguageTag, mLocaleStringForCompatibility,
+ mNotificationTargetClassName);
initStyle(context);
}
@@ -194,7 +209,8 @@
public SuggestionSpan(Parcel src) {
mSuggestions = src.readStringArray();
mFlags = src.readInt();
- mLocaleString = src.readString();
+ mLocaleStringForCompatibility = src.readString();
+ mLanguageTag = src.readString();
mNotificationTargetClassName = src.readString();
mNotificationTargetPackageName = src.readString();
mHashCode = src.readInt();
@@ -214,10 +230,29 @@
}
/**
- * @return the locale of the suggestions
+ * @deprecated use {@link #getLocaleObject()} instead.
+ * @return the locale of the suggestions. An empty string is returned if no locale is specified.
*/
+ @NonNull
+ @Deprecated
public String getLocale() {
- return mLocaleString;
+ return mLocaleStringForCompatibility;
+ }
+
+ /**
+ * Returns a well-formed BCP 47 language tag representation of the suggestions, as a
+ * {@link Locale} object.
+ *
+ * <p><b>Caveat</b>: The returned object is guaranteed to be a a well-formed BCP 47 language tag
+ * representation. For example, this method can return an empty locale rather than returning a
+ * malformed data when this object is initialized with an malformed {@link Locale} object, e.g.
+ * {@code new Locale(" a ", " b c d ", " "}.</p>
+ *
+ * @return the locale of the suggestions. {@code null} is returned if no locale is specified.
+ */
+ @Nullable
+ public Locale getLocaleObject() {
+ return mLanguageTag.isEmpty() ? null : Locale.forLanguageTag(mLanguageTag);
}
/**
@@ -255,7 +290,8 @@
public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeStringArray(mSuggestions);
dest.writeInt(mFlags);
- dest.writeString(mLocaleString);
+ dest.writeString(mLocaleStringForCompatibility);
+ dest.writeString(mLanguageTag);
dest.writeString(mNotificationTargetClassName);
dest.writeString(mNotificationTargetPackageName);
dest.writeInt(mHashCode);
@@ -290,10 +326,10 @@
return mHashCode;
}
- private static int hashCodeInternal(String[] suggestions, String locale,
- String notificationTargetClassName) {
+ private static int hashCodeInternal(String[] suggestions, @NonNull String languageTag,
+ @NonNull String localeStringForCompatibility, String notificationTargetClassName) {
return Arrays.hashCode(new Object[] {Long.valueOf(SystemClock.uptimeMillis()), suggestions,
- locale, notificationTargetClassName});
+ languageTag, localeStringForCompatibility, notificationTargetClassName});
}
public static final Parcelable.Creator<SuggestionSpan> CREATOR =
diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java
index b10f6a0..817541c 100644
--- a/core/java/android/transition/SidePropagation.java
+++ b/core/java/android/transition/SidePropagation.java
@@ -16,6 +16,7 @@
package android.transition;
import android.graphics.Rect;
+import android.transition.Slide.GravityFlag;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -45,7 +46,7 @@
* {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT},
* {@link Gravity#BOTTOM}, {@link Gravity#START}, or {@link Gravity#END}.
*/
- public void setSide(int side) {
+ public void setSide(@GravityFlag int side) {
mSide = side;
}
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index 9063b43..9af65e4 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -17,6 +17,7 @@
import android.animation.Animator;
import android.animation.TimeInterpolator;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
@@ -27,6 +28,9 @@
import android.view.animation.DecelerateInterpolator;
import com.android.internal.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This transition tracks changes to the visibility of target views in the
* start and end scenes and moves views in or out from one of the edges of the
@@ -42,7 +46,12 @@
private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
private CalculateSlide mSlideCalculator = sCalculateBottom;
- private int mSlideEdge = Gravity.BOTTOM;
+ private @GravityFlag int mSlideEdge = Gravity.BOTTOM;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Gravity.LEFT, Gravity.TOP, Gravity.RIGHT, Gravity.BOTTOM, Gravity.START, Gravity.END})
+ public @interface GravityFlag {}
private interface CalculateSlide {
@@ -176,7 +185,7 @@
* {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
* @attr ref android.R.styleable#Slide_slideEdge
*/
- public void setSlideEdge(int slideEdge) {
+ public void setSlideEdge(@GravityFlag int slideEdge) {
switch (slideEdge) {
case Gravity.LEFT:
mSlideCalculator = sCalculateLeft;
@@ -214,6 +223,7 @@
* {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
* @attr ref android.R.styleable#Slide_slideEdge
*/
+ @GravityFlag
public int getSlideEdge() {
return mSlideEdge;
}
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index e711812..eb95a02 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -16,17 +16,21 @@
package android.transition;
-import com.android.internal.R;
-
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.Animator.AnimatorPauseListener;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This transition tracks changes to the visibility of target views in the
* start and end scenes. Visibility is determined not just by the
@@ -46,6 +50,11 @@
private static final String PROPNAME_PARENT = "android:visibility:parent";
private static final String PROPNAME_SCREEN_LOCATION = "android:visibility:screenLocation";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag=true, value={MODE_IN, MODE_OUT})
+ @interface VisibilityMode {}
+
/**
* Mode used in {@link #setMode(int)} to make the transition
* operate on targets that are appearing. Maybe be combined with
@@ -99,7 +108,7 @@
* {@link #MODE_IN} and {@link #MODE_OUT}.
* @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
*/
- public void setMode(int mode) {
+ public void setMode(@VisibilityMode int mode) {
if ((mode & ~(MODE_IN | MODE_OUT)) != 0) {
throw new IllegalArgumentException("Only MODE_IN and MODE_OUT flags are allowed");
}
@@ -113,6 +122,7 @@
* {@link #MODE_IN} and {@link #MODE_OUT}.
* @attr ref android.R.styleable#VisibilityTransition_transitionVisibilityMode
*/
+ @VisibilityMode
public int getMode() {
return mMode;
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
new file mode 100644
index 0000000..81c8c45
--- /dev/null
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.util.apk;
+
+import android.util.Pair;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.math.BigInteger;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.security.DigestException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PSSParameterSpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * APK Signature Scheme v2 verifier.
+ *
+ * @hide for internal use only.
+ */
+public class ApkSignatureSchemeV2Verifier {
+
+ /**
+ * {@code .SF} file header section attribute indicating that the APK is signed not just with
+ * JAR signature scheme but also with APK Signature Scheme v2 or newer. This attribute
+ * facilitates v2 signature stripping detection.
+ *
+ * <p>The attribute contains a comma-separated set of signature scheme IDs.
+ */
+ public static final String SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME = "X-Android-APK-Signed";
+ // TODO: Change the value when signing scheme finalized.
+ public static final int SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID = 1234567890;
+
+ /**
+ * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
+ * associated with each signer.
+ *
+ * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
+ * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
+ * @throws IOException if an I/O error occurs while reading the APK file.
+ */
+ public static X509Certificate[][] verify(String apkFile)
+ throws SignatureNotFoundException, SecurityException, IOException {
+ try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
+ return verify(apk);
+ }
+ }
+
+ /**
+ * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
+ * associated with each signer.
+ *
+ * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
+ * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
+ * @throws IOException if an I/O error occurs while reading the APK file.
+ */
+ public static X509Certificate[][] verify(RandomAccessFile apk)
+ throws SignatureNotFoundException, SecurityException, IOException {
+
+ long fileSize = apk.length();
+ 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();
+ return verify(apkContents);
+ }
+
+ /**
+ * Verifies APK Signature Scheme v2 signatures of the provided APK and returns the certificates
+ * associated with each signer.
+ *
+ * @param apkContents contents of the APK. The contents start at the current position and end
+ * at the limit of the buffer.
+ *
+ * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
+ * @throws SecurityException if a APK Signature Scheme v2 signature of this APK does not verify.
+ */
+ public static X509Certificate[][] verify(ByteBuffer apkContents)
+ throws SignatureNotFoundException, SecurityException {
+ // Avoid modifying byte order, position, limit, and mark of the original apkContents.
+ apkContents = apkContents.slice();
+
+ // 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;
+
+ // 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.
+ ByteBuffer apkSignatureSchemeV2Block = findApkSignatureSchemeV2Block(apkSigningBlock);
+
+ // Verify the contents of the APK outside of the APK Signing Block using the APK Signature
+ // Scheme v2 Block.
+ return verify(
+ apkContents,
+ apkSignatureSchemeV2Block,
+ apkSigningBlockOffset,
+ centralDirOffset,
+ eocdOffset);
+ }
+
+ /**
+ * Verifies the contents outside of the APK Signing Block using the provided APK Signature
+ * Scheme v2 Block.
+ */
+ private static X509Certificate[][] verify(
+ ByteBuffer apkContents,
+ ByteBuffer v2Block,
+ int apkSigningBlockOffset,
+ int centralDirOffset,
+ int eocdOffset) throws SecurityException {
+ int signerCount = 0;
+ Map<Integer, byte[]> contentDigests = new HashMap<>();
+ List<X509Certificate[]> signerCerts = new ArrayList<>();
+ CertificateFactory certFactory;
+ try {
+ certFactory = CertificateFactory.getInstance("X.509");
+ } catch (CertificateException e) {
+ throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
+ }
+ ByteBuffer signers;
+ try {
+ signers = getLengthPrefixedSlice(v2Block);
+ } catch (IOException e) {
+ throw new SecurityException("Failed to read list of signers", e);
+ }
+ while (signers.hasRemaining()) {
+ signerCount++;
+ try {
+ ByteBuffer signer = getLengthPrefixedSlice(signers);
+ X509Certificate[] certs = verifySigner(signer, contentDigests, certFactory);
+ signerCerts.add(certs);
+ } catch (IOException | BufferUnderflowException | SecurityException e) {
+ throw new SecurityException(
+ "Failed to parse/verify signer #" + signerCount + " block",
+ e);
+ }
+ }
+
+ if (signerCount < 1) {
+ throw new SecurityException("No signers found");
+ }
+
+ if (contentDigests.isEmpty()) {
+ throw new SecurityException("No content digests found");
+ }
+
+ verifyIntegrity(
+ contentDigests,
+ apkContents,
+ apkSigningBlockOffset,
+ centralDirOffset,
+ eocdOffset);
+
+ return signerCerts.toArray(new X509Certificate[signerCerts.size()][]);
+ }
+
+ private static X509Certificate[] verifySigner(
+ ByteBuffer signerBlock,
+ Map<Integer, byte[]> contentDigests,
+ CertificateFactory certFactory) throws SecurityException, IOException {
+ ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
+ ByteBuffer signatures = getLengthPrefixedSlice(signerBlock);
+ byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock);
+
+ int signatureCount = 0;
+ int bestSigAlgorithm = -1;
+ byte[] bestSigAlgorithmSignatureBytes = null;
+ List<Integer> signaturesSigAlgorithms = new ArrayList<>();
+ while (signatures.hasRemaining()) {
+ signatureCount++;
+ try {
+ ByteBuffer signature = getLengthPrefixedSlice(signatures);
+ if (signature.remaining() < 8) {
+ throw new SecurityException("Signature record too short");
+ }
+ int sigAlgorithm = signature.getInt();
+ signaturesSigAlgorithms.add(sigAlgorithm);
+ if (!isSupportedSignatureAlgorithm(sigAlgorithm)) {
+ continue;
+ }
+ if ((bestSigAlgorithm == -1)
+ || (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) {
+ bestSigAlgorithm = sigAlgorithm;
+ bestSigAlgorithmSignatureBytes = readLengthPrefixedByteArray(signature);
+ }
+ } catch (IOException | BufferUnderflowException e) {
+ throw new SecurityException(
+ "Failed to parse signature record #" + signatureCount,
+ e);
+ }
+ }
+ if (bestSigAlgorithm == -1) {
+ if (signatureCount == 0) {
+ throw new SecurityException("No signatures found");
+ } else {
+ throw new SecurityException("No supported signatures found");
+ }
+ }
+
+ String keyAlgorithm = getSignatureAlgorithmJcaKeyAlgorithm(bestSigAlgorithm);
+ Pair<String, ? extends AlgorithmParameterSpec> signatureAlgorithmParams =
+ getSignatureAlgorithmJcaSignatureAlgorithm(bestSigAlgorithm);
+ String jcaSignatureAlgorithm = signatureAlgorithmParams.first;
+ AlgorithmParameterSpec jcaSignatureAlgorithmParams = signatureAlgorithmParams.second;
+ boolean sigVerified;
+ try {
+ PublicKey publicKey =
+ KeyFactory.getInstance(keyAlgorithm)
+ .generatePublic(new X509EncodedKeySpec(publicKeyBytes));
+ Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
+ sig.initVerify(publicKey);
+ if (jcaSignatureAlgorithmParams != null) {
+ sig.setParameter(jcaSignatureAlgorithmParams);
+ }
+ sig.update(signedData);
+ sigVerified = sig.verify(bestSigAlgorithmSignatureBytes);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
+ | InvalidAlgorithmParameterException | SignatureException e) {
+ throw new SecurityException(
+ "Failed to verify " + jcaSignatureAlgorithm + " signature", e);
+ }
+ if (!sigVerified) {
+ throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify");
+ }
+
+ // Signature over signedData has verified.
+
+ byte[] contentDigest = null;
+ signedData.clear();
+ ByteBuffer digests = getLengthPrefixedSlice(signedData);
+ List<Integer> digestsSigAlgorithms = new ArrayList<>();
+ int digestCount = 0;
+ while (digests.hasRemaining()) {
+ digestCount++;
+ try {
+ ByteBuffer digest = getLengthPrefixedSlice(digests);
+ if (digest.remaining() < 8) {
+ throw new IOException("Record too short");
+ }
+ int sigAlgorithm = digest.getInt();
+ digestsSigAlgorithms.add(sigAlgorithm);
+ if (sigAlgorithm == bestSigAlgorithm) {
+ contentDigest = readLengthPrefixedByteArray(digest);
+ }
+ } catch (IOException | BufferUnderflowException e) {
+ throw new IOException("Failed to parse digest record #" + digestCount, e);
+ }
+ }
+
+ if (!signaturesSigAlgorithms.equals(digestsSigAlgorithms)) {
+ throw new SecurityException(
+ "Signature algorithms don't match between digests and signatures records");
+ }
+ int digestAlgorithm = getSignatureAlgorithmContentDigestAlgorithm(bestSigAlgorithm);
+ byte[] previousSignerDigest = contentDigests.put(digestAlgorithm, contentDigest);
+ if ((previousSignerDigest != null)
+ && (!MessageDigest.isEqual(previousSignerDigest, contentDigest))) {
+ throw new SecurityException(
+ getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
+ + " contents digest does not match the digest specified by a preceding signer");
+ }
+
+ ByteBuffer certificates = getLengthPrefixedSlice(signedData);
+ List<X509Certificate> certs = new ArrayList<>();
+ int certificateCount = 0;
+ while (certificates.hasRemaining()) {
+ certificateCount++;
+ byte[] encodedCert = readLengthPrefixedByteArray(certificates);
+ X509Certificate certificate;
+ try {
+ certificate = (X509Certificate)
+ certFactory.generateCertificate(new ByteArrayInputStream(encodedCert));
+ } catch (CertificateException e) {
+ throw new SecurityException("Failed to decode certificate #" + certificateCount, e);
+ }
+ certificate = new VerbatimX509Certificate(certificate, encodedCert);
+ certs.add(certificate);
+ }
+
+ if (certs.isEmpty()) {
+ throw new SecurityException("No certificates listed");
+ }
+ X509Certificate mainCertificate = certs.get(0);
+ byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
+ if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
+ throw new SecurityException(
+ "Public key mismatch between certificate and signature record");
+ }
+
+ return certs.toArray(new X509Certificate[certs.size()]);
+ }
+
+ private static void verifyIntegrity(
+ Map<Integer, byte[]> expectedDigests,
+ ByteBuffer apkContents,
+ int apkSigningBlockOffset,
+ int centralDirOffset,
+ int eocdOffset) throws SecurityException {
+
+ if (expectedDigests.isEmpty()) {
+ throw new SecurityException("No digests provided");
+ }
+
+ ByteBuffer beforeApkSigningBlock = sliceFromTo(apkContents, 0, apkSigningBlockOffset);
+ ByteBuffer centralDir = sliceFromTo(apkContents, centralDirOffset, eocdOffset);
+ // For the purposes of integrity verification, ZIP End of Central Directory's field Start of
+ // Central Directory must be considered to point to the offset of the APK Signing Block.
+ byte[] eocdBytes = new byte[apkContents.capacity() - eocdOffset];
+ apkContents.position(eocdOffset);
+ apkContents.get(eocdBytes);
+ ByteBuffer eocd = ByteBuffer.wrap(eocdBytes);
+ eocd.order(apkContents.order());
+ ZipUtils.setZipEocdCentralDirectoryOffset(eocd, apkSigningBlockOffset);
+
+ int[] digestAlgorithms = new int[expectedDigests.size()];
+ int digestAlgorithmCount = 0;
+ for (int digestAlgorithm : expectedDigests.keySet()) {
+ digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
+ digestAlgorithmCount++;
+ }
+ Map<Integer, byte[]> actualDigests;
+ try {
+ actualDigests =
+ computeContentDigests(
+ digestAlgorithms,
+ new ByteBuffer[] {beforeApkSigningBlock, centralDir, eocd});
+ } catch (DigestException e) {
+ throw new SecurityException("Failed to compute digest(s) of contents", e);
+ }
+ for (Map.Entry<Integer, byte[]> entry : expectedDigests.entrySet()) {
+ int digestAlgorithm = entry.getKey();
+ byte[] expectedDigest = entry.getValue();
+ byte[] actualDigest = actualDigests.get(digestAlgorithm);
+ if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
+ throw new SecurityException(
+ getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
+ + " digest of contents did not verify");
+ }
+ }
+ }
+
+ private static Map<Integer, byte[]> computeContentDigests(
+ int[] digestAlgorithms,
+ ByteBuffer[] contents) throws DigestException {
+ // For each digest algorithm the result is computed as follows:
+ // 1. Each segment of contents is split into consecutive chunks of 1 MB in size.
+ // The final chunk will be shorter iff the length of segment is not a multiple of 1 MB.
+ // No chunks are produced for empty (zero length) segments.
+ // 2. The digest of each chunk is computed over the concatenation of byte 0xa5, the chunk's
+ // length in bytes (uint32 little-endian) and the chunk's contents.
+ // 3. The output digest is computed over the concatenation of the byte 0x5a, the number of
+ // chunks (uint32 little-endian) and the concatenation of digests of chunks of all
+ // segments in-order.
+
+ int totalChunkCount = 0;
+ for (ByteBuffer input : contents) {
+ totalChunkCount += getChunkCount(input.remaining());
+ }
+
+ Map<Integer, byte[]> digestsOfChunks = new HashMap<>(totalChunkCount);
+ for (int digestAlgorithm : digestAlgorithms) {
+ int digestOutputSizeBytes = getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm);
+ byte[] concatenationOfChunkCountAndChunkDigests =
+ new byte[5 + totalChunkCount * digestOutputSizeBytes];
+ concatenationOfChunkCountAndChunkDigests[0] = 0x5a;
+ setUnsignedInt32LittleEndian(
+ totalChunkCount,
+ concatenationOfChunkCountAndChunkDigests,
+ 1);
+ digestsOfChunks.put(digestAlgorithm, concatenationOfChunkCountAndChunkDigests);
+ }
+
+ byte[] chunkContentPrefix = new byte[5];
+ chunkContentPrefix[0] = (byte) 0xa5;
+ int chunkIndex = 0;
+ for (ByteBuffer input : contents) {
+ while (input.hasRemaining()) {
+ int chunkSize = Math.min(input.remaining(), CHUNK_SIZE_BYTES);
+ ByteBuffer chunk = getByteBuffer(input, chunkSize);
+ for (int digestAlgorithm : digestAlgorithms) {
+ String jcaAlgorithmName =
+ getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm);
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance(jcaAlgorithmName);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(jcaAlgorithmName + " digest not supported", e);
+ }
+ chunk.clear();
+ setUnsignedInt32LittleEndian(chunk.remaining(), chunkContentPrefix, 1);
+ md.update(chunkContentPrefix);
+ md.update(chunk);
+ byte[] concatenationOfChunkCountAndChunkDigests =
+ digestsOfChunks.get(digestAlgorithm);
+ int expectedDigestSizeBytes =
+ getContentDigestAlgorithmOutputSizeBytes(digestAlgorithm);
+ int actualDigestSizeBytes = md.digest(concatenationOfChunkCountAndChunkDigests,
+ 5 + chunkIndex * expectedDigestSizeBytes, expectedDigestSizeBytes);
+ if (actualDigestSizeBytes != expectedDigestSizeBytes) {
+ throw new RuntimeException(
+ "Unexpected output size of " + md.getAlgorithm() + " digest: "
+ + actualDigestSizeBytes);
+ }
+ }
+ chunkIndex++;
+ }
+ }
+
+ Map<Integer, byte[]> result = new HashMap<>(digestAlgorithms.length);
+ for (Map.Entry<Integer, byte[]> entry : digestsOfChunks.entrySet()) {
+ int digestAlgorithm = entry.getKey();
+ byte[] input = entry.getValue();
+ String jcaAlgorithmName = getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm);
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance(jcaAlgorithmName);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(jcaAlgorithmName + " digest not supported", e);
+ }
+ byte[] output = md.digest(input);
+ result.put(digestAlgorithm, output);
+ }
+ return result;
+ }
+
+ private static final int getChunkCount(int inputSizeBytes) {
+ return (inputSizeBytes + CHUNK_SIZE_BYTES - 1) / CHUNK_SIZE_BYTES;
+ }
+
+ private static final int CHUNK_SIZE_BYTES = 1024 * 1024;
+
+ private static final int SIGNATURE_RSA_PSS_WITH_SHA256 = 0x0101;
+ private static final int SIGNATURE_RSA_PSS_WITH_SHA512 = 0x0102;
+ private static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256 = 0x0103;
+ private static final int SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512 = 0x0104;
+ private static final int SIGNATURE_ECDSA_WITH_SHA256 = 0x0201;
+ private static final int SIGNATURE_ECDSA_WITH_SHA512 = 0x0202;
+ private static final int SIGNATURE_DSA_WITH_SHA256 = 0x0301;
+ private static final int SIGNATURE_DSA_WITH_SHA512 = 0x0302;
+
+ private static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
+ private static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
+
+ private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
+ switch (sigAlgorithm) {
+ case SIGNATURE_RSA_PSS_WITH_SHA256:
+ case SIGNATURE_RSA_PSS_WITH_SHA512:
+ case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
+ case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
+ case SIGNATURE_ECDSA_WITH_SHA256:
+ case SIGNATURE_ECDSA_WITH_SHA512:
+ case SIGNATURE_DSA_WITH_SHA256:
+ case SIGNATURE_DSA_WITH_SHA512:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
+ int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
+ int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2);
+ return compareContentDigestAlgorithm(digestAlgorithm1, digestAlgorithm2);
+ }
+
+ private static int compareContentDigestAlgorithm(int digestAlgorithm1, int digestAlgorithm2) {
+ switch (digestAlgorithm1) {
+ case CONTENT_DIGEST_CHUNKED_SHA256:
+ switch (digestAlgorithm2) {
+ case CONTENT_DIGEST_CHUNKED_SHA256:
+ return 0;
+ case CONTENT_DIGEST_CHUNKED_SHA512:
+ return -1;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown digestAlgorithm2: " + digestAlgorithm2);
+ }
+ case CONTENT_DIGEST_CHUNKED_SHA512:
+ switch (digestAlgorithm2) {
+ case CONTENT_DIGEST_CHUNKED_SHA256:
+ return 1;
+ case CONTENT_DIGEST_CHUNKED_SHA512:
+ return 0;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown digestAlgorithm2: " + digestAlgorithm2);
+ }
+ default:
+ throw new IllegalArgumentException("Unknown digestAlgorithm1: " + digestAlgorithm1);
+ }
+ }
+
+ private static int getSignatureAlgorithmContentDigestAlgorithm(int sigAlgorithm) {
+ switch (sigAlgorithm) {
+ case SIGNATURE_RSA_PSS_WITH_SHA256:
+ case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
+ case SIGNATURE_ECDSA_WITH_SHA256:
+ case SIGNATURE_DSA_WITH_SHA256:
+ return CONTENT_DIGEST_CHUNKED_SHA256;
+ case SIGNATURE_RSA_PSS_WITH_SHA512:
+ case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
+ case SIGNATURE_ECDSA_WITH_SHA512:
+ case SIGNATURE_DSA_WITH_SHA512:
+ return CONTENT_DIGEST_CHUNKED_SHA512;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown signature algorithm: 0x"
+ + Long.toHexString(sigAlgorithm & 0xffffffff));
+ }
+ }
+
+ private static String getContentDigestAlgorithmJcaDigestAlgorithm(int digestAlgorithm) {
+ switch (digestAlgorithm) {
+ case CONTENT_DIGEST_CHUNKED_SHA256:
+ return "SHA-256";
+ case CONTENT_DIGEST_CHUNKED_SHA512:
+ return "SHA-512";
+ default:
+ throw new IllegalArgumentException(
+ "Unknown content digest algorthm: " + digestAlgorithm);
+ }
+ }
+
+ private static int getContentDigestAlgorithmOutputSizeBytes(int digestAlgorithm) {
+ switch (digestAlgorithm) {
+ case CONTENT_DIGEST_CHUNKED_SHA256:
+ return 256 / 8;
+ case CONTENT_DIGEST_CHUNKED_SHA512:
+ return 512 / 8;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown content digest algorthm: " + digestAlgorithm);
+ }
+ }
+
+ private static String getSignatureAlgorithmJcaKeyAlgorithm(int sigAlgorithm) {
+ switch (sigAlgorithm) {
+ case SIGNATURE_RSA_PSS_WITH_SHA256:
+ case SIGNATURE_RSA_PSS_WITH_SHA512:
+ case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
+ case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
+ return "RSA";
+ case SIGNATURE_ECDSA_WITH_SHA256:
+ case SIGNATURE_ECDSA_WITH_SHA512:
+ return "EC";
+ case SIGNATURE_DSA_WITH_SHA256:
+ case SIGNATURE_DSA_WITH_SHA512:
+ return "DSA";
+ default:
+ throw new IllegalArgumentException(
+ "Unknown signature algorithm: 0x"
+ + Long.toHexString(sigAlgorithm & 0xffffffff));
+ }
+ }
+
+ private static Pair<String, ? extends AlgorithmParameterSpec>
+ getSignatureAlgorithmJcaSignatureAlgorithm(int sigAlgorithm) {
+ switch (sigAlgorithm) {
+ case SIGNATURE_RSA_PSS_WITH_SHA256:
+ return Pair.create(
+ "SHA256withRSA/PSS",
+ new PSSParameterSpec(
+ "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 256 / 8, 1));
+ case SIGNATURE_RSA_PSS_WITH_SHA512:
+ return Pair.create(
+ "SHA512withRSA/PSS",
+ new PSSParameterSpec(
+ "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1));
+ case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256:
+ return Pair.create("SHA256withRSA", null);
+ case SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512:
+ return Pair.create("SHA512withRSA", null);
+ case SIGNATURE_ECDSA_WITH_SHA256:
+ return Pair.create("SHA256withECDSA", null);
+ case SIGNATURE_ECDSA_WITH_SHA512:
+ return Pair.create("SHA512withECDSA", null);
+ case SIGNATURE_DSA_WITH_SHA256:
+ return Pair.create("SHA256withDSA", null);
+ case SIGNATURE_DSA_WITH_SHA512:
+ return Pair.create("SHA512withDSA", null);
+ default:
+ throw new IllegalArgumentException(
+ "Unknown signature algorithm: 0x"
+ + Long.toHexString(sigAlgorithm & 0xffffffff));
+ }
+ }
+
+ /**
+ * Returns new byte buffer whose content is a shared subsequence of this buffer's content
+ * between the specified start (inclusive) and end (exclusive) positions. As opposed to
+ * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
+ * buffer's byte order.
+ */
+ private static ByteBuffer sliceFromTo(ByteBuffer source, int start, int end) {
+ if (start < 0) {
+ throw new IllegalArgumentException("start: " + start);
+ }
+ if (end < start) {
+ throw new IllegalArgumentException("end < start: " + end + " < " + start);
+ }
+ int capacity = source.capacity();
+ if (end > source.capacity()) {
+ throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity);
+ }
+ int originalLimit = source.limit();
+ int originalPosition = source.position();
+ try {
+ source.position(0);
+ source.limit(end);
+ source.position(start);
+ ByteBuffer result = source.slice();
+ result.order(source.order());
+ return result;
+ } finally {
+ source.position(0);
+ source.limit(originalLimit);
+ source.position(originalPosition);
+ }
+ }
+
+ /**
+ * Relative <em>get</em> method for reading {@code size} number of bytes from the current
+ * position of this buffer.
+ *
+ * <p>This method reads the next {@code size} bytes at this buffer's current position,
+ * returning them as a {@code ByteBuffer} with start set to 0, limit and capacity set to
+ * {@code size}, byte order set to this buffer's byte order; and then increments the position by
+ * {@code size}.
+ */
+ private static ByteBuffer getByteBuffer(ByteBuffer source, int size)
+ throws BufferUnderflowException {
+ if (size < 0) {
+ throw new IllegalArgumentException("size: " + size);
+ }
+ int originalLimit = source.limit();
+ int position = source.position();
+ int limit = position + size;
+ if ((limit < position) || (limit > originalLimit)) {
+ throw new BufferUnderflowException();
+ }
+ source.limit(limit);
+ try {
+ ByteBuffer result = source.slice();
+ result.order(source.order());
+ source.position(limit);
+ return result;
+ } finally {
+ source.limit(originalLimit);
+ }
+ }
+
+ private static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws IOException {
+ if (source.remaining() < 4) {
+ throw new IOException(
+ "Remaining buffer too short to contain length of length-prefixed field."
+ + " Remaining: " + source.remaining());
+ }
+ int len = source.getInt();
+ if (len < 0) {
+ throw new IllegalArgumentException("Negative length");
+ } else if (len > source.remaining()) {
+ throw new IOException("Length-prefixed field longer than remaining buffer."
+ + " Field length: " + len + ", remaining: " + source.remaining());
+ }
+ return getByteBuffer(source, len);
+ }
+
+ private static byte[] readLengthPrefixedByteArray(ByteBuffer buf) throws IOException {
+ int len = buf.getInt();
+ if (len < 0) {
+ throw new IOException("Negative length");
+ } else if (len > buf.remaining()) {
+ throw new IOException("Underflow while reading length-prefixed value. Length: " + len
+ + ", available: " + buf.remaining());
+ }
+ byte[] result = new byte[len];
+ buf.get(result);
+ return result;
+ }
+
+ private static void setUnsignedInt32LittleEndian(int value, byte[] result, int offset) {
+ result[offset] = (byte) (value & 0xff);
+ result[offset + 1] = (byte) ((value >>> 8) & 0xff);
+ result[offset + 2] = (byte) ((value >>> 16) & 0xff);
+ result[offset + 3] = (byte) ((value >>> 24) & 0xff);
+ }
+
+ private static final long APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42L;
+ private static final long APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041L;
+ private static final int APK_SIG_BLOCK_MIN_SIZE = 32;
+
+ private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
+
+ private static int findApkSigningBlock(ByteBuffer apkContents, int centralDirOffset)
+ throws SignatureNotFoundException {
+ checkByteOrderLittleEndian(apkContents);
+
+ // FORMAT:
+ // OFFSET DATA TYPE DESCRIPTION
+ // * @+0 bytes uint64: size in bytes (excluding this field)
+ // * @+8 bytes payload
+ // * @-24 bytes uint64: size in bytes (same as the one above)
+ // * @-16 bytes uint128: magic
+
+ if (centralDirOffset < APK_SIG_BLOCK_MIN_SIZE) {
+ throw new SignatureNotFoundException(
+ "APK too small for APK Signing Block. ZIP Central Directory offset: "
+ + centralDirOffset);
+ }
+ // Check magic field present
+ if ((apkContents.getLong(centralDirOffset - 16) != APK_SIG_BLOCK_MAGIC_LO)
+ || (apkContents.getLong(centralDirOffset - 8) != APK_SIG_BLOCK_MAGIC_HI)) {
+ throw new SignatureNotFoundException(
+ "No APK Signing Block before ZIP Central Directory");
+ }
+ // Read and compare size fields
+ long apkSigBlockSizeLong = apkContents.getLong(centralDirOffset - 24);
+ if ((apkSigBlockSizeLong < 24) || (apkSigBlockSizeLong > Integer.MAX_VALUE - 8)) {
+ throw new SignatureNotFoundException(
+ "APK Signing Block size out of range: " + apkSigBlockSizeLong);
+ }
+ int apkSigBlockSizeFromFooter = (int) apkSigBlockSizeLong;
+ int totalSize = apkSigBlockSizeFromFooter + 8;
+ int apkSigBlockOffset = centralDirOffset - totalSize;
+ if (apkSigBlockOffset < 0) {
+ throw new SignatureNotFoundException(
+ "APK Signing Block offset out of range: " + apkSigBlockOffset);
+ }
+ long apkSigBlockSizeFromHeader = apkContents.getLong(apkSigBlockOffset);
+ if (apkSigBlockSizeFromHeader != apkSigBlockSizeFromFooter) {
+ throw new SignatureNotFoundException(
+ "APK Signing Block sizes in header and footer do not match: "
+ + apkSigBlockSizeFromHeader + " vs " + apkSigBlockSizeFromFooter);
+ }
+ return apkSigBlockOffset;
+ }
+
+ private static ByteBuffer findApkSignatureSchemeV2Block(ByteBuffer apkSigningBlock)
+ throws SignatureNotFoundException {
+ checkByteOrderLittleEndian(apkSigningBlock);
+ // FORMAT:
+ // OFFSET DATA TYPE DESCRIPTION
+ // * @+0 bytes uint64: size in bytes (excluding this field)
+ // * @+8 bytes pairs
+ // * @-24 bytes uint64: size in bytes (same as the one above)
+ // * @-16 bytes uint128: magic
+ ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24);
+
+ int entryCount = 0;
+ while (pairs.hasRemaining()) {
+ entryCount++;
+ if (pairs.remaining() < 8) {
+ throw new SignatureNotFoundException(
+ "Insufficient data to read size of APK Signing Block entry #" + entryCount);
+ }
+ long lenLong = pairs.getLong();
+ if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) {
+ throw new SignatureNotFoundException(
+ "APK Signing Block entry #" + entryCount
+ + " size out of range: " + lenLong);
+ }
+ int len = (int) lenLong;
+ int nextEntryPos = pairs.position() + len;
+ if (len > pairs.remaining()) {
+ throw new SignatureNotFoundException(
+ "APK Signing Block entry #" + entryCount + " size out of range: " + len
+ + ", available: " + pairs.remaining());
+ }
+ int id = pairs.getInt();
+ if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {
+ return getByteBuffer(pairs, len - 4);
+ }
+ pairs.position(nextEntryPos);
+ }
+
+ throw new SignatureNotFoundException(
+ "No APK Signature Scheme v2 block in APK Signing Block");
+ }
+
+ private static void checkByteOrderLittleEndian(ByteBuffer buffer) {
+ if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
+ throw new IllegalArgumentException("ByteBuffer byte order must be little endian");
+ }
+ }
+
+ public static class SignatureNotFoundException extends Exception {
+ public SignatureNotFoundException(String message) {
+ super(message);
+ }
+
+ public SignatureNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ /**
+ * For legacy reasons we need to return exactly the original encoded certificate bytes, instead
+ * of letting the underlying implementation have a shot at re-encoding the data.
+ */
+ private static class VerbatimX509Certificate extends WrappedX509Certificate {
+ private byte[] encodedVerbatim;
+
+ public VerbatimX509Certificate(X509Certificate wrapped, byte[] encodedVerbatim) {
+ super(wrapped);
+ this.encodedVerbatim = encodedVerbatim;
+ }
+
+ @Override
+ public byte[] getEncoded() throws CertificateEncodingException {
+ return encodedVerbatim;
+ }
+ }
+
+ private static class WrappedX509Certificate extends X509Certificate {
+ private final X509Certificate wrapped;
+
+ public WrappedX509Certificate(X509Certificate wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ @Override
+ public Set<String> getCriticalExtensionOIDs() {
+ return wrapped.getCriticalExtensionOIDs();
+ }
+
+ @Override
+ public byte[] getExtensionValue(String oid) {
+ return wrapped.getExtensionValue(oid);
+ }
+
+ @Override
+ public Set<String> getNonCriticalExtensionOIDs() {
+ return wrapped.getNonCriticalExtensionOIDs();
+ }
+
+ @Override
+ public boolean hasUnsupportedCriticalExtension() {
+ return wrapped.hasUnsupportedCriticalExtension();
+ }
+
+ @Override
+ public void checkValidity()
+ throws CertificateExpiredException, CertificateNotYetValidException {
+ wrapped.checkValidity();
+ }
+
+ @Override
+ public void checkValidity(Date date)
+ throws CertificateExpiredException, CertificateNotYetValidException {
+ wrapped.checkValidity(date);
+ }
+
+ @Override
+ public int getVersion() {
+ return wrapped.getVersion();
+ }
+
+ @Override
+ public BigInteger getSerialNumber() {
+ return wrapped.getSerialNumber();
+ }
+
+ @Override
+ public Principal getIssuerDN() {
+ return wrapped.getIssuerDN();
+ }
+
+ @Override
+ public Principal getSubjectDN() {
+ return wrapped.getSubjectDN();
+ }
+
+ @Override
+ public Date getNotBefore() {
+ return wrapped.getNotBefore();
+ }
+
+ @Override
+ public Date getNotAfter() {
+ return wrapped.getNotAfter();
+ }
+
+ @Override
+ public byte[] getTBSCertificate() throws CertificateEncodingException {
+ return wrapped.getTBSCertificate();
+ }
+
+ @Override
+ public byte[] getSignature() {
+ return wrapped.getSignature();
+ }
+
+ @Override
+ public String getSigAlgName() {
+ return wrapped.getSigAlgName();
+ }
+
+ @Override
+ public String getSigAlgOID() {
+ return wrapped.getSigAlgOID();
+ }
+
+ @Override
+ public byte[] getSigAlgParams() {
+ return wrapped.getSigAlgParams();
+ }
+
+ @Override
+ public boolean[] getIssuerUniqueID() {
+ return wrapped.getIssuerUniqueID();
+ }
+
+ @Override
+ public boolean[] getSubjectUniqueID() {
+ return wrapped.getSubjectUniqueID();
+ }
+
+ @Override
+ public boolean[] getKeyUsage() {
+ return wrapped.getKeyUsage();
+ }
+
+ @Override
+ public int getBasicConstraints() {
+ return wrapped.getBasicConstraints();
+ }
+
+ @Override
+ public byte[] getEncoded() throws CertificateEncodingException {
+ return wrapped.getEncoded();
+ }
+
+ @Override
+ public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException,
+ InvalidKeyException, NoSuchProviderException, SignatureException {
+ wrapped.verify(key);
+ }
+
+ @Override
+ public void verify(PublicKey key, String sigProvider)
+ throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+ NoSuchProviderException, SignatureException {
+ wrapped.verify(key, sigProvider);
+ }
+
+ @Override
+ public String toString() {
+ return wrapped.toString();
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ return wrapped.getPublicKey();
+ }
+ }
+}
diff --git a/core/java/android/util/apk/ZipUtils.java b/core/java/android/util/apk/ZipUtils.java
new file mode 100644
index 0000000..a383d5c
--- /dev/null
+++ b/core/java/android/util/apk/ZipUtils.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.util.apk;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Assorted ZIP format helpers.
+ *
+ * <p>NOTE: Most helper methods operating on {@code ByteBuffer} instances except that the byte
+ * order of these buffers is little-endian.
+ */
+abstract class ZipUtils {
+ private ZipUtils() {}
+
+ private static final int ZIP_EOCD_REC_MIN_SIZE = 22;
+ private static final int ZIP_EOCD_REC_SIG = 0x06054b50;
+ private static final int ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET = 12;
+ private static final int ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET = 16;
+ private static final int ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET = 20;
+
+ private static final int ZIP64_EOCD_LOCATOR_SIZE = 20;
+ private static final int ZIP64_EOCD_LOCATOR_SIG = 0x07064b50;
+
+ private static final int UINT32_MAX_VALUE = 0xffff;
+
+ /**
+ * Returns the position at which ZIP End of Central Directory record starts in the provided
+ * buffer or {@code -1} if the record is not present.
+ *
+ * <p>NOTE: Byte order of {@code zipContents} must be little-endian.
+ */
+ public static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) {
+ assertByteOrderLittleEndian(zipContents);
+
+ // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive.
+ // The record can be identified by its 4-byte signature/magic which is located at the very
+ // beginning of the record. A complication is that the record is variable-length because of
+ // the comment field.
+ // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from
+ // end of the buffer for the EOCD record signature. Whenever we find a signature, we check
+ // the candidate record's comment length is such that the remainder of the record takes up
+ // exactly the remaining bytes in the buffer. The search is bounded because the maximum
+ // size of the comment field is 65535 bytes because the field is an unsigned 32-bit number.
+
+ int archiveSize = zipContents.capacity();
+ if (archiveSize < ZIP_EOCD_REC_MIN_SIZE) {
+ System.out.println("File size smaller than EOCD min size");
+ return -1;
+ }
+ int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT32_MAX_VALUE);
+ int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE;
+ for (int expectedCommentLength = 0; expectedCommentLength < maxCommentLength;
+ expectedCommentLength++) {
+ int eocdStartPos = eocdWithEmptyCommentStartPosition - expectedCommentLength;
+ if (zipContents.getInt(eocdStartPos) == ZIP_EOCD_REC_SIG) {
+ int actualCommentLength =
+ getUnsignedInt16(
+ zipContents, eocdStartPos + ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET);
+ if (actualCommentLength == expectedCommentLength) {
+ return eocdStartPos;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Returns {@code true} if the provided buffer contains a ZIP64 End of Central Directory
+ * Locator.
+ *
+ * <p>NOTE: Byte order of {@code zipContents} must be little-endian.
+ */
+ public static final boolean isZip64EndOfCentralDirectoryLocatorPresent(
+ ByteBuffer zipContents, int zipEndOfCentralDirectoryPosition) {
+ assertByteOrderLittleEndian(zipContents);
+
+ // ZIP64 End of Central Directory Locator immediately precedes the ZIP End of Central
+ // Directory Record.
+
+ int locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE;
+ if (locatorPosition < 0) {
+ return false;
+ }
+
+ return zipContents.getInt(locatorPosition) == ZIP64_EOCD_LOCATOR_SIG;
+ }
+
+ /**
+ * Returns the offset of the start of the ZIP Central Directory in the archive.
+ *
+ * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
+ */
+ public static long getZipEocdCentralDirectoryOffset(ByteBuffer zipEndOfCentralDirectory) {
+ assertByteOrderLittleEndian(zipEndOfCentralDirectory);
+ return getUnsignedInt32(
+ zipEndOfCentralDirectory,
+ zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET);
+ }
+
+ /**
+ * Sets the offset of the start of the ZIP Central Directory in the archive.
+ *
+ * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
+ */
+ public static void setZipEocdCentralDirectoryOffset(
+ ByteBuffer zipEndOfCentralDirectory, long offset) {
+ assertByteOrderLittleEndian(zipEndOfCentralDirectory);
+ setUnsignedInt32(
+ zipEndOfCentralDirectory,
+ zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET,
+ offset);
+ }
+
+ /**
+ * Returns the size (in bytes) of the ZIP Central Directory.
+ *
+ * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
+ */
+ public static long getZipEocdCentralDirectorySizeBytes(ByteBuffer zipEndOfCentralDirectory) {
+ assertByteOrderLittleEndian(zipEndOfCentralDirectory);
+ return getUnsignedInt32(
+ zipEndOfCentralDirectory,
+ zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET);
+ }
+
+ private static void assertByteOrderLittleEndian(ByteBuffer buffer) {
+ if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
+ throw new IllegalArgumentException("ByteBuffer byte order must be little endian");
+ }
+ }
+
+ private static int getUnsignedInt16(ByteBuffer buffer, int offset) {
+ return buffer.getShort(offset) & 0xffff;
+ }
+
+ private static long getUnsignedInt32(ByteBuffer buffer, int offset) {
+ return buffer.getInt(offset) & 0xffffffffL;
+ }
+
+ private static void setUnsignedInt32(ByteBuffer buffer, int offset, long value) {
+ if ((value < 0) || (value > 0xffffffffL)) {
+ throw new IllegalArgumentException("uint32 value of out range: " + value);
+ }
+ buffer.putInt(buffer.position() + offset, (int) value);
+ }
+}
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index fd57806..302a08d 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -18,7 +18,6 @@
package android.util.jar;
import dalvik.system.CloseGuard;
-import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -30,9 +29,7 @@
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
import java.util.jar.JarFile;
-import java.util.jar.Manifest;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -59,7 +56,13 @@
private final CloseGuard guard = CloseGuard.get();
private boolean closed;
- public StrictJarFile(String fileName) throws IOException, SecurityException {
+ public StrictJarFile(String fileName)
+ throws IOException, SecurityException {
+ this(fileName, true);
+ }
+
+ public StrictJarFile(String fileName, boolean verify)
+ throws IOException, SecurityException {
this.nativeHandle = nativeOpenJarFile(fileName);
this.raf = new RandomAccessFile(fileName, "r");
@@ -67,17 +70,23 @@
// Read the MANIFEST and signature files up front and try to
// parse them. We never want to accept a JAR File with broken signatures
// or manifests, so it's best to throw as early as possible.
- HashMap<String, byte[]> metaEntries = getMetaEntries();
- this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
- this.verifier = new StrictJarVerifier(fileName, manifest, metaEntries);
- Set<String> files = manifest.getEntries().keySet();
- for (String file : files) {
- if (findEntry(file) == null) {
- throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+ if (verify) {
+ HashMap<String, byte[]> metaEntries = getMetaEntries();
+ this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
+ this.verifier = new StrictJarVerifier(fileName, manifest, metaEntries);
+ Set<String> files = manifest.getEntries().keySet();
+ for (String file : files) {
+ if (findEntry(file) == null) {
+ throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+ }
}
- }
- isSigned = verifier.readCertificates() && verifier.isSignedJar();
+ isSigned = verifier.readCertificates() && verifier.isSignedJar();
+ } else {
+ isSigned = false;
+ this.manifest = null;
+ this.verifier = null;
+ }
} catch (IOException | SecurityException e) {
nativeClose(this.nativeHandle);
IoUtils.closeQuietly(this.raf);
diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java
index ca2aec1..0546a5f 100644
--- a/core/java/android/util/jar/StrictJarVerifier.java
+++ b/core/java/android/util/jar/StrictJarVerifier.java
@@ -32,8 +32,12 @@
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
+import android.util.ArraySet;
+import android.util.apk.ApkSignatureSchemeV2Verifier;
import libcore.io.Base64;
import sun.security.jca.Providers;
import sun.security.pkcs.PKCS7;
@@ -353,6 +357,43 @@
return;
}
+ // Check whether APK Signature Scheme v2 signature was stripped.
+ String apkSignatureSchemeIdList =
+ attributes.getValue(
+ ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
+ if (apkSignatureSchemeIdList != null) {
+ // This field contains a comma-separated list of APK signature scheme IDs which were
+ // used to sign this APK. If an ID is known to us, it means signatures of that scheme
+ // were stripped from the APK because otherwise we wouldn't have fallen back to
+ // verifying the APK using the JAR signature scheme.
+ boolean v2SignatureGenerated = false;
+ StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
+ while (tokenizer.hasMoreTokens()) {
+ String idText = tokenizer.nextToken().trim();
+ if (idText.isEmpty()) {
+ continue;
+ }
+ int id;
+ try {
+ id = Integer.parseInt(idText);
+ } catch (Exception ignored) {
+ continue;
+ }
+ if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
+ // This APK was supposed to be signed with APK Signature Scheme v2 but no such
+ // signature was found.
+ v2SignatureGenerated = true;
+ break;
+ }
+ }
+
+ if (v2SignatureGenerated) {
+ throw new SecurityException(signatureFile + " indicates " + jarName + " is signed"
+ + " using APK Signature Scheme v2, but no such signature was found."
+ + " Signature stripped?");
+ }
+ }
+
// Do we actually have any signatures to look at?
if (attributes.get(Attributes.Name.SIGNATURE_VERSION) == null) {
return;
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 9e478c1..9231394 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -25,6 +25,8 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
+import com.android.internal.os.IResultReceiver;
+
/**
* API back to a client window that the Window Manager uses to inform it of
* interesting things happening.
@@ -83,4 +85,9 @@
* Called for non-application windows when the enter animation has completed.
*/
void dispatchWindowShown();
+
+ /**
+ * Called when Keyboard Shortcuts are requested for the window.
+ */
+ void requestAppKeyboardShortcuts(IResultReceiver receiver);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 84d312d..b045c17 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -17,6 +17,7 @@
package android.view;
import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -370,4 +371,11 @@
* @param alpha The translucency of the dim layer, between 0 and 1.
*/
void setResizeDimLayer(boolean visible, int targetStackId, float alpha);
+
+ /**
+ * Requests Keyboard Shortcuts from the displayed window.
+ *
+ * @param receiver The receiver to deliver the results to.
+ */
+ void requestAppKeyboardShortcuts(IResultReceiver receiver);
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index f037958..51e1f4b 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1856,6 +1856,9 @@
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_WAKEUP:
case KeyEvent.KEYCODE_PAIRING:
+ case KeyEvent.KEYCODE_STEM_1:
+ case KeyEvent.KEYCODE_STEM_2:
+ case KeyEvent.KEYCODE_STEM_3:
return true;
}
return false;
diff --git a/core/java/android/view/KeyboardShortcutGroup.java b/core/java/android/view/KeyboardShortcutGroup.java
new file mode 100644
index 0000000..013255b
--- /dev/null
+++ b/core/java/android/view/KeyboardShortcutGroup.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+/**
+ * A group of {@link KeyboardShortcutInfo}.
+ */
+public final class KeyboardShortcutGroup implements Parcelable {
+ private final CharSequence mLabel;
+ private final List<KeyboardShortcutInfo> mItems;
+
+ /**
+ * @param label The title to be used for this group, or null if there is none.
+ * @param items The set of items to be included.
+ */
+ public KeyboardShortcutGroup(@Nullable CharSequence label,
+ @NonNull List<KeyboardShortcutInfo> items) {
+ mLabel = label;
+ mItems = new ArrayList<>(checkNotNull(items));
+ }
+
+ /**
+ * @param label The title to be used for this group, or null if there is none.
+ */
+ public KeyboardShortcutGroup(@Nullable CharSequence label) {
+ this(label, Collections.<KeyboardShortcutInfo>emptyList());
+ }
+
+ private KeyboardShortcutGroup(Parcel source) {
+ mItems = new ArrayList<>();
+ mLabel = source.readCharSequence();
+ source.readTypedList(mItems, KeyboardShortcutInfo.CREATOR);
+ }
+
+ /**
+ * Returns the label to be used to describe this group.
+ */
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * Returns the list of items included in this group.
+ */
+ public List<KeyboardShortcutInfo> getItems() {
+ return mItems;
+ }
+
+ /**
+ * Adds an item to the existing list.
+ *
+ * @param item The item to be added.
+ */
+ public void addItem(KeyboardShortcutInfo item) {
+ mItems.add(item);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeCharSequence(mLabel);
+ dest.writeTypedList(mItems);
+ }
+
+ public static final Creator<KeyboardShortcutGroup> CREATOR =
+ new Creator<KeyboardShortcutGroup>() {
+ public KeyboardShortcutGroup createFromParcel(Parcel source) {
+ return new KeyboardShortcutGroup(source);
+ }
+ public KeyboardShortcutGroup[] newArray(int size) {
+ return new KeyboardShortcutGroup[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java
new file mode 100644
index 0000000..2c9006d
--- /dev/null
+++ b/core/java/android/view/KeyboardShortcutInfo.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view;
+
+import android.annotation.Nullable;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static java.lang.Character.MIN_VALUE;
+
+/**
+ * Information about a Keyboard Shortcut.
+ */
+public final class KeyboardShortcutInfo implements Parcelable {
+ private final CharSequence mLabel;
+ private final Icon mIcon;
+ private final char mBaseCharacter;
+ private final int mModifiers;
+
+ /**
+ * @param label The label that identifies the action performed by this shortcut.
+ * @param icon An icon that identifies the action performed by this shortcut.
+ * @param baseCharacter The character that triggers the shortcut.
+ * @param modifiers The set of modifiers that, combined with the key, trigger the shortcut.
+ * These should be a combination of {@link KeyEvent#META_CTRL_ON},
+ * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_META_ON} and
+ * {@link KeyEvent#META_ALT_ON}.
+ *
+ * @hide
+ */
+ public KeyboardShortcutInfo(
+ @Nullable CharSequence label, @Nullable Icon icon, char baseCharacter, int modifiers) {
+ mLabel = label;
+ mIcon = icon;
+ checkArgument(baseCharacter != MIN_VALUE);
+ mBaseCharacter = baseCharacter;
+ mModifiers = modifiers;
+ }
+
+ /**
+ * Convenience constructor for shortcuts with a label and no icon.
+ *
+ * @param label The label that identifies the action performed by this shortcut.
+ * @param baseCharacter The character that triggers the shortcut.
+ * @param modifiers The set of modifiers that, combined with the key, trigger the shortcut.
+ * These should be a combination of {@link KeyEvent#META_CTRL_ON},
+ * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_META_ON} and
+ * {@link KeyEvent#META_ALT_ON}.
+ */
+ public KeyboardShortcutInfo(CharSequence label, char baseCharacter, int modifiers) {
+ mLabel = label;
+ checkArgument(baseCharacter != MIN_VALUE);
+ mBaseCharacter = baseCharacter;
+ mModifiers = modifiers;
+ mIcon = null;
+ }
+
+ private KeyboardShortcutInfo(Parcel source) {
+ mLabel = source.readCharSequence();
+ mIcon = (Icon) source.readParcelable(null);
+ mBaseCharacter = (char) source.readInt();
+ mModifiers = source.readInt();
+ }
+
+ /**
+ * Returns the label to be used to describe this shortcut.
+ */
+ @Nullable
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * Returns the icon to be used to describe this shortcut.
+ *
+ * @hide
+ */
+ @Nullable
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Returns the base character that, combined with the modifiers, triggers this shortcut.
+ */
+ public char getBaseCharacter() {
+ return mBaseCharacter;
+ }
+
+ /**
+ * Returns the set of modifiers that, combined with the key, trigger this shortcut.
+ */
+ public int getModifiers() {
+ return mModifiers;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeCharSequence(mLabel);
+ dest.writeParcelable(mIcon, 0);
+ dest.writeInt(mBaseCharacter);
+ dest.writeInt(mModifiers);
+ }
+
+ public static final Creator<KeyboardShortcutInfo> CREATOR =
+ new Creator<KeyboardShortcutInfo>() {
+ public KeyboardShortcutInfo createFromParcel(Parcel source) {
+ return new KeyboardShortcutInfo(source);
+ }
+ public KeyboardShortcutInfo[] newArray(int size) {
+ return new KeyboardShortcutInfo[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 68f1ac3..0b8018b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -79,10 +79,11 @@
import android.util.SuperNotCalledException;
import android.util.TypedValue;
import android.view.ContextMenu.ContextMenuInfo;
-import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.AccessibilityIterators.CharacterTextSegmentIterator;
-import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
+import android.view.AccessibilityIterators.TextSegmentIterator;
+import android.view.AccessibilityIterators.WordTextSegmentIterator;
+import android.view.ViewGroup.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityEventSource;
import android.view.accessibility.AccessibilityManager;
@@ -3592,9 +3593,6 @@
private int[] mDrawableState = null;
- /** Whether draw() is currently being called. */
- private boolean mInDraw = false;
-
ViewOutlineProvider mOutlineProvider = ViewOutlineProvider.BACKGROUND;
/**
@@ -16473,8 +16471,6 @@
*/
@CallSuper
public void draw(Canvas canvas) {
- mInDraw = true;
-
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
@@ -16519,7 +16515,6 @@
onDrawForeground(canvas);
// we're done...
- mInDraw = false;
return;
}
@@ -16667,8 +16662,6 @@
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
-
- mInDraw = false;
}
/**
@@ -17113,8 +17106,7 @@
*/
@Override
public void invalidateDrawable(@NonNull Drawable drawable) {
- // Don't invalidate if a drawable changes during drawing.
- if (verifyDrawable(drawable) && !mInDraw) {
+ if (verifyDrawable(drawable)) {
final Rect dirty = drawable.getDirtyBounds();
final int scrollX = mScrollX;
final int scrollY = mScrollY;
@@ -17267,8 +17259,10 @@
*/
@CallSuper
protected boolean verifyDrawable(Drawable who) {
- return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who)
- || (mForegroundInfo != null && mForegroundInfo.mDrawable == 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.
+ return who == mBackground || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
}
/**
@@ -21715,6 +21709,13 @@
}
/**
+ * @hide
+ */
+ public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> data) {
+ // Do nothing.
+ }
+
+ /**
* Interface definition for a callback to be invoked when a hardware key event is
* dispatched to this view. The callback will be invoked before the key event is
* given to the view. This is only useful for hardware keyboards; a software input
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1c24392..f674298 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5359,6 +5359,9 @@
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;
@@ -5372,8 +5375,16 @@
&& (theParent != this)) {
if (offsetFromChildToParent) {
- rect.offset(descendant.mLeft - descendant.mScrollX,
- descendant.mTop - descendant.mScrollY);
+ 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);
+
if (clipToBounds) {
View p = (View) theParent;
boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
@@ -5391,8 +5402,16 @@
rect.setEmpty();
}
}
- rect.offset(descendant.mScrollX - descendant.mLeft,
- descendant.mScrollY - descendant.mTop);
+ 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);
}
descendant = (View) theParent;
@@ -5403,11 +5422,26 @@
// to get into our coordinate space
if (theParent == this) {
if (offsetFromChildToParent) {
- rect.offset(descendant.mLeft - descendant.mScrollX,
- descendant.mTop - descendant.mScrollY);
+ 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);
} else {
- rect.offset(descendant.mScrollX - descendant.mLeft,
- descendant.mScrollY - descendant.mTop);
+ 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);
}
} else {
throw new IllegalArgumentException("parameter must be a descendant of this view");
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0fb3951..f8e96d6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -17,6 +17,9 @@
package android.view;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import android.Manifest;
import android.animation.LayoutTransition;
@@ -79,6 +82,7 @@
import android.widget.Scroller;
import com.android.internal.R;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.PhoneFallbackEventHandler;
import com.android.internal.view.BaseSurfaceHolder;
@@ -1336,6 +1340,17 @@
host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */));
}
+ private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) {
+ return lp.type == TYPE_STATUS_BAR_PANEL
+ || lp.type == TYPE_INPUT_METHOD
+ || lp.type == TYPE_VOLUME_OVERLAY;
+ }
+
+ private int dipToPx(int dip) {
+ final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+ return (int) (displayMetrics.density * dip + 0.5f);
+ }
+
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
@@ -1390,18 +1405,16 @@
mFullRedrawNeeded = true;
mLayoutRequested = true;
- if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
- || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
+ if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
- DisplayMetrics packageMetrics =
- mView.getContext().getResources().getDisplayMetrics();
- desiredWindowWidth = packageMetrics.widthPixels;
- desiredWindowHeight = packageMetrics.heightPixels;
+ Configuration config = mContext.getResources().getConfiguration();
+ desiredWindowWidth = dipToPx(config.screenWidthDp);
+ desiredWindowHeight = dipToPx(config.screenHeightDp);
}
// We used to use the following condition to choose 32 bits drawing caches:
@@ -1485,16 +1498,21 @@
if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
insetsChanged = true;
}
- if ((lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
- || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT)
- && (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
- || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD)) {
+ if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
+ || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;
- // NOTE -- system code, won't try to do compat mode.
- Point size = new Point();
- mDisplay.getRealSize(size);
- desiredWindowWidth = size.x;
- desiredWindowHeight = size.y;
+
+ if (shouldUseDisplaySize(lp)) {
+ // NOTE -- system code, won't try to do compat mode.
+ Point size = new Point();
+ mDisplay.getRealSize(size);
+ desiredWindowWidth = size.x;
+ desiredWindowHeight = size.y;
+ } else {
+ Configuration config = res.getConfiguration();
+ desiredWindowWidth = dipToPx(config.screenWidthDp);
+ desiredWindowHeight = dipToPx(config.screenHeightDp);
+ }
}
}
@@ -3234,6 +3252,7 @@
private final static int MSG_WINDOW_MOVED = 23;
private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24;
private final static int MSG_DISPATCH_WINDOW_SHOWN = 25;
+ private final static int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26;
final class ViewRootHandler extends Handler {
@Override
@@ -3510,7 +3529,11 @@
} break;
case MSG_DISPATCH_WINDOW_SHOWN: {
handleDispatchWindowShown();
- }
+ } break;
+ case MSG_REQUEST_KEYBOARD_SHORTCUTS: {
+ IResultReceiver receiver = (IResultReceiver) msg.obj;
+ handleRequestKeyboardShortcuts(receiver);
+ } break;
}
}
}
@@ -5403,6 +5426,19 @@
mAttachInfo.mTreeObserver.dispatchOnWindowShown();
}
+ public void handleRequestKeyboardShortcuts(IResultReceiver receiver) {
+ Bundle data = new Bundle();
+ ArrayList<KeyboardShortcutGroup> list = new ArrayList<>();
+ if (mView != null) {
+ mView.requestKeyboardShortcuts(list);
+ }
+ data.putParcelableArrayList(WindowManager.PARCEL_KEY_SHORTCUTS_ARRAY, list);
+ try {
+ receiver.send(0, data);
+ } catch (RemoteException e) {
+ }
+ }
+
public void getLastTouchPoint(Point outLocation) {
outLocation.x = (int) mLastTouchPoint.x;
outLocation.y = (int) mLastTouchPoint.y;
@@ -6332,6 +6368,10 @@
}
}
+ public void dispatchRequestKeyboardShortcuts(IResultReceiver receiver) {
+ mHandler.obtainMessage(MSG_REQUEST_KEYBOARD_SHORTCUTS, receiver).sendToTarget();
+ }
+
/**
* Post a callback to send a
* {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
@@ -6905,6 +6945,14 @@
viewAncestor.dispatchWindowShown();
}
}
+
+ @Override
+ public void requestAppKeyboardShortcuts(IResultReceiver receiver) {
+ ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchRequestKeyboardShortcuts(receiver);
+ }
+ }
}
public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0b06d15..d89369b 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -42,6 +42,8 @@
import android.transition.TransitionManager;
import android.view.accessibility.AccessibilityEvent;
+import java.util.List;
+
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
@@ -556,6 +558,15 @@
* @param mode The mode that was just finished.
*/
public void onActionModeFinished(ActionMode mode);
+
+ /**
+ * Called when Keyboard Shortcuts are requested for the current window.
+ *
+ * @param data The data list to populate with shortcuts.
+ * @param menu The current menu, which may be null.
+ */
+ public void onProvideKeyboardShortcuts(
+ List<KeyboardShortcutGroup> data, @Nullable Menu menu);
}
/** @hide */
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
index 8ce1f8c..bed74e9 100644
--- a/core/java/android/view/WindowCallbackWrapper.java
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -19,6 +19,8 @@
import android.view.accessibility.AccessibilityEvent;
+import java.util.List;
+
/**
* A simple decorator stub for Window.Callback that passes through any calls
* to the wrapped instance as a base implementation. Call super.foo() to call into
@@ -150,5 +152,10 @@
public void onActionModeFinished(ActionMode mode) {
mWrapped.onActionModeFinished(mode);
}
+
+ @Override
+ public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {
+ mWrapped.onProvideKeyboardShortcuts(data, menu);
+ }
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 251f4c8..772eeec 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -29,6 +29,8 @@
import android.text.TextUtils;
import android.util.Log;
+import java.util.List;
+
/**
* The interface that apps use to talk to the window manager.
@@ -118,6 +120,34 @@
*/
public void removeViewImmediate(View view);
+ /**
+ * Used to asynchronously request Keyboard Shortcuts from the focused window.
+ *
+ * @hide
+ */
+ public interface KeyboardShortcutsReceiver {
+ /**
+ * Callback used when the focused window keyboard shortcuts are ready to be displayed.
+ *
+ * @param result The keyboard shortcuts to be displayed.
+ */
+ void onKeyboardShortcutsReceived(List<KeyboardShortcutGroup> result);
+ }
+
+ /**
+ * @hide
+ */
+ public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
+
+ /**
+ * Request for keyboard shortcuts to be retrieved asynchronously.
+ *
+ * @param receiver The callback to be triggered when the result is ready.
+ *
+ * @hide
+ */
+ public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver);
+
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
/**
* X position for this window. With the default gravity it is ignored.
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 98e9f54..6e11671 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -17,7 +17,15 @@
package android.view;
import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.R;
+
+import java.util.List;
/**
* Provides low-level communication with the system window manager for
@@ -117,6 +125,23 @@
}
@Override
+ public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver) {
+ IResultReceiver resultReceiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) throws RemoteException {
+ List<KeyboardShortcutGroup> result =
+ resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);
+ receiver.onKeyboardShortcutsReceived(result);
+ }
+ };
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .requestAppKeyboardShortcuts(resultReceiver);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
public Display getDefaultDisplay() {
return mDisplay;
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index a78b56a..4a1142f 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1333,9 +1333,9 @@
* Calculates the stable insets without running a layout.
*
* @param displayRotation the current display rotation
- * @param outInsets the insets to return
* @param displayWidth the current display width
* @param displayHeight the current display height
+ * @param outInsets the insets to return
*/
public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
Rect outInsets);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index b6570cc..9e79057 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -58,5 +58,5 @@
void temporaryEnableAccessibilityStateUntilKeyguardRemoved(in ComponentName service,
boolean touchExplorationEnabled);
- IBinder getWindowToken(int windowId);
+ IBinder getWindowToken(int windowId, int userId);
}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index e0dbe2f..1536c29 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -850,7 +850,7 @@
normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
}
- final boolean expired = normalizedTime >= 1.0f;
+ final boolean expired = normalizedTime >= 1.0f || isCanceled();
mMore = !expired;
if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
@@ -875,7 +875,7 @@
}
if (expired) {
- if (mRepeatCount == mRepeated) {
+ if (mRepeatCount == mRepeated || isCanceled()) {
if (!mEnded) {
mEnded = true;
guard.close();
@@ -905,6 +905,10 @@
return mMore;
}
+ private boolean isCanceled() {
+ return mStartTime == Long.MIN_VALUE;
+ }
+
private void fireAnimationStart() {
if (mListener != null) {
if (mListenerHandler == null) mListener.onAnimationStart(this);
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 6e5e591..bdf89e9 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -195,7 +195,6 @@
public boolean commitText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "commitText " + text);
replaceText(text, newCursorPosition, false);
- mIMM.notifyUserAction();
sendCurrentText();
return true;
}
@@ -450,7 +449,6 @@
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "setComposingText " + text);
replaceText(text, newCursorPosition, true);
- mIMM.notifyUserAction();
return true;
}
@@ -523,29 +521,17 @@
* attached to the input connection's view.
*/
public boolean sendKeyEvent(KeyEvent event) {
- synchronized (mIMM.mH) {
- ViewRootImpl viewRootImpl = mTargetView != null ? mTargetView.getViewRootImpl() : null;
- if (viewRootImpl == null) {
- if (mIMM.mServedView != null) {
- viewRootImpl = mIMM.mServedView.getViewRootImpl();
- }
- }
- if (viewRootImpl != null) {
- viewRootImpl.dispatchKeyFromIme(event);
- }
- }
- mIMM.notifyUserAction();
+ mIMM.dispatchKeyEventFromInputMethod(mTargetView, event);
return false;
}
-
+
/**
* Updates InputMethodManager with the current fullscreen mode.
*/
public boolean reportFullscreenMode(boolean enabled) {
- mIMM.setFullscreenMode(enabled);
return true;
}
-
+
private void sendCurrentText() {
if (!mDummyMode) {
return;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 5e07347..9647345 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -25,6 +25,8 @@
import com.android.internal.view.InputBindResult;
import com.android.internal.view.InputMethodClient;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.graphics.Rect;
@@ -527,7 +529,7 @@
}
}
}
-
+
private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
private final InputMethodManager mParentInputMethodManager;
private boolean mActive;
@@ -549,13 +551,23 @@
}
@Override
+ protected void onUserAction() {
+ mParentInputMethodManager.notifyUserAction();
+ }
+
+ @Override
+ protected void onReportFullscreenMode(boolean enabled) {
+ mParentInputMethodManager.setFullscreenMode(enabled);
+ }
+
+ @Override
public String toString() {
return "ControlledInputConnectionWrapper{mActive=" + mActive
+ " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
+ "}";
}
}
-
+
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -1813,6 +1825,34 @@
return DISPATCH_NOT_HANDLED;
}
+ /**
+ * Provides the default implementation of {@link InputConnection#sendKeyEvent(KeyEvent)}, which
+ * is expected to dispatch an keyboard event sent from the IME to an appropriate event target
+ * depending on the given {@link View} and the current focus state.
+ *
+ * <p>CAUTION: This method is provided only for the situation where
+ * {@link InputConnection#sendKeyEvent(KeyEvent)} needs to be implemented without relying on
+ * {@link BaseInputConnection}. Do not use this API for anything else.</p>
+ *
+ * @param targetView the default target view. If {@code null} is specified, then this method
+ * tries to find a good event target based on the current focus state.
+ * @param event the key event to be dispatched.
+ */
+ public void dispatchKeyEventFromInputMethod(@Nullable View targetView,
+ @NonNull KeyEvent event) {
+ synchronized (mH) {
+ ViewRootImpl viewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
+ if (viewRootImpl == null) {
+ if (mServedView != null) {
+ viewRootImpl = mServedView.getViewRootImpl();
+ }
+ }
+ if (viewRootImpl != null) {
+ viewRootImpl.dispatchKeyFromIme(event);
+ }
+ }
+ }
+
// Must be called on the main looper
void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
final boolean handled;
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 831481dd..4ca8971 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -142,6 +142,7 @@
}
applyThumbTint();
+ applyTickMarkTint();
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index 48d1d2b..ac8d578 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -59,7 +59,6 @@
*/
public class ActionMenuPresenter extends BaseMenuPresenter
implements ActionProvider.SubUiVisibilityListener {
- private static final String TAG = "ActionMenuPresenter";
private static final int ITEM_ANIMATION_DURATION = 150;
private static final boolean ACTIONBAR_ANIMATIONS_ENABLED = false;
@@ -87,20 +86,16 @@
private OpenOverflowRunnable mPostedOpenRunnable;
private ActionMenuPopupCallback mPopupCallback;
- private final boolean mShowCascadingMenus;
-
final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
int mOpenSubMenuId;
// These collections are used to store pre- and post-layout information for menu items,
// which is used to determine appropriate animations to run for changed items.
- private SparseArray<MenuItemLayoutInfo> mPreLayoutItems =
- new SparseArray<MenuItemLayoutInfo>();
- private SparseArray<MenuItemLayoutInfo> mPostLayoutItems =
- new SparseArray<MenuItemLayoutInfo>();
+ private SparseArray<MenuItemLayoutInfo> mPreLayoutItems = new SparseArray<>();
+ private SparseArray<MenuItemLayoutInfo> mPostLayoutItems = new SparseArray<>();
// The list of currently running animations on menu items.
- private List<ItemAnimationInfo> mRunningItemAnimations = new ArrayList<ItemAnimationInfo>();
+ private List<ItemAnimationInfo> mRunningItemAnimations = new ArrayList<>();
private ViewTreeObserver.OnPreDrawListener mItemAnimationPreDrawListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -130,9 +125,6 @@
public ActionMenuPresenter(Context context) {
super(context, com.android.internal.R.layout.action_menu_layout,
com.android.internal.R.layout.action_menu_item_layout);
-
- mShowCascadingMenus = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enableCascadingSubmenus);
}
@Override
@@ -845,8 +837,6 @@
}
private class OverflowMenuButton extends ImageButton implements ActionMenuView.ActionMenuChildView {
- private final float[] mTempPts = new float[2];
-
public OverflowMenuButton(Context context) {
super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 2aaa356..cde7604 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -16,8 +16,11 @@
package android.widget;
+import android.annotation.AttrRes;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.annotation.Widget;
import android.content.Context;
@@ -37,9 +40,13 @@
import java.util.TimeZone;
/**
- * This class is a calendar widget for displaying and selecting dates. The range
- * of dates supported by this calendar is configurable. A user can select a date
- * by taping on it and can scroll and fling the calendar to a desired date.
+ * This class is a calendar widget for displaying and selecting dates. The
+ * range of dates supported by this calendar is configurable.
+ * <p>
+ * The exact appearance and interaction model of this widget may vary between
+ * OS versions and themes (e.g. Holo versus Material), but in general a user
+ * can select a date by tapping on it and can scroll or fling the calendar to a
+ * desired date.
*
* @attr ref android.R.styleable#CalendarView_showWeekNumber
* @attr ref android.R.styleable#CalendarView_firstDayOfWeek
@@ -77,22 +84,24 @@
* @param month The month that was set [0-11].
* @param dayOfMonth The day of the month that was set.
*/
- public void onSelectedDayChange(CalendarView view, int year, int month, int dayOfMonth);
+ void onSelectedDayChange(@NonNull CalendarView view, int year, int month, int dayOfMonth);
}
- public CalendarView(Context context) {
+ public CalendarView(@NonNull Context context) {
this(context, null);
}
- public CalendarView(Context context, AttributeSet attrs) {
+ public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, R.attr.calendarViewStyle);
}
- public CalendarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public CalendarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public CalendarView(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
@@ -322,7 +331,7 @@
*
* @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
*/
- public void setWeekDayTextAppearance(int resourceId) {
+ public void setWeekDayTextAppearance(@StyleRes int resourceId) {
mDelegate.setWeekDayTextAppearance(resourceId);
}
@@ -333,7 +342,7 @@
*
* @attr ref android.R.styleable#CalendarView_weekDayTextAppearance
*/
- public int getWeekDayTextAppearance() {
+ public @StyleRes int getWeekDayTextAppearance() {
return mDelegate.getWeekDayTextAppearance();
}
@@ -344,7 +353,7 @@
*
* @attr ref android.R.styleable#CalendarView_dateTextAppearance
*/
- public void setDateTextAppearance(int resourceId) {
+ public void setDateTextAppearance(@StyleRes int resourceId) {
mDelegate.setDateTextAppearance(resourceId);
}
@@ -355,7 +364,7 @@
*
* @attr ref android.R.styleable#CalendarView_dateTextAppearance
*/
- public int getDateTextAppearance() {
+ public @StyleRes int getDateTextAppearance() {
return mDelegate.getDateTextAppearance();
}
@@ -552,36 +561,29 @@
int getShownWeekCount();
void setSelectedWeekBackgroundColor(@ColorInt int color);
- @ColorInt
- int getSelectedWeekBackgroundColor();
+ @ColorInt int getSelectedWeekBackgroundColor();
void setFocusedMonthDateColor(@ColorInt int color);
- @ColorInt
- int getFocusedMonthDateColor();
+ @ColorInt int getFocusedMonthDateColor();
void setUnfocusedMonthDateColor(@ColorInt int color);
- @ColorInt
- int getUnfocusedMonthDateColor();
+ @ColorInt int getUnfocusedMonthDateColor();
void setWeekNumberColor(@ColorInt int color);
- @ColorInt
- int getWeekNumberColor();
+ @ColorInt int getWeekNumberColor();
void setWeekSeparatorLineColor(@ColorInt int color);
- @ColorInt
- int getWeekSeparatorLineColor();
+ @ColorInt int getWeekSeparatorLineColor();
void setSelectedDateVerticalBar(@DrawableRes int resourceId);
void setSelectedDateVerticalBar(Drawable drawable);
Drawable getSelectedDateVerticalBar();
void setWeekDayTextAppearance(@StyleRes int resourceId);
- @StyleRes
- int getWeekDayTextAppearance();
+ @StyleRes int getWeekDayTextAppearance();
void setDateTextAppearance(@StyleRes int resourceId);
- @StyleRes
- int getDateTextAppearance();
+ @StyleRes int getDateTextAppearance();
void setMinDate(long minDate);
long getMinDate();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 15cea77..1576877 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -82,6 +82,7 @@
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.RenderNode;
+import android.view.SubMenu;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnClickListener;
@@ -285,6 +286,9 @@
boolean mIsInsertionActionModeStartPending = false;
+ private final SuggestionHelper mSuggestionHelper = new SuggestionHelper();
+ private SuggestionInfo[] mSuggestionInfosInContextMenu;
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
@@ -2369,6 +2373,9 @@
if (offset == -1) {
return;
}
+ mPreserveDetachedSelection = true;
+ stopTextActionMode();
+ mPreserveDetachedSelection = false;
final boolean isOnSelection = mTextView.hasSelection()
&& offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd();
if (!isOnSelection) {
@@ -2378,7 +2385,24 @@
Selection.setSelection((Spannable) mTextView.getText(), offset);
}
- // TODO: Add suggestions in the context menu.
+ 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 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);
+ }
+ }
menu.add(Menu.NONE, TextView.ID_UNDO, MENU_ITEM_ORDER_UNDO,
com.android.internal.R.string.undo)
@@ -2422,6 +2446,61 @@
mPreserveDetachedSelection = true;
}
+ private void replaceWithSuggestion(SuggestionInfo suggestionInfo, int spanStart, int spanEnd) {
+ final Editable editable = (Editable) mTextView.getText();
+ final String originalText = TextUtils.substring(editable, spanStart, spanEnd);
+ // SuggestionSpans are removed by replace: save them before
+ SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
+ SuggestionSpan.class);
+ final int length = suggestionSpans.length;
+ int[] suggestionSpansStarts = new int[length];
+ int[] suggestionSpansEnds = new int[length];
+ int[] suggestionSpansFlags = new int[length];
+ for (int i = 0; i < length; i++) {
+ final SuggestionSpan suggestionSpan = suggestionSpans[i];
+ suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan);
+ suggestionSpansEnds[i] = editable.getSpanEnd(suggestionSpan);
+ suggestionSpansFlags[i] = editable.getSpanFlags(suggestionSpan);
+
+ // Remove potential misspelled flags
+ int suggestionSpanFlags = suggestionSpan.getFlags();
+ if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
+ suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
+ suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
+ suggestionSpan.setFlags(suggestionSpanFlags);
+ }
+ }
+
+ // Notify source IME of the suggestion pick. Do this before swapping texts.
+ suggestionInfo.mSuggestionSpan.notifySelection(
+ mTextView.getContext(), originalText, suggestionInfo.mSuggestionIndex);
+
+ // Swap text content between actual text and Suggestion span
+ final int suggestionStart = suggestionInfo.mSuggestionStart;
+ final int suggestionEnd = suggestionInfo.mSuggestionEnd;
+ final String suggestion = suggestionInfo.mText.subSequence(
+ suggestionStart, suggestionEnd).toString();
+ mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
+
+ String[] suggestions = suggestionInfo.mSuggestionSpan.getSuggestions();
+ suggestions[suggestionInfo.mSuggestionIndex] = originalText;
+
+ // Restore previous SuggestionSpans
+ final int lengthDelta = suggestion.length() - (spanEnd - spanStart);
+ for (int i = 0; i < length; i++) {
+ // Only spans that include the modified region make sense after replacement
+ // Spans partially included in the replaced region are removed, there is no
+ // way to assign them a valid range after replacement
+ if (suggestionSpansStarts[i] <= spanStart && suggestionSpansEnds[i] >= spanEnd) {
+ mTextView.setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i],
+ suggestionSpansEnds[i] + lengthDelta, suggestionSpansFlags[i]);
+ }
+ }
+ // Move cursor at the end of the replaced word
+ final int newCursorPosition = spanEnd + lengthDelta;
+ mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
+ }
+
private final MenuItem.OnMenuItemClickListener mOnContextMenuItemClickListener =
new MenuItem.OnMenuItemClickListener() {
@Override
@@ -2433,6 +2512,31 @@
}
};
+ 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.
@@ -2849,6 +2953,131 @@
}
}
+ private static class SuggestionInfo {
+ // Range of actual suggestion within text
+ int mSuggestionStart, mSuggestionEnd;
+
+ // The SuggestionSpan that this TextView represents
+ @Nullable
+ SuggestionSpan mSuggestionSpan;
+
+ // The index of this suggestion inside suggestionSpan
+ int mSuggestionIndex;
+
+ final SpannableStringBuilder mText = new SpannableStringBuilder();
+
+ void clear() {
+ mSuggestionSpan = null;
+ mText.clear();
+ }
+ }
+
+ private class SuggestionHelper {
+ private final Comparator<SuggestionSpan> mSuggestionSpanComparator =
+ new SuggestionSpanComparator();
+ private final HashMap<SuggestionSpan, Integer> mSpansLengths =
+ new HashMap<SuggestionSpan, Integer>();
+
+ private class SuggestionSpanComparator implements Comparator<SuggestionSpan> {
+ public int compare(SuggestionSpan span1, SuggestionSpan span2) {
+ final int flag1 = span1.getFlags();
+ final int flag2 = span2.getFlags();
+ if (flag1 != flag2) {
+ // The order here should match what is used in updateDrawState
+ final boolean easy1 = (flag1 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
+ final boolean easy2 = (flag2 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
+ final boolean misspelled1 = (flag1 & SuggestionSpan.FLAG_MISSPELLED) != 0;
+ final boolean misspelled2 = (flag2 & SuggestionSpan.FLAG_MISSPELLED) != 0;
+ if (easy1 && !misspelled1) return -1;
+ if (easy2 && !misspelled2) return 1;
+ if (misspelled1) return -1;
+ if (misspelled2) return 1;
+ }
+
+ return mSpansLengths.get(span1).intValue() - mSpansLengths.get(span2).intValue();
+ }
+ }
+
+ /**
+ * Returns the suggestion spans that cover the current cursor position. The suggestion
+ * spans are sorted according to the length of text that they are attached to.
+ */
+ private SuggestionSpan[] getSortedSuggestionSpans() {
+ int pos = mTextView.getSelectionStart();
+ Spannable spannable = (Spannable) mTextView.getText();
+ SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class);
+
+ mSpansLengths.clear();
+ for (SuggestionSpan suggestionSpan : suggestionSpans) {
+ int start = spannable.getSpanStart(suggestionSpan);
+ int end = spannable.getSpanEnd(suggestionSpan);
+ mSpansLengths.put(suggestionSpan, Integer.valueOf(end - start));
+ }
+
+ // The suggestions are sorted according to their types (easy correction first, then
+ // misspelled) and to the length of the text that they cover (shorter first).
+ Arrays.sort(suggestionSpans, mSuggestionSpanComparator);
+ mSpansLengths.clear();
+
+ return suggestionSpans;
+ }
+
+ /**
+ * Gets the SuggestionInfo list that contains suggestion information at the current cursor
+ * position.
+ *
+ * @param suggestionInfos SuggestionInfo array the results will be set.
+ * @return the number of suggestions actually fetched.
+ */
+ public int getSuggestionInfo(SuggestionInfo[] suggestionInfos) {
+ 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];
+ final int spanStart = spannable.getSpanStart(suggestionSpan);
+ final int spanEnd = spannable.getSpanEnd(suggestionSpan);
+
+ final String[] suggestions = suggestionSpan.getSuggestions();
+ final int nbSuggestions = suggestions.length;
+ 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);
+ if (spanStart == otherSpanStart && spanEnd == otherSpanEnd) {
+ suggestionIsDuplicate = true;
+ break;
+ }
+ }
+ }
+
+ if (suggestionIsDuplicate) {
+ continue;
+ }
+ SuggestionInfo suggestionInfo = suggestionInfos[numberOfSuggestions];
+ suggestionInfo.mSuggestionSpan = suggestionSpan;
+ suggestionInfo.mSuggestionIndex = suggestionIndex;
+ suggestionInfo.mSuggestionStart = 0;
+ suggestionInfo.mSuggestionEnd = suggestion.length();
+ suggestionInfo.mText.replace(0, suggestionInfo.mText.length(), suggestion);
+ numberOfSuggestions++;
+ if (numberOfSuggestions >= suggestionInfos.length) {
+ return numberOfSuggestions;
+ }
+ }
+ }
+ return numberOfSuggestions;
+ }
+ }
+
@VisibleForTesting
public class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
@@ -2862,8 +3091,6 @@
private boolean mCursorWasVisibleBeforeSuggestions;
private boolean mIsShowingUp = false;
private SuggestionAdapter mSuggestionsAdapter;
- private final Comparator<SuggestionSpan> mSuggestionSpanComparator;
- private final HashMap<SuggestionSpan, Integer> mSpansLengths;
private final TextAppearanceSpan mHighlightSpan = new TextAppearanceSpan(
mTextView.getContext(), mTextView.mTextEditSuggestionHighlightStyle);
private TextView mAddToDictionaryButton;
@@ -2895,8 +3122,6 @@
public SuggestionsPopupWindow() {
mCursorWasVisibleBeforeSuggestions = mCursorVisible;
- mSuggestionSpanComparator = new SuggestionSpanComparator();
- mSpansLengths = new HashMap<SuggestionSpan, Integer>();
}
@Override
@@ -2987,24 +3212,6 @@
mIsShowingUp = false;
}
- private final class SuggestionInfo {
- int suggestionStart, suggestionEnd; // range of actual suggestion within text
-
- // the SuggestionSpan that this TextView represents
- @Nullable
- SuggestionSpan suggestionSpan;
-
- int suggestionIndex; // the index of this suggestion inside suggestionSpan
-
- @Nullable
- final SpannableStringBuilder text = new SpannableStringBuilder();
-
- void clear() {
- suggestionSpan = null;
- text.clear();
- }
- }
-
private class SuggestionAdapter extends BaseAdapter {
private LayoutInflater mInflater = (LayoutInflater) mTextView.getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -3034,55 +3241,11 @@
}
final SuggestionInfo suggestionInfo = mSuggestionInfos[position];
- textView.setText(suggestionInfo.text);
+ textView.setText(suggestionInfo.mText);
return textView;
}
}
- private class SuggestionSpanComparator implements Comparator<SuggestionSpan> {
- public int compare(SuggestionSpan span1, SuggestionSpan span2) {
- final int flag1 = span1.getFlags();
- final int flag2 = span2.getFlags();
- if (flag1 != flag2) {
- // The order here should match what is used in updateDrawState
- final boolean easy1 = (flag1 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
- final boolean easy2 = (flag2 & SuggestionSpan.FLAG_EASY_CORRECT) != 0;
- final boolean misspelled1 = (flag1 & SuggestionSpan.FLAG_MISSPELLED) != 0;
- final boolean misspelled2 = (flag2 & SuggestionSpan.FLAG_MISSPELLED) != 0;
- if (easy1 && !misspelled1) return -1;
- if (easy2 && !misspelled2) return 1;
- if (misspelled1) return -1;
- if (misspelled2) return 1;
- }
-
- return mSpansLengths.get(span1).intValue() - mSpansLengths.get(span2).intValue();
- }
- }
-
- /**
- * Returns the suggestion spans that cover the current cursor position. The suggestion
- * spans are sorted according to the length of text that they are attached to.
- */
- private SuggestionSpan[] getSuggestionSpans() {
- int pos = mTextView.getSelectionStart();
- Spannable spannable = (Spannable) mTextView.getText();
- SuggestionSpan[] suggestionSpans = spannable.getSpans(pos, pos, SuggestionSpan.class);
-
- mSpansLengths.clear();
- for (SuggestionSpan suggestionSpan : suggestionSpans) {
- int start = spannable.getSpanStart(suggestionSpan);
- int end = spannable.getSpanEnd(suggestionSpan);
- mSpansLengths.put(suggestionSpan, Integer.valueOf(end - start));
- }
-
- // The suggestions are sorted according to their types (easy correction first, then
- // misspelled) and to the length of the text that they cover (shorter first).
- Arrays.sort(suggestionSpans, mSuggestionSpanComparator);
- mSpansLengths.clear();
-
- return suggestionSpans;
- }
-
@VisibleForTesting
public ViewGroup getContentViewForTesting() {
return mContentView;
@@ -3166,66 +3329,26 @@
private boolean updateSuggestions() {
Spannable spannable = (Spannable) mTextView.getText();
- SuggestionSpan[] suggestionSpans = getSuggestionSpans();
+ mNumberOfSuggestions =
+ mSuggestionHelper.getSuggestionInfo(mSuggestionInfos);
+ if (mNumberOfSuggestions == 0) {
+ return false;
+ }
- final int nbSpans = suggestionSpans.length;
- // Suggestions are shown after a delay: the underlying spans may have been removed
- if (nbSpans == 0) return false;
-
- mNumberOfSuggestions = 0;
int spanUnionStart = mTextView.getText().length();
int spanUnionEnd = 0;
mMisspelledSpan = null;
- int underlineColor = 0;
-
- for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
- SuggestionSpan suggestionSpan = suggestionSpans[spanIndex];
- final int spanStart = spannable.getSpanStart(suggestionSpan);
- final int spanEnd = spannable.getSpanEnd(suggestionSpan);
- spanUnionStart = Math.min(spanStart, spanUnionStart);
- spanUnionEnd = Math.max(spanEnd, spanUnionEnd);
-
+ 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;
}
-
- // The first span dictates the background color of the highlighted text
- if (spanIndex == 0) underlineColor = suggestionSpan.getUnderlineColor();
-
- String[] suggestions = suggestionSpan.getSuggestions();
- int nbSuggestions = suggestions.length;
- for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
- String suggestion = suggestions[suggestionIndex];
-
- boolean suggestionIsDuplicate = false;
- for (int i = 0; i < mNumberOfSuggestions; i++) {
- if (mSuggestionInfos[i].text.toString().equals(suggestion)) {
- SuggestionSpan otherSuggestionSpan = mSuggestionInfos[i].suggestionSpan;
- final int otherSpanStart = spannable.getSpanStart(otherSuggestionSpan);
- final int otherSpanEnd = spannable.getSpanEnd(otherSuggestionSpan);
- if (spanStart == otherSpanStart && spanEnd == otherSpanEnd) {
- suggestionIsDuplicate = true;
- break;
- }
- }
- }
-
- if (!suggestionIsDuplicate) {
- SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions];
- suggestionInfo.suggestionSpan = suggestionSpan;
- suggestionInfo.suggestionIndex = suggestionIndex;
- suggestionInfo.text.replace(0, suggestionInfo.text.length(), suggestion);
-
- mNumberOfSuggestions++;
-
- if (mNumberOfSuggestions == MAX_NUMBER_SUGGESTIONS) {
- // Also end outer for loop
- spanIndex = nbSpans;
- break;
- }
- }
- }
+ final int spanStart = spannable.getSpanStart(suggestionSpan);
+ final int spanEnd = spannable.getSpanEnd(suggestionSpan);
+ spanUnionStart = Math.min(spanUnionStart, spanStart);
+ spanUnionEnd = Math.max(spanUnionEnd, spanEnd);
}
for (int i = 0; i < mNumberOfSuggestions; i++) {
@@ -3244,6 +3367,7 @@
mAddToDictionaryButton.setVisibility(addToDictionaryButtonVisibility);
if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan();
+ final int underlineColor = mSuggestionInfos[0].mSuggestionSpan.getUnderlineColor();
if (underlineColor == 0) {
// Fallback on the default highlight color when the first span does not provide one
mSuggestionRangeSpan.setBackgroundColor(mTextView.mHighlightColor);
@@ -3263,21 +3387,21 @@
private void highlightTextDifferences(SuggestionInfo suggestionInfo, int unionStart,
int unionEnd) {
final Spannable text = (Spannable) mTextView.getText();
- final int spanStart = text.getSpanStart(suggestionInfo.suggestionSpan);
- final int spanEnd = text.getSpanEnd(suggestionInfo.suggestionSpan);
+ final int spanStart = text.getSpanStart(suggestionInfo.mSuggestionSpan);
+ final int spanEnd = text.getSpanEnd(suggestionInfo.mSuggestionSpan);
// Adjust the start/end of the suggestion span
- suggestionInfo.suggestionStart = spanStart - unionStart;
- suggestionInfo.suggestionEnd = suggestionInfo.suggestionStart
- + suggestionInfo.text.length();
+ suggestionInfo.mSuggestionStart = spanStart - unionStart;
+ suggestionInfo.mSuggestionEnd = suggestionInfo.mSuggestionStart
+ + suggestionInfo.mText.length();
- suggestionInfo.text.setSpan(mHighlightSpan, 0, suggestionInfo.text.length(),
+ suggestionInfo.mText.setSpan(mHighlightSpan, 0, suggestionInfo.mText.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// Add the text before and after the span.
final String textAsString = text.toString();
- suggestionInfo.text.insert(0, textAsString.substring(unionStart, spanStart));
- suggestionInfo.text.append(textAsString.substring(spanEnd, unionEnd));
+ suggestionInfo.mText.insert(0, textAsString.substring(unionStart, spanStart));
+ suggestionInfo.mText.append(textAsString.substring(spanEnd, unionEnd));
}
@Override
@@ -3285,69 +3409,14 @@
Editable editable = (Editable) mTextView.getText();
SuggestionInfo suggestionInfo = mSuggestionInfos[position];
- final int spanStart = editable.getSpanStart(suggestionInfo.suggestionSpan);
- final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan);
+ 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;
}
-
- final String originalText = TextUtils.substring(editable, spanStart, spanEnd);
-
- // SuggestionSpans are removed by replace: save them before
- final SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
- SuggestionSpan.class);
- final int length = suggestionSpans.length;
- final int[] suggestionSpansStarts = new int[length];
- final int[] suggestionSpansEnds = new int[length];
- final int[] suggestionSpansFlags = new int[length];
- for (int i = 0; i < length; i++) {
- final SuggestionSpan suggestionSpan = suggestionSpans[i];
- suggestionSpansStarts[i] = editable.getSpanStart(suggestionSpan);
- suggestionSpansEnds[i] = editable.getSpanEnd(suggestionSpan);
- suggestionSpansFlags[i] = editable.getSpanFlags(suggestionSpan);
-
- // Remove potential misspelled flags
- int suggestionSpanFlags = suggestionSpan.getFlags();
- if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
- suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
- suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
- suggestionSpan.setFlags(suggestionSpanFlags);
- }
- }
-
- final int suggestionStart = suggestionInfo.suggestionStart;
- final int suggestionEnd = suggestionInfo.suggestionEnd;
- final String suggestion = suggestionInfo.text.subSequence(
- suggestionStart, suggestionEnd).toString();
- mTextView.replaceText_internal(spanStart, spanEnd, suggestion);
-
- // Notify source IME of the suggestion pick. Do this before
- // swaping texts.
- suggestionInfo.suggestionSpan.notifySelection(
- mTextView.getContext(), originalText, suggestionInfo.suggestionIndex);
-
- // Swap text content between actual text and Suggestion span
- final String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions();
- suggestions[suggestionInfo.suggestionIndex] = originalText;
-
- // Restore previous SuggestionSpans
- final int lengthDifference = suggestion.length() - (spanEnd - spanStart);
- for (int i = 0; i < length; i++) {
- // Only spans that include the modified region make sense after replacement
- // Spans partially included in the replaced region are removed, there is no
- // way to assign them a valid range after replacement
- if (suggestionSpansStarts[i] <= spanStart &&
- suggestionSpansEnds[i] >= spanEnd) {
- mTextView.setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i],
- suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]);
- }
- }
-
- // Move cursor at the end of the replaced word
- final int newCursorPosition = spanEnd + lengthDifference;
- mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
+ replaceWithSuggestion(suggestionInfo, spanStart, spanEnd);
hideWithCleanUp();
}
}
diff --git a/core/java/android/widget/ForwardingListener.java b/core/java/android/widget/ForwardingListener.java
index 7ddeff9..b383e1c 100644
--- a/core/java/android/widget/ForwardingListener.java
+++ b/core/java/android/widget/ForwardingListener.java
@@ -53,12 +53,6 @@
/** Whether this listener is currently forwarding touch events. */
private boolean mForwarding;
- /**
- * Whether forwarding was initiated by a long-press. If so, we won't
- * force the window to dismiss when the touch stream ends.
- */
- private boolean mWasLongPress;
-
/** The id of the first pointer down in the current event stream. */
private int mActivePointerId;
@@ -172,7 +166,6 @@
switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = srcEvent.getPointerId(0);
- mWasLongPress = false;
if (mDisallowIntercept == null) {
mDisallowIntercept = new DisallowIntercept();
@@ -243,7 +236,6 @@
e.recycle();
mForwarding = true;
- mWasLongPress = true;
}
/**
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index b95bc28..595adc2 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -16,18 +16,20 @@
package android.widget;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
+import com.android.internal.R;
+import com.android.internal.view.menu.ShowableListMenu;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.IntProperty;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -38,13 +40,7 @@
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
-import android.view.animation.AccelerateDecelerateInterpolator;
-
-import com.android.internal.R;
-import com.android.internal.view.menu.ShowableListMenu;
-import com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller;
-
-import java.util.Locale;
+import android.widget.AdapterView.OnItemSelectedListener;
/**
* A ListPopupWindow anchors itself to a host view and displays a
@@ -109,8 +105,6 @@
private boolean mModal;
- private int mLayoutDirection;
-
PopupWindow mPopup;
/**
@@ -174,7 +168,7 @@
*
* @param context Context used for contained views.
*/
- public ListPopupWindow(Context context) {
+ public ListPopupWindow(@NonNull Context context) {
this(context, null, com.android.internal.R.attr.listPopupWindowStyle, 0);
}
@@ -185,7 +179,7 @@
* @param context Context used for contained views.
* @param attrs Attributes from inflating parent views used to style the popup.
*/
- public ListPopupWindow(Context context, AttributeSet attrs) {
+ public ListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.listPopupWindowStyle, 0);
}
@@ -197,7 +191,8 @@
* @param attrs Attributes from inflating parent views used to style the popup.
* @param defStyleAttr Default style attribute to use for popup content.
*/
- public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
+ public ListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
@@ -210,7 +205,8 @@
* @param defStyleAttr Style attribute to read for default styling of popup content.
* @param defStyleRes Style resource ID to use for default styling of popup content.
*/
- public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public ListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
mContext = context;
mHandler = new Handler(context.getMainLooper());
@@ -227,9 +223,6 @@
mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes);
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
- // Set the default layout direction to match the default locale one
- final Locale locale = mContext.getResources().getConfiguration().locale;
- mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(locale);
}
/**
@@ -238,7 +231,7 @@
*
* @param adapter The adapter to use to create this window's content.
*/
- public void setAdapter(ListAdapter adapter) {
+ public void setAdapter(@Nullable ListAdapter adapter) {
if (mObserver == null) {
mObserver = new PopupDataSetObserver();
} else if (mAdapter != null) {
@@ -371,7 +364,7 @@
/**
* @return The background drawable for the popup window.
*/
- public Drawable getBackground() {
+ public @Nullable Drawable getBackground() {
return mPopup.getBackground();
}
@@ -380,7 +373,7 @@
*
* @param d A drawable to set as the background.
*/
- public void setBackgroundDrawable(Drawable d) {
+ public void setBackgroundDrawable(@Nullable Drawable d) {
mPopup.setBackgroundDrawable(d);
}
@@ -389,7 +382,7 @@
*
* @param animationStyle Animation style to use.
*/
- public void setAnimationStyle(int animationStyle) {
+ public void setAnimationStyle(@StyleRes int animationStyle) {
mPopup.setAnimationStyle(animationStyle);
}
@@ -399,7 +392,7 @@
*
* @return Animation style that will be used.
*/
- public int getAnimationStyle() {
+ public @StyleRes int getAnimationStyle() {
return mPopup.getAnimationStyle();
}
@@ -408,7 +401,7 @@
*
* @return The popup's anchor view
*/
- public View getAnchorView() {
+ public @Nullable View getAnchorView() {
return mDropDownAnchorView;
}
@@ -418,7 +411,7 @@
*
* @param anchor The view to use as an anchor.
*/
- public void setAnchorView(View anchor) {
+ public void setAnchorView(@Nullable View anchor) {
mDropDownAnchorView = anchor;
}
@@ -537,7 +530,7 @@
*
* @see ListView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)
*/
- public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
+ public void setOnItemClickListener(@Nullable AdapterView.OnItemClickListener clickListener) {
mItemClickListener = clickListener;
}
@@ -546,9 +539,9 @@
*
* @param selectedListener Listener to register.
*
- * @see ListView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
+ * @see ListView#setOnItemSelectedListener(OnItemSelectedListener)
*/
- public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener selectedListener) {
+ public void setOnItemSelectedListener(@Nullable OnItemSelectedListener selectedListener) {
mItemSelectedListener = selectedListener;
}
@@ -558,7 +551,7 @@
*
* @param prompt View to use as an informational prompt.
*/
- public void setPromptView(View prompt) {
+ public void setPromptView(@Nullable View prompt) {
boolean showing = isShowing();
if (showing) {
removePromptView();
@@ -686,7 +679,7 @@
*
* @param listener Listener that will be notified when the popup is dismissed.
*/
- public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
+ public void setOnDismissListener(@Nullable PopupWindow.OnDismissListener listener) {
mPopup.setOnDismissListener(listener);
}
@@ -795,7 +788,7 @@
/**
* @return The currently selected item or null if the popup is not showing.
*/
- public Object getSelectedItem() {
+ public @Nullable Object getSelectedItem() {
if (!isShowing()) {
return null;
}
@@ -834,7 +827,7 @@
*
* @see ListView#getSelectedView()
*/
- public View getSelectedView() {
+ public @Nullable View getSelectedView() {
if (!isShowing()) {
return null;
}
@@ -846,11 +839,11 @@
* Only valid when {@link #isShowing()} == {@code true}.
*/
@Override
- public ListView getListView() {
+ public @Nullable ListView getListView() {
return mDropDownList;
}
- DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
+ @NonNull DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
return new DropDownListView(context, hijackFocus);
}
@@ -874,7 +867,7 @@
*
* @see #setModal(boolean)
*/
- public boolean onKeyDown(int keyCode, KeyEvent event) {
+ public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
// when the drop down is shown, we drive it directly
if (isShowing()) {
// the key events are forwarded to the list in the drop down view
@@ -969,7 +962,7 @@
*
* @see #setModal(boolean)
*/
- public boolean onKeyUp(int keyCode, KeyEvent event) {
+ public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
boolean consumed = mDropDownList.onKeyUp(keyCode, event);
if (consumed && KeyEvent.isConfirmKey(keyCode)) {
@@ -993,7 +986,7 @@
*
* @see #setModal(boolean)
*/
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && isShowing()) {
// special case for the back key, we do not even try to send it
// to the drop down list but instead, consume it immediately
@@ -1159,7 +1152,6 @@
mPopup.setContentView(dropDownView);
} else {
- dropDownView = (ViewGroup) mPopup.getContentView();
final View view = mPromptView;
if (view != null) {
LinearLayout.LayoutParams hintParams =
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index c79e184..09cf704 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -271,6 +271,10 @@
attrs, R.styleable.Spinner, defStyleAttr, defStyleRes);
mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_dropDownWidth,
ViewGroup.LayoutParams.WRAP_CONTENT);
+ if (pa.hasValueOrEmpty(R.styleable.Spinner_dropDownSelector)) {
+ popup.setListSelector(pa.getDrawable(
+ R.styleable.Spinner_dropDownSelector));
+ }
popup.setBackgroundDrawable(pa.getDrawable(R.styleable.Spinner_popupBackground));
popup.setPromptText(a.getString(R.styleable.Spinner_prompt));
pa.recycle();
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 8e711b0..06daf61 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1946,6 +1946,9 @@
public void setMenuCallbacks(MenuPresenter.Callback pcb, MenuBuilder.Callback mcb) {
mActionMenuPresenterCallback = pcb;
mMenuBuilderCallback = mcb;
+ if (mMenuView != null) {
+ mMenuView.setMenuCallbacks(pcb, mcb);
+ }
}
/**
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index ce4fc06..b0b25d3 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -63,6 +63,22 @@
public static final int PROFILE_CHALLENGE = 271;
public static final int QS_BATTERY_DETAIL = 272;
+ /**
+ * Logged when the user goes into the overview history.
+ */
+ public static final int OVERVIEW_HISTORY = 273;
+
+ /**
+ * Logged when the user pages through overview.
+ */
+ public static final int ACTION_OVERVIEW_PAGE = 274;
+
+ /**
+ * Logged when the user launches a task from overview.
+ */
+ public static final int ACTION_OVERVIEW_SELECT = 275;
+
+
public static void visible(Context context, int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
throw new IllegalArgumentException("Must define metric category");
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index 830da79..b3222f0 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -19,6 +19,7 @@
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.SystemClock;
+import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.util.Preconditions;
@@ -29,6 +30,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Arrays;
/**
* Represents a connection to {@code installd}. Allows multiple connect and
@@ -61,6 +63,11 @@
}
public synchronized String transact(String cmd) {
+ if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
+ Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+ + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
+ }
+
if (!connect()) {
Slog.e(TAG, "connection failed");
return "-1";
@@ -96,44 +103,50 @@
}
}
- public int execute(String cmd) {
- if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
- Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
- + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
- }
-
- String res = transact(cmd);
+ public void execute(String cmd, Object... args) throws InstallerException {
+ final String resRaw = executeForResult(cmd, args);
+ int res = -1;
try {
- return Integer.parseInt(res);
- } catch (NumberFormatException ex) {
- return -1;
+ res = Integer.parseInt(resRaw);
+ } catch (NumberFormatException ignored) {
+ }
+ if (res != 0) {
+ throw new InstallerException(
+ "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
}
}
- public int dexopt(String apkPath, int uid, String instructionSet,
- int dexoptNeeded, int dexFlags) {
- return dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
- null /*outputPath*/, dexFlags);
+ public String executeForResult(String cmd, Object... args)
+ throws InstallerException {
+ final StringBuilder builder = new StringBuilder(cmd);
+ for (Object arg : args) {
+ String escaped;
+ if (arg == null) {
+ escaped = "";
+ } else {
+ escaped = String.valueOf(arg);
+ }
+ if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
+ throw new InstallerException(
+ "Invalid argument while executing " + cmd + " " + Arrays.toString(args));
+ }
+ if (TextUtils.isEmpty(escaped)) {
+ escaped = "!";
+ }
+ builder.append(' ').append(escaped);
+ }
+ return transact(builder.toString());
}
- public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
- int dexoptNeeded, String outputPath, int dexFlags) {
- StringBuilder builder = new StringBuilder("dexopt");
- builder.append(' ');
- builder.append(apkPath);
- builder.append(' ');
- builder.append(uid);
- builder.append(' ');
- builder.append(pkgName);
- builder.append(' ');
- builder.append(instructionSet);
- builder.append(' ');
- builder.append(dexoptNeeded);
- builder.append(' ');
- builder.append(outputPath != null ? outputPath : "!");
- builder.append(' ');
- builder.append(dexFlags);
- return execute(builder.toString());
+ public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
+ int dexFlags) throws InstallerException {
+ dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded, null /* outputPath */, dexFlags);
+ }
+
+ public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+ int dexoptNeeded, String outputPath, int dexFlags) throws InstallerException {
+ execute("dexopt", apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
+ dexFlags);
}
private boolean connect() {
@@ -227,11 +240,19 @@
public void waitForConnection() {
for (;;) {
- if (execute("ping") >= 0) {
+ try {
+ execute("ping");
return;
+ } catch (InstallerException ignored) {
}
Slog.w(TAG, "installd not ready");
SystemClock.sleep(1000);
}
}
+
+ public static class InstallerException extends Exception {
+ public InstallerException(String detailMessage) {
+ super(detailMessage);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 4a1f7f4..eecc0ee 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -37,6 +37,8 @@
import android.util.Log;
import android.webkit.WebViewFactory;
+import com.android.internal.os.InstallerConnection.InstallerException;
+
import dalvik.system.DexFile;
import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
@@ -502,8 +504,8 @@
dexoptNeeded, 0 /*dexFlags*/);
}
}
- } catch (IOException ioe) {
- throw new RuntimeException("Error starting system_server", ioe);
+ } catch (IOException | InstallerException e) {
+ throw new RuntimeException("Error starting system_server", e);
} finally {
installer.disconnect();
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index cea9867..7ae0efb 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -17,6 +17,7 @@
package com.android.internal.policy;
import com.android.internal.R;
+import com.android.internal.policy.PhoneWindow.PanelFeatureState;
import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.view.RootViewSurfaceTaker;
@@ -28,6 +29,8 @@
import com.android.internal.widget.DecorCaptionView;
import com.android.internal.widget.FloatingToolbar;
+import java.util.List;
+
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.app.ActivityManager;
@@ -48,6 +51,7 @@
import android.view.Gravity;
import android.view.InputQueue;
import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -85,6 +89,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
/** @hide */
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
@@ -1956,6 +1961,21 @@
res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
}
+ /**
+ * @hide
+ */
+ @Override
+ public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list) {
+ final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
+ if (!mWindow.isDestroyed() && st != null && mWindow.getCallback() != null) {
+ try {
+ mWindow.getCallback().onProvideKeyboardShortcuts(list, st.menu);
+ } catch (AbstractMethodError e) {
+ // We run into this if the app is using supportlib.
+ }
+ }
+ }
+
private static class ColorViewState {
View view = null;
int targetVisibility = View.INVISIBLE;
diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index e79f1b8..59a1e4a 100644
--- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -62,6 +62,7 @@
private final SnapTarget mDismissStartTarget;
private final SnapTarget mDismissEndTarget;
+ private final SnapTarget mMiddleTarget;
public DividerSnapAlgorithm(Resources res, float minFlingVelocityPxPerSecond,
int displayWidth, int displayHeight, int dividerSize, boolean isHorizontalDivision,
@@ -80,6 +81,7 @@
mLastSplitTarget = mTargets.get(mTargets.size() - 2);
mDismissStartTarget = mTargets.get(0);
mDismissEndTarget = mTargets.get(mTargets.size() - 1);
+ mMiddleTarget = mTargets.get(mTargets.size() / 2);
}
public SnapTarget calculateSnapTarget(int position, float velocity) {
@@ -207,12 +209,12 @@
}
private void addMiddleTarget(boolean isHorizontalDivision) {
- int start = isHorizontalDivision ? mInsets.top : mInsets.left;
- int end = isHorizontalDivision
- ? mDisplayHeight - mInsets.bottom
- : mDisplayWidth - mInsets.right;
- mTargets.add(new SnapTarget(start + (end - start) / 2 - mDividerSize / 2,
- SnapTarget.FLAG_NONE));
+ mTargets.add(new SnapTarget(DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+ mInsets, mDisplayWidth, mDisplayHeight, mDividerSize), SnapTarget.FLAG_NONE));
+ }
+
+ public SnapTarget getMiddleTarget() {
+ return mMiddleTarget;
}
/**
diff --git a/core/java/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
index 25a060e..d06e2bb 100644
--- a/core/java/com/android/internal/policy/DockedDividerUtils.java
+++ b/core/java/com/android/internal/policy/DockedDividerUtils.java
@@ -19,6 +19,11 @@
import android.graphics.Rect;
import android.view.WindowManager;
+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;
+
/**
* Utility functions for docked stack divider used by both window manager and System UI.
*
@@ -71,4 +76,45 @@
return 0;
}
}
+
+ public static int calculateMiddlePosition(boolean isHorizontalDivision, Rect insets,
+ int displayWidth, int displayHeight, int dividerSize) {
+ int start = isHorizontalDivision ? insets.top : insets.left;
+ int end = isHorizontalDivision
+ ? displayHeight - insets.bottom
+ : displayWidth - insets.right;
+ return start + (end - start) / 2 - dividerSize / 2;
+ }
+
+ public static int getDockSideFromCreatedMode(boolean dockOnTopOrLeft,
+ boolean isHorizontalDivision) {
+ if (dockOnTopOrLeft) {
+ if (isHorizontalDivision) {
+ return DOCKED_TOP;
+ } else {
+ return DOCKED_LEFT;
+ }
+ } else {
+ if (isHorizontalDivision) {
+ return DOCKED_BOTTOM;
+ } else {
+ return DOCKED_RIGHT;
+ }
+ }
+ }
+
+ public static int invertDockSide(int dockSide) {
+ switch (dockSide) {
+ case WindowManager.DOCKED_LEFT:
+ return WindowManager.DOCKED_RIGHT;
+ case WindowManager.DOCKED_TOP:
+ return WindowManager.DOCKED_BOTTOM;
+ case WindowManager.DOCKED_RIGHT:
+ return WindowManager.DOCKED_LEFT;
+ case WindowManager.DOCKED_BOTTOM:
+ return WindowManager.DOCKED_TOP;
+ default:
+ return WindowManager.DOCKED_INVALID;
+ }
+ }
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index f159a4d..b4c4ef5 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2677,20 +2677,16 @@
invalidatePanelMenu(FEATURE_ACTION_BAR);
}
} else {
- mTitleView = (TextView)findViewById(R.id.title);
+ mTitleView = (TextView) findViewById(R.id.title);
if (mTitleView != null) {
- mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
- View titleContainer = findViewById(
- R.id.title_container);
+ final View titleContainer = findViewById(R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
- if (mContentParent instanceof FrameLayout) {
- ((FrameLayout)mContentParent).setForeground(null);
- }
+ mContentParent.setForeground(null);
} else {
mTitleView.setText(mTitle);
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 16bf9dd..ca1334c 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -26,6 +26,7 @@
import java.lang.reflect.Array;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -372,6 +373,10 @@
return (array != null) ? array.clone() : null;
}
+ public static @Nullable <T> ArraySet<T> cloneOrNull(@Nullable ArraySet<T> array) {
+ return (array != null) ? new ArraySet<T>(array) : null;
+ }
+
public static @NonNull <T> ArraySet<T> add(@Nullable ArraySet<T> cur, T val) {
if (cur == null) {
cur = new ArraySet<>();
@@ -420,6 +425,16 @@
return (cur != null) ? cur.contains(val) : false;
}
+ public static @Nullable <T> T[] trimToSize(@Nullable T[] array, int size) {
+ if (array == null || size == 0) {
+ return null;
+ } else if (array.length == size) {
+ return array;
+ } else {
+ return Arrays.copyOf(array, size);
+ }
+ }
+
/**
* Returns true if the two ArrayLists are equal with respect to the objects they contain.
* The objects must be in the same order and be reference equal (== not .equals()).
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index b692a18..381e71f 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -16,6 +16,10 @@
package com.android.internal.util;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.text.TextUtils;
+
import java.util.Collection;
/**
@@ -31,6 +35,22 @@
}
/**
+ * Ensures that an string reference passed as a parameter to the calling
+ * method is not empty.
+ *
+ * @param string an string reference
+ * @return the string reference that was validated
+ * @throws IllegalArgumentException if {@code string} is empty
+ */
+ public static @NonNull String checkStringNotEmpty(final String string,
+ final Object errorMessage) {
+ if (TextUtils.isEmpty(string)) {
+ throw new IllegalArgumentException(String.valueOf(errorMessage));
+ }
+ return string;
+ }
+
+ /**
* Ensures that an object reference passed as a parameter to the calling
* method is not null.
*
@@ -38,7 +58,7 @@
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
- public static <T> T checkNotNull(final T reference) {
+ public static @NonNull <T> T checkNotNull(final T reference) {
if (reference == null) {
throw new NullPointerException();
}
@@ -55,7 +75,7 @@
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
- public static <T> T checkNotNull(final T reference, final Object errorMessage) {
+ public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
}
@@ -95,7 +115,8 @@
* @return the validated numeric value
* @throws IllegalArgumentException if {@code value} was negative
*/
- public static int checkArgumentNonnegative(final int value, final String errorMessage) {
+ public static @IntRange(from = 0) int checkArgumentNonnegative(final int value,
+ final String errorMessage) {
if (value < 0) {
throw new IllegalArgumentException(errorMessage);
}
@@ -218,6 +239,33 @@
}
/**
+ * Ensures that the argument long value is within the inclusive range.
+ *
+ * @param value a long value
+ * @param lower the lower endpoint of the inclusive range
+ * @param upper the upper endpoint of the inclusive range
+ * @param valueName the name of the argument to use if the check fails
+ *
+ * @return the validated long value
+ *
+ * @throws IllegalArgumentException if {@code value} was not within the range
+ */
+ public static long checkArgumentInRange(long value, long lower, long upper,
+ String valueName) {
+ if (value < lower) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%d, %d] (too low)", valueName, lower, upper));
+ } else if (value > upper) {
+ throw new IllegalArgumentException(
+ String.format(
+ "%s is out of range of [%d, %d] (too high)", valueName, lower, upper));
+ }
+
+ return value;
+ }
+
+ /**
* Ensures that the array is not {@code null}, and none of its elements are {@code null}.
*
* @param value an array of boxed objects
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 8699843..aa4b564c 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -25,6 +25,8 @@
import android.view.IWindow;
import android.view.IWindowSession;
+import com.android.internal.os.IResultReceiver;
+
public class BaseIWindow extends IWindow.Stub {
private IWindowSession mSession;
public int mSeq;
@@ -103,4 +105,8 @@
@Override
public void dispatchWindowShown() {
}
+
+ @Override
+ public void requestAppKeyboardShortcuts(IResultReceiver receiver) {
+ }
}
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 85ec29c..0e7f06b 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -30,7 +30,7 @@
import java.lang.ref.WeakReference;
-public class IInputConnectionWrapper extends IInputContext.Stub {
+public abstract class IInputConnectionWrapper extends IInputContext.Stub {
static final String TAG = "IInputConnectionWrapper";
private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
@@ -80,15 +80,25 @@
}
public IInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
- mInputConnection = new WeakReference<InputConnection>(conn);
+ mInputConnection = new WeakReference<>(conn);
mMainLooper = mainLooper;
mH = new MyHandler(mMainLooper);
}
- public boolean isActive() {
- return true;
- }
-
+ abstract protected boolean isActive();
+
+ /**
+ * Called when the user took some actions that should be taken into consideration to update the
+ * LRU list for input method rotation.
+ */
+ abstract protected void onUserAction();
+
+ /**
+ * Called when the input method started or stopped full-screen mode.
+ *
+ */
+ abstract protected void onReportFullscreenMode(boolean enabled);
+
public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
}
@@ -284,6 +294,7 @@
return;
}
ic.commitText((CharSequence)msg.obj, msg.arg1);
+ onUserAction();
return;
}
case DO_SET_SELECTION: {
@@ -338,6 +349,7 @@
return;
}
ic.setComposingText((CharSequence)msg.obj, msg.arg1);
+ onUserAction();
return;
}
case DO_SET_COMPOSING_REGION: {
@@ -369,6 +381,7 @@
return;
}
ic.sendKeyEvent((KeyEvent)msg.obj);
+ onUserAction();
return;
}
case DO_CLEAR_META_KEY_STATES: {
@@ -413,7 +426,9 @@
Log.w(TAG, "reportFullscreenMode on inexistent InputConnection");
return;
}
- ic.reportFullscreenMode(msg.arg1 == 1);
+ final boolean enabled = msg.arg1 == 1;
+ ic.reportFullscreenMode(enabled);
+ onReportFullscreenMode(enabled);
return;
}
case DO_PERFORM_PRIVATE_COMMAND: {
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index c2adc42..a67e43a 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -1,3 +1,19 @@
+/*
+ * 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.internal.view.menu;
import android.content.Context;
@@ -7,7 +23,6 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnKeyListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -30,7 +45,7 @@
MenuPresenter, OnKeyListener {
private final Context mContext;
- private final LayoutInflater mInflater;
+
private final MenuBuilder mMenu;
private final MenuAdapter mAdapter;
private final boolean mOverflowOnly;
@@ -79,8 +94,6 @@
private Callback mPresenterCallback;
private ViewTreeObserver mTreeObserver;
- private ViewGroup mMeasureParent;
-
/** Whether the popup has been dismissed. Once dismissed, it cannot be opened again. */
private boolean mWasDismissed;
@@ -99,10 +112,10 @@
public StandardMenuPopup(Context context, MenuBuilder menu, View anchorView, int popupStyleAttr,
int popupStyleRes, boolean overflowOnly) {
mContext = Preconditions.checkNotNull(context);
- mInflater = LayoutInflater.from(context);
mMenu = menu;
mOverflowOnly = overflowOnly;
- mAdapter = new MenuAdapter(menu, mInflater, mOverflowOnly);
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ mAdapter = new MenuAdapter(menu, inflater, mOverflowOnly);
mPopupStyleAttr = popupStyleAttr;
mPopupStyleRes = popupStyleRes;
@@ -155,8 +168,7 @@
mPopup.setDropDownGravity(mDropDownGravity);
if (!mHasContentWidth) {
- mContentWidth = measureIndividualMenuWidth(
- mAdapter, mMeasureParent, mContext, mPopupMaxWidth);
+ mContentWidth = measureIndividualMenuWidth(mAdapter, null, mContext, mPopupMaxWidth);
mHasContentWidth = true;
}
@@ -177,7 +189,9 @@
false);
TextView titleView = (TextView) titleItemView.findViewById(
com.android.internal.R.id.title);
- titleView.setText(mMenu.getHeaderTitle());
+ if (titleView != null) {
+ titleView.setText(mMenu.getHeaderTitle());
+ }
titleItemView.setEnabled(false);
listView.addHeaderView(titleItemView, null, false);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e38d82f..4e8f19c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -24,6 +24,7 @@
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.IBinder;
@@ -34,6 +35,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.provider.Settings;
@@ -135,6 +137,8 @@
private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
+ private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
+
// Maximum allowed number of repeated or ordered characters in a sequence before we'll
// consider it a complex PIN/password.
public static final int MAX_ALLOWED_SEQUENCE = 3;
@@ -143,6 +147,7 @@
private final ContentResolver mContentResolver;
private DevicePolicyManager mDevicePolicyManager;
private ILockSettings mLockSettingsService;
+ private UserManager mUserManager;
public static final class RequestThrottledException extends Exception {
@@ -173,6 +178,13 @@
return mDevicePolicyManager;
}
+ private UserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = UserManager.get(mContext);
+ }
+ return mUserManager;
+ }
+
private TrustManager getTrustManager() {
TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
if (trust == null) {
@@ -866,6 +878,39 @@
}
/**
+ * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op
+ * for user handles that do not belong to a managed profile.
+ */
+ public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled) {
+ UserInfo info = getUserManager().getUserInfo(userHandle);
+ if (info.isManagedProfile()) {
+ setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userHandle);
+ }
+ }
+
+ /**
+ * Retrieves whether the Separate Profile Challenge is enabled for this {@param userHandle}.
+ */
+ public boolean isSeparateProfileChallengeEnabled(int userHandle) {
+ UserInfo info = getUserManager().getUserInfo(userHandle);
+ if (info == null || !info.isManagedProfile()) {
+ return false;
+ }
+ return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userHandle);
+ }
+
+ /**
+ * Retrieves whether the current DPM allows use of the Profile Challenge.
+ */
+ public boolean isSeparateProfileChallengeAllowed(int userHandle) {
+ UserInfo info = getUserManager().getUserInfo(userHandle);
+ if (info == null || !info.isManagedProfile()) {
+ return false;
+ }
+ return getDevicePolicyManager().isSeparateProfileChallengeAllowed(userHandle);
+ }
+
+ /**
* Deserialize a pattern.
* @param string The pattern serialized with {@link #patternToString}
* @return The pattern.
@@ -1288,10 +1333,6 @@
}
}
- public static boolean isSeparateWorkChallengeEnabled() {
- return StorageManager.isFileBasedEncryptionEnabled();
- }
-
public void registerStrongAuthTracker(final StrongAuthTracker strongAuthTracker) {
try {
getLockSettings().registerStrongAuthTracker(strongAuthTracker.mStub);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 30593f2..fc1ba44 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -7,6 +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_CPPFLAGS += -Wno-conversion-null
ifeq ($(TARGET_ARCH), arm)
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 63f193d..40af22a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1257,6 +1257,7 @@
REG_JNI(register_android_os_Binder),
REG_JNI(register_android_os_Parcel),
REG_JNI(register_android_nio_utils),
+ REG_JNI(register_android_graphics_Canvas),
REG_JNI(register_android_graphics_Graphics),
REG_JNI(register_android_view_DisplayEventReceiver),
REG_JNI(register_android_view_RenderNode),
@@ -1289,7 +1290,6 @@
REG_JNI(register_android_graphics_BitmapRegionDecoder),
REG_JNI(register_android_graphics_Camera),
REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),
- REG_JNI(register_android_graphics_Canvas),
REG_JNI(register_android_graphics_CanvasProperty),
REG_JNI(register_android_graphics_ColorFilter),
REG_JNI(register_android_graphics_DrawFilter),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index a805b6d..80ccb61 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -774,11 +774,14 @@
return ret;
}
-static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
- LocalScopedBitmap bitmap(bitmapHandle);
+static void Bitmap_destruct(Bitmap* bitmap) {
bitmap->detachFromJava();
}
+static jlong Bitmap_getNativeFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Bitmap_destruct));
+}
+
static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
LocalScopedBitmap bitmap(bitmapHandle);
bitmap->freePixels();
@@ -1357,7 +1360,7 @@
(void*)Bitmap_copy },
{ "nativeCopyAshmem", "(J)Landroid/graphics/Bitmap;",
(void*)Bitmap_copyAshmem },
- { "nativeDestructor", "(J)V", (void*)Bitmap_destructor },
+ { "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer },
{ "nativeRecycle", "(J)Z", (void*)Bitmap_recycle },
{ "nativeReconfigure", "(JIIIIZ)V", (void*)Bitmap_reconfigure },
{ "nativeCompress", "(JIILjava/io/OutputStream;[B)Z",
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index 83fd073..1a86e5f 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -42,7 +42,7 @@
}
static jlong CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) {
- return reinterpret_cast<jlong>(SkColorFilter::CreateLightingFilter(mul, add));
+ return reinterpret_cast<jlong>(SkColorMatrixFilter::CreateLightingFilter(mul, add));
}
static jlong CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 0a25a0a..98f8ce3 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -76,9 +76,12 @@
AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
};
- static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
- Paint* obj = reinterpret_cast<Paint*>(objHandle);
- delete obj;
+ static void deletePaint(Paint* paint) {
+ delete paint;
+ }
+
+ static jlong getNativeFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&deletePaint));
}
static jlong init(JNIEnv* env, jobject) {
@@ -863,7 +866,7 @@
}; // namespace PaintGlue
static const JNINativeMethod methods[] = {
- {"nFinalizer", "(J)V", (void*) PaintGlue::finalizer},
+ {"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer},
{"nInit","()J", (void*) PaintGlue::init},
{"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 799ed83..2507e4d 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -61,18 +61,27 @@
SkASSERT(shaderHandle);
SkAutoTUnref<SkShader> currentShader(reinterpret_cast<SkShader*>(shaderHandle));
- SkMatrix currentMatrix;
- SkAutoTUnref<SkShader> baseShader(currentShader->refAsALocalMatrixShader(¤tMatrix));
+ // Attempt to peel off an existing proxy shader and get the proxy's matrix. If
+ // the proxy existed and it's matrix equals the desired matrix then just return
+ // the proxy, otherwise replace it with a new proxy containing the desired matrix.
+ //
+ // refAsALocalMatrixShader(): if the shader contains a proxy then it unwraps the proxy
+ // returning both the underlying shader and the proxy's matrix.
+ // newWithLocalMatrix(): will return a proxy shader that wraps the provided shader and
+ // concats the provided local matrix with the shader's matrix.
+ //
+ // WARNING: This proxy replacement only behaves like a setter because the Java
+ // API enforces that all local matrices are set using this call and
+ // not passed to the constructor of the Shader.
+ SkMatrix proxyMatrix;
+ SkAutoTUnref<SkShader> baseShader(currentShader->refAsALocalMatrixShader(&proxyMatrix));
if (baseShader.get()) {
- // if the matrices are same then there is no need to allocate a new
- // shader that is identical to the existing one.
- if (currentMatrix == *matrix) {
+ if (proxyMatrix == *matrix) {
return reinterpret_cast<jlong>(currentShader.detach());
}
- return reinterpret_cast<jlong>(SkShader::CreateLocalMatrixShader(baseShader, *matrix));
+ return reinterpret_cast<jlong>(baseShader->newWithLocalMatrix(*matrix));
}
-
- return reinterpret_cast<jlong>(SkShader::CreateLocalMatrixShader(currentShader, *matrix));
+ return reinterpret_cast<jlong>(currentShader->newWithLocalMatrix(*matrix));
}
///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index e4e73a4..34877e0 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -37,8 +37,12 @@
return reinterpret_cast<Canvas*>(canvasHandle);
}
-static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
- delete get_canvas(canvasHandle);
+static void delete_canvas(Canvas* canvas) {
+ delete canvas;
+}
+
+static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&delete_canvas));
}
// Native wrapper constructor used by Canvas(Bitmap)
@@ -710,7 +714,7 @@
}; // namespace CanvasJNI
static const JNINativeMethod gMethods[] = {
- {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
+ {"getNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer},
{"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
{"native_setBitmap", "!(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
{"native_isOpaque","!(J)Z", (void*) CanvasJNI::isOpaque},
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index e162810..8e8f6c3 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -152,7 +152,8 @@
static struct {
jmethodID postDynPolicyEventFromNative;
-} gDynPolicyEventHandlerMethods;
+ jmethodID postRecordConfigEventFromNative;
+} gAudioPolicyEventHandlerMethods;
static Mutex gLock;
@@ -378,12 +379,26 @@
const char* zechars = regId.string();
jstring zestring = env->NewStringUTF(zechars);
- env->CallStaticVoidMethod(clazz, gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative,
+ env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative,
event, zestring, val);
env->ReleaseStringUTFChars(zestring, zechars);
env->DeleteLocalRef(clazz);
+}
+static void
+android_media_AudioSystem_recording_callback(int event, int session, int source)
+{
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ if (env == NULL) {
+ return;
+ }
+
+ jclass clazz = env->FindClass(kClassPathName);
+ env->CallStaticVoidMethod(clazz,
+ gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative,
+ event, session, source);
+ env->DeleteLocalRef(clazz);
}
static jint
@@ -1503,6 +1518,12 @@
AudioSystem::setDynPolicyCallback(android_media_AudioSystem_dyn_policy_callback);
}
+static void
+android_media_AudioSystem_registerRecordingCallback(JNIEnv *env, jobject thiz)
+{
+ AudioSystem::setRecordConfigCallback(android_media_AudioSystem_recording_callback);
+}
+
static jint convertAudioMixToNative(JNIEnv *env,
AudioMix *nAudioMix,
@@ -1677,6 +1698,8 @@
(void *)android_media_AudioSystem_registerPolicyMixes},
{"native_register_dynamic_policy_callback", "()V",
(void *)android_media_AudioSystem_registerDynPolicyCallback},
+ {"native_register_recording_callback", "()V",
+ (void *)android_media_AudioSystem_registerRecordingCallback},
{"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
};
@@ -1780,9 +1803,12 @@
gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env,
eventHandlerClass, "mJniCallback", "J");
- gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative =
+ gAudioPolicyEventHandlerMethods.postDynPolicyEventFromNative =
GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
"dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
+ gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative =
+ GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
+ "recordingCallbackFromNative", "(III)V");
jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 2488111..03a1e71 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -84,6 +84,7 @@
jfieldID privateClean_field;
jfieldID sharedClean_field;
jfieldID swappedOut_field;
+ jfieldID swappedOutPss_field;
};
struct stat_field_names {
@@ -94,20 +95,22 @@
const char* privateClean_name;
const char* sharedClean_name;
const char* swappedOut_name;
+ const char* swappedOutPss_name;
};
static stat_fields stat_fields[_NUM_CORE_HEAP];
static stat_field_names stat_field_names[_NUM_CORE_HEAP] = {
{ "otherPss", "otherSwappablePss", "otherPrivateDirty", "otherSharedDirty",
- "otherPrivateClean", "otherSharedClean", "otherSwappedOut" },
+ "otherPrivateClean", "otherSharedClean", "otherSwappedOut", "otherSwappedOutPss" },
{ "dalvikPss", "dalvikSwappablePss", "dalvikPrivateDirty", "dalvikSharedDirty",
- "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut" },
+ "dalvikPrivateClean", "dalvikSharedClean", "dalvikSwappedOut", "dalvikSwappedOutPss" },
{ "nativePss", "nativeSwappablePss", "nativePrivateDirty", "nativeSharedDirty",
- "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut" }
+ "nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" }
};
jfieldID otherStats_field;
+jfieldID hasSwappedOutPss_field;
static bool memtrackLoaded;
@@ -119,6 +122,7 @@
int privateClean;
int sharedClean;
int swappedOut;
+ int swappedOutPss;
};
#define BINDER_STATS "/proc/binder/stats"
@@ -206,7 +210,7 @@
return err;
}
-static void read_mapinfo(FILE *fp, stats_t* stats)
+static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss)
{
char line[1024];
int len, nameLen;
@@ -216,7 +220,7 @@
float sharing_proportion = 0.0;
unsigned shared_clean = 0, shared_dirty = 0;
unsigned private_clean = 0, private_dirty = 0;
- unsigned swapped_out = 0;
+ unsigned swapped_out = 0, swapped_out_pss = 0;
bool is_swappable = false;
unsigned temp;
@@ -230,6 +234,8 @@
int subHeap = HEAP_UNKNOWN;
int prevHeap = HEAP_UNKNOWN;
+ *foundSwapPss = false;
+
if(fgets(line, sizeof(line), fp) == 0) return;
while (!done) {
@@ -340,6 +346,7 @@
private_clean = 0;
private_dirty = 0;
swapped_out = 0;
+ swapped_out_pss = 0;
while (true) {
if (fgets(line, 1024, fp) == 0) {
@@ -365,6 +372,9 @@
/* referenced = temp; */
} else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
swapped_out = temp;
+ } else if (line[0] == 'S' && sscanf(line, "SwapPss: %d kB", &temp) == 1) {
+ *foundSwapPss = true;
+ swapped_out_pss = temp;
} else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
// looks like a new mapping
// example: "10000000-10001000 ---p 10000000 00:00 0"
@@ -390,6 +400,7 @@
stats[whichHeap].privateClean += private_clean;
stats[whichHeap].sharedClean += shared_clean;
stats[whichHeap].swappedOut += swapped_out;
+ stats[whichHeap].swappedOutPss += swapped_out_pss;
if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) {
stats[subHeap].pss += pss;
stats[subHeap].swappablePss += swappable_pss;
@@ -398,12 +409,13 @@
stats[subHeap].privateClean += private_clean;
stats[subHeap].sharedClean += shared_clean;
stats[subHeap].swappedOut += swapped_out;
+ stats[subHeap].swappedOutPss += swapped_out_pss;
}
}
}
}
-static void load_maps(int pid, stats_t* stats)
+static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
{
char tmp[128];
FILE *fp;
@@ -412,17 +424,18 @@
fp = fopen(tmp, "r");
if (fp == 0) return;
- read_mapinfo(fp, stats);
+ read_mapinfo(fp, stats, foundSwapPss);
fclose(fp);
}
static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
jint pid, jobject object)
{
+ bool foundSwapPss;
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
- load_maps(pid, stats);
+ load_maps(pid, stats, &foundSwapPss);
struct graphics_memory_pss graphics_mem;
if (read_memtrack_memory(pid, &graphics_mem) == 0) {
@@ -442,6 +455,7 @@
stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
+ stats[HEAP_UNKNOWN].swappedOutPss += stats[i].swappedOutPss;
}
for (int i=0; i<_NUM_CORE_HEAP; i++) {
@@ -452,9 +466,11 @@
env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
+ env->SetIntField(object, stat_fields[i].swappedOutPss_field, stats[i].swappedOutPss);
}
+ env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss);
jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
@@ -471,6 +487,7 @@
otherArray[j++] = stats[i].privateClean;
otherArray[j++] = stats[i].sharedClean;
otherArray[j++] = stats[i].swappedOut;
+ otherArray[j++] = stats[i].swappedOutPss;
}
env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
@@ -481,11 +498,12 @@
android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
}
-static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jlongArray outUss,
- jlongArray outMemtrack)
+static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid,
+ jlongArray outUssSwapPss, jlongArray outMemtrack)
{
char line[1024];
jlong pss = 0;
+ jlong swapPss = 0;
jlong uss = 0;
jlong memtrack = 0;
@@ -521,19 +539,31 @@
}
uss += atoi(c);
}
+ } else if (line[0] == 'S' && strncmp(line, "SwapPss:", 8) == 0) {
+ char* c = line + 8;
+ jlong lSwapPss;
+ while (*c != 0 && (*c < '0' || *c > '9')) {
+ c++;
+ }
+ lSwapPss = atoi(c);
+ swapPss += lSwapPss;
+ pss += lSwapPss; // Also in swap, those pages would be accounted as Pss without SWAP
}
}
fclose(fp);
}
- if (outUss != NULL) {
- if (env->GetArrayLength(outUss) >= 1) {
- jlong* outUssArray = env->GetLongArrayElements(outUss, 0);
- if (outUssArray != NULL) {
- outUssArray[0] = uss;
+ if (outUssSwapPss != NULL) {
+ if (env->GetArrayLength(outUssSwapPss) >= 1) {
+ jlong* outUssSwapPssArray = env->GetLongArrayElements(outUssSwapPss, 0);
+ if (outUssSwapPssArray != NULL) {
+ outUssSwapPssArray[0] = uss;
+ if (env->GetArrayLength(outUssSwapPss) >= 2) {
+ outUssSwapPssArray[1] = swapPss;
+ }
}
- env->ReleaseLongArrayElements(outUss, outUssArray, 0);
+ env->ReleaseLongArrayElements(outUssSwapPss, outUssSwapPssArray, 0);
}
}
@@ -1056,6 +1086,7 @@
}
otherStats_field = env->GetFieldID(clazz, "otherStats", "[I");
+ hasSwappedOutPss_field = env->GetFieldID(clazz, "hasSwappedOutPss", "Z");
for (int i=0; i<_NUM_CORE_HEAP; i++) {
stat_fields[i].pss_field =
@@ -1072,6 +1103,8 @@
env->GetFieldID(clazz, stat_field_names[i].sharedClean_name, "I");
stat_fields[i].swappedOut_field =
env->GetFieldID(clazz, stat_field_names[i].swappedOut_name, "I");
+ stat_fields[i].swappedOutPss_field =
+ env->GetFieldID(clazz, stat_field_names[i].swappedOutPss_name, "I");
}
return jniRegisterNativeMethods(env, "android/os/Debug", gMethods, NELEM(gMethods));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 75077df..b02de0e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -385,6 +385,7 @@
<protected-broadcast android:name="com.android.server.device_idle.STEP_LIGHT_IDLE_STATE" />
<protected-broadcast android:name="com.android.server.Wifi.action.TOGGLE_PNO" />
<protected-broadcast android:name="intent.action.ACTION_RF_BAND_INFO" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_RESOURCE_GRANTED" />
<protected-broadcast android:name="android.app.action.INTERRUPTION_FILTER_CHANGED" />
<protected-broadcast android:name="android.app.action.INTERRUPTION_FILTER_CHANGED_INTERNAL" />
@@ -2828,6 +2829,12 @@
<permission android:name="android.permission.ACCESS_EPHEMERAL_APPS"
android:protectionLevel="signature" />
+ <!-- Allows receiving the usage of media resource e.g. video/audio codec and
+ graphic memory.
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"
+ android:protectionLevel="signature|privileged" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/core/res/res/drawable/ic_notification_alert.xml b/core/res/res/drawable/ic_notification_alert.xml
new file mode 100644
index 0000000..d17dfc1
--- /dev/null
+++ b/core/res/res/drawable/ic_notification_alert.xml
@@ -0,0 +1,33 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M18.4,2.2L17.0,3.6c2.0,1.4 3.3,3.7 3.5,6.4l2.0,0.0C22.3,6.8 20.8,4.0 18.4,2.2z"
+ android:fillColor="#231F20"/>
+ <path
+ android:pathData="M7.1,3.6L5.7,2.2C3.3,4.0 1.7,6.8 1.5,10.0l2.0,0.0C3.7,7.3 5.0,5.0 7.1,3.6z"
+ android:fillColor="#231F20"/>
+ <path
+ android:pathData="M18.5,10.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.5,3.5C13.5,2.7 12.8,2.0 12.0,2.0s-1.5,0.7 -1.5,1.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.5,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0l-2.0,-2.0L18.5,10.5zM13.0,16.5l-2.0,0.0l0.0,-2.0l2.0,0.0L13.0,16.5zM13.0,12.5l-2.0,0.0l0.0,-6.0l2.0,0.0L13.0,12.5z"
+ android:fillColor="#231F20"/>
+ <path
+ android:pathData="M12.0,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0L10.0,20.0C10.0,21.1 10.9,22.0 12.0,22.0z"
+ android:fillColor="#231F20"/>
+</vector>
diff --git a/core/res/res/drawable/ic_notification_block.xml b/core/res/res/drawable/ic_notification_block.xml
new file mode 100644
index 0000000..27690740
--- /dev/null
+++ b/core/res/res/drawable/ic_notification_block.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zM4.0,12.0c0.0,-4.42 3.58,-8.0 8.0,-8.0 1.85,0.0 3.5,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4.0,13.85 4.0,12.0zm8.0,8.0c-1.85,0.0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20.0,10.15 20.0,12.0c0.0,4.42 -3.58,8.0 -8.0,8.0z"/>
+</vector>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 36c167e..c945459 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sluit persoonlike data soos kredietkaartnommers en wagwoorde in."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Beheer vertoonskermvergroting"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Beheer die vertoonskerm se zoemvlak en posisionering."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Voer gebare uit"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan tik, swiep, knyp en ander gebare uitvoer."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktiveer of verander statusbalk"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Laat die program toe om die statusbalk te deaktiveer en stelselikone by te voeg of te verwyder."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"wees die statusbalk"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index ec16add..17b5a29 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"እንደ የክሬዲት ካርድ ቁጥሮች እና የይለፍ ቃላት ያሉ የግል ውሂብ ያካትታል።"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"የመቆጣጠሪያ ማሳያ እንዲጎላ አደራረግ"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"የማሳያውን የማጉያ ደረጃ እና አቀማመጥ ይቆጣጠሩ።"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"የጣት ምልክቶችን ያከናውኑ"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"መታ ማድረግ፣ ማንሸራተት፣ መቆንጠጥ እና ሌሎች የጣት ምልክቶችን ማከናወን ይችላል።"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"የሁኔቴ አሞሌ አቦዝን ወይም ቀይር"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"የስርዓት አዶዎችን ወደ ሁኔታ አሞሌ ላለማስቻል ወይም ለማከል እና ለማስወገድ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"የሁኔታ አሞሌ መሆን"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 12ef759..efde61f 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -260,6 +260,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"يتضمن بيانات شخصية مثل أرقام بطاقات الائتمان وكلمات المرور."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"التحكم في تكبير الشاشة"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"يمكنك التحكم في مستوى التكبير/التصغير للشاشة وتحديد الموضع."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"تنفيذ إيماءات"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"يمكن النقر والتمرير بسرعة والتصغير وتنفيذ إيماءات أخرى."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"تعطيل شريط الحالة أو تعديله"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"للسماح للتطبيق بتعطيل شريط الحالة أو إضافة رموز نظام وإزالتها."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"العمل كشريط للحالة"</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 7f0e6d7..0e4771e 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kredit kartı nömrələri və parollar kimi şəxsi məlumatlar daxildir."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekran böyütməsinə nəzarət edin"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ekran yaxınlaşdırma səviyyəsi və yerləşdirməsinə nəzarət edin."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Jestlər ilə əməliyyat aparın"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Digər jestlərə tıklaya, sürüşdürə və əməliyyat apara bilərsiniz."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"status panelini deaktivləşdir və ya dəyişdir"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Tətbiqə status panelini deaktiv etməyə və ya sistem ikonalarını əlavə etmək və ya silmək imkanı verir."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"status paneli edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ac5ce66..fd9ddec 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -257,6 +257,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Obuhvata lične podatke kao što su brojevi kreditnih kartica i lozinke."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Upravljaj uvećanjem prikaza"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Upravlja nivoom zumiranja prikaza i određivanjem položaja."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Obavljanje pokreta"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Može da dodiruje, lista, skuplja prikaz i obavlja druge pokrete."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"onemogućavanje ili izmena statusne trake"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Dozvoljava aplikaciji da onemogući statusnu traku ili da dodaje i uklanja sistemske ikone."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"funkcionisanje kao statusna traka"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 301e1e3..955727b 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Включва лични данни, като например номера на кредитни карти и пароли."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Управление на увеличението на дисплея"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Управление на нивото на мащаба и позиционирането на дисплея."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Извършване на жестове"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Можете да докосвате, да прекарвате пръст, да събирате пръсти и да извършвате други жестове."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"деактивиране или промяна на лентата на състоянието"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Разрешава на приложението да деактивира лентата на състоянието или да добавя и премахва системни икони."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"изпълняване на ролята на лента на състоянието"</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 9c88d44..6e7a77f 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ক্রেডিট কার্ডের নম্বর ও পাসওয়ার্ডগুলির মতো ব্যক্তিগত তথ্য অন্তর্ভুক্ত করে৷"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"প্রদর্শনের বৃহত্তরীকরণ ব্যবস্থা নিয়ন্ত্রণ করুন"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"প্রদর্শনের জুমের স্তর এবং অবস্থান নির্ধারন নিয়ন্ত্রণ করুন৷"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"অঙ্গভঙ্গির কাজগুলি সম্পাদন করুন"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"আলতো চাপ দেওয়া, সোয়াইপ, পিঞ্চ করা এবং অন্যান্য অঙ্গভঙ্গির কাজগুলি সম্পাদন করতে পারবেন৷"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"স্থিতি দন্ড নিষ্ক্রিয় অথবা সংশোধন করে"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"অ্যাপ্লিকেশানকে স্থিতি দন্ড অক্ষম করতে এবং সিস্টেম আইকনগুলি সরাতে দেয়৷"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"স্থিতি দন্ডে থাকুন"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 8554b843..bbe9887 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclou dades personals com ara números de targetes de crèdit i contrasenyes."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controla l\'ampliació de la pantalla"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el nivell i el posicionament del zoom de la pantalla."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Utilitza gestos"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Pot tocar, lliscar, pessigar i utilitzar altres gestos."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra d\'estat"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"aparèixer a la barra d\'estat"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 2eca49a..f5763f9 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -258,6 +258,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sledování zahrnuje osobní údaje, jako jsou například čísla kreditních karet a hesla."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Nastavení zvětšení obsahu obrazovky"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Určuje umístění a úroveň přiblížení displeje."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Provádění gest"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Může provádět gesta klepnutí, přejetí, stažení prstů a další."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zakázání či změny stavového řádku"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikaci zakázat stavový řádek nebo přidat či odebrat systémové ikony."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vydávání se za stavový řádek"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 2a719b0..3e8a86d 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Dette omfatter personlige data såsom kreditkortnumre og adgangskoder."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrollér skærmforstørrelsen"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrollér skærmens zoomniveau og position."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Udfør bevægelser"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan trykke, stryge, knibe sammen og udføre andre bevægelser."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktiver eller rediger statuslinje"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Tillader, at appen kan deaktivere statusbjælken eller tilføje og fjerne systemikoner."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vær statusbjælken"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index d116a24..e2fb6ff 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -56,10 +56,10 @@
<string name="badPin" msgid="9015277645546710014">"Die von Ihnen eingegebene alte PIN ist nicht korrekt."</string>
<string name="badPuk" msgid="5487257647081132201">"Der von Ihnen eingegebene PUK ist nicht korrekt."</string>
<string name="mismatchPin" msgid="609379054496863419">"Die von Ihnen eingegebenen PIN-Nummern stimmen nicht überein."</string>
- <string name="invalidPin" msgid="3850018445187475377">"Geben Sie eine PIN ein, die 4 bis 8 Zahlen enthält."</string>
- <string name="invalidPuk" msgid="8761456210898036513">"Geben Sie eine mindestens achtstellige PUK ein."</string>
- <string name="needPuk" msgid="919668385956251611">"Ihre SIM-Karte ist mit einem PUK gesperrt. Geben Sie zum Entsperren den PUK-Code ein."</string>
- <string name="needPuk2" msgid="4526033371987193070">"Geben Sie zum Entsperren der SIM-Karte den PUK2 ein."</string>
+ <string name="invalidPin" msgid="3850018445187475377">"Gib eine PIN ein, die 4 bis 8 Zahlen enthält."</string>
+ <string name="invalidPuk" msgid="8761456210898036513">"Gib eine mindestens achtstellige PUK ein."</string>
+ <string name="needPuk" msgid="919668385956251611">"Deine SIM-Karte ist mit einem PUK gesperrt. Gib zum Entsperren den PUK-Code ein."</string>
+ <string name="needPuk2" msgid="4526033371987193070">"Gib zum Entsperren der SIM-Karte den PUK2 ein."</string>
<string name="enablePin" msgid="209412020907207950">"Fehler. SIM-/RUIM-Sperre aktivieren."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
<item quantity="other">Sie haben noch <xliff:g id="NUMBER_1">%d</xliff:g> Versuche, bevor Ihre SIM-Karte gesperrt wird.</item>
@@ -87,7 +87,7 @@
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Beschränkt"</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"Anrufer-ID ist standardmäßig nicht beschränkt. Nächster Anruf: Nicht beschränkt"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"Dienst nicht eingerichtet."</string>
- <string name="CLIRPermanent" msgid="3377371145926835671">"Sie können die Einstellung für die Anrufer-ID nicht ändern."</string>
+ <string name="CLIRPermanent" msgid="3377371145926835671">"Du kannst die Einstellung für die Anrufer-ID nicht ändern."</string>
<string name="RestrictedChangedTitle" msgid="5592189398956187498">"Eingeschränkter Zugriff geändert"</string>
<string name="RestrictedOnData" msgid="8653794784690065540">"Daten-Dienst ist gesperrt."</string>
<string name="RestrictedOnEmergency" msgid="6581163779072833665">"Notruf ist gesperrt."</string>
@@ -156,7 +156,7 @@
<string name="httpErrorBadUrl" msgid="3636929722728881972">"Die Seite kann nicht geöffnet werden, weil die URL ungültig ist."</string>
<string name="httpErrorFile" msgid="2170788515052558676">"Auf die Datei konnte nicht zugegriffen werden."</string>
<string name="httpErrorFileNotFound" msgid="6203856612042655084">"Die angeforderte Datei wurde nicht gefunden."</string>
- <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Es werden zurzeit zu viele Anfragen verarbeitet. Versuchen Sie es später erneut."</string>
+ <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Es werden zurzeit zu viele Anfragen verarbeitet. Versuche es später erneut."</string>
<string name="notification_title" msgid="8967710025036163822">"Fehler bei Anmeldung für <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synchronisierung"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronisierung"</string>
@@ -198,7 +198,7 @@
<string name="shutdown_confirm" product="tv" msgid="476672373995075359">"Ihr Fernseher wird ausgeschaltet."</string>
<string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Ihre Uhr wird heruntergefahren."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Telefon wird heruntergefahren."</string>
- <string name="shutdown_confirm_question" msgid="2906544768881136183">"Möchten Sie das Gerät herunterfahren?"</string>
+ <string name="shutdown_confirm_question" msgid="2906544768881136183">"Möchtest du das Gerät herunterfahren?"</string>
<string name="reboot_safemode_title" msgid="7054509914500140361">"Im abgesicherten Modus starten"</string>
<string name="reboot_safemode_confirm" msgid="55293944502784668">"Möchten Sie im abgesicherten Modus neu starten? Dadurch werden alle Apps von Drittanbietern deaktiviert, die Sie installiert haben. Sie werden jedoch nach einem weiteren Neustart wiederhergestellt."</string>
<string name="recent_tasks_title" msgid="3691764623638127888">"Kürzlich geöffnet"</string>
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Einschließlich personenbezogener Daten wie Kreditkartennummern und Passwörter."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Displayvergrößerung festlegen"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Legt die Zoom-Stufe des Displays und die Zoom-Position auf dem Display fest."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Bewegungen möglich"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Tippen, Wischen, Zusammenziehen und andere Bewegungen möglich."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Statusleiste deaktivieren oder ändern"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ermöglicht der App, die Statusleiste zu deaktivieren oder Systemsymbole hinzuzufügen oder zu entfernen"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"Statusleiste darstellen"</string>
@@ -650,11 +652,11 @@
<string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Passwort zum Entsperren eingeben"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"PIN zum Entsperren eingeben"</string>
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Falscher PIN-Code"</string>
- <string name="keyguard_label_text" msgid="861796461028298424">"Drücken Sie zum Entsperren die Menütaste und dann auf \"0\"."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"Drücke zum Entsperren die Menütaste und dann auf \"0\"."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Notrufnummer"</string>
<string name="lockscreen_carrier_default" msgid="6169005837238288522">"Kein Dienst"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Display gesperrt"</string>
- <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücken Sie die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
+ <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücke die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Zum Entsperren die Menütaste drücken"</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Muster zum Entsperren zeichnen"</string>
<string name="lockscreen_emergency_call" msgid="5298642613417801888">"Notfall"</string>
@@ -667,10 +669,10 @@
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Keine SIM-Karte im Tablet"</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Keine SIM-Karte im Fernseher"</string>
<string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Keine SIM-Karte im Telefon"</string>
- <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Legen Sie eine SIM-Karte ein."</string>
- <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM-Karte fehlt oder ist nicht lesbar. Bitte legen Sie eine SIM-Karte ein."</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Lege eine SIM-Karte ein."</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM-Karte fehlt oder ist nicht lesbar. Bitte lege eine SIM-Karte ein."</string>
<string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"SIM-Karte unbrauchbar"</string>
- <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"Ihre SIM-Karte wurde dauerhaft deaktiviert.\n Wenden Sie sich an Ihren Mobilfunkanbieter, um eine andere SIM-Karte zu erhalten."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"Deine SIM-Karte wurde dauerhaft deaktiviert.\n Wende dich an deinen Mobilfunkanbieter, um eine andere SIM-Karte zu erhalten."</string>
<string name="lockscreen_transport_prev_description" msgid="6300840251218161534">"Vorheriger Titel"</string>
<string name="lockscreen_transport_next_description" msgid="573285210424377338">"Nächster Titel"</string>
<string name="lockscreen_transport_pause_description" msgid="3980308465056173363">"Pausieren"</string>
@@ -681,22 +683,22 @@
<string name="emergency_calls_only" msgid="6733978304386365407">"Nur Notrufe"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"Netzwerk gesperrt"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"PUK-Sperre auf SIM"</string>
- <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Weitere Informationen erhalten Sie im Nutzerhandbuch oder beim Kundendienst."</string>
+ <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Weitere Informationen erhältst du im Nutzerhandbuch oder beim Kundendienst."</string>
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"PIN eingeben"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"SIM-Karte wird entsperrt..."</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. \n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Tablet mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\n Bitte versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Du hast dein Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. \n\nBitte versuche es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du hast dein Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuche es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du hast dein Passwort <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch eingegeben.\n\nBitte versuche es in <xliff:g id="NUMBER_1">%2$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du hast dein Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wirst du aufgefordert, dein Tablet mithilfe deiner Google-Anmeldeinformationen zu entsperren.\n\n Bitte versuche es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Wenn Sie es noch <xliff:g id="NUMBER_1">%2$d</xliff:g>-mal falsch eingeben, werden Sie aufgefordert, Ihren Fernseher mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\n Versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden erneut."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe Ihrer Google-Anmeldeinformationen zu entsperren.\n\nBitte versuchen Sie es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du hast dein Entsperrungsmuster <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wirst du aufgefordert, dein Telefon mithilfe deiner Google-Anmeldeinformationen zu entsperren.\n\nBitte versuche es in <xliff:g id="NUMBER_2">%3$d</xliff:g> Sekunden noch einmal."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du hast <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Tablet zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Tablet auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird der Fernseher auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Sie haben <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Das Tablet wird nun auf die Werkseinstellungen zurückgesetzt."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du hast <xliff:g id="NUMBER_0">%1$d</xliff:g> Mal erfolglos versucht, das Telefon zu entsperren. Nach <xliff:g id="NUMBER_1">%2$d</xliff:g> weiteren erfolglosen Versuchen wird das Telefon auf die Werkseinstellungen zurückgesetzt und alle Nutzerdaten gehen verloren."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du hast <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Tablet zu entsperren. Das Tablet wird nun auf die Werkseinstellungen zurückgesetzt."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, den Fernseher zu entsperren. Der Fernseher wird nun auf die Werkseinstellungen zurückgesetzt."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Sie haben <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Das Telefon wird nun auf die Werkseinstellungen zurückgesetzt."</string>
- <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Versuchen Sie es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden erneut."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du hast <xliff:g id="NUMBER">%d</xliff:g>-mal erfolglos versucht, das Telefon zu entsperren. Das Telefon wird nun auf die Werkseinstellungen zurückgesetzt."</string>
+ <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Versuche es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden erneut."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Muster vergessen?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Kontoentsperrung"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"Zu viele Schemaversuche"</string>
@@ -786,11 +788,11 @@
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ermöglicht der App, Nachrichten zu Ihrem Mailbox-Posteingang hinzuzufügen"</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Geolokalisierungsberechtigungen des Browsers ändern"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ermöglicht der App, die Geolokalisierungsberechtigungen des Browsers zu ändern. Schädliche Apps können so Standortinformationen an beliebige Websites senden."</string>
- <string name="save_password_message" msgid="767344687139195790">"Möchten Sie, dass der Browser dieses Passwort speichert?"</string>
+ <string name="save_password_message" msgid="767344687139195790">"Möchtest du, dass der Browser dieses Passwort speichert?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Nicht jetzt"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Speichern"</string>
<string name="save_password_never" msgid="8274330296785855105">"Nie"</string>
- <string name="open_permission_deny" msgid="7374036708316629800">"Sie sind nicht zum Öffnen dieser Seite berechtigt."</string>
+ <string name="open_permission_deny" msgid="7374036708316629800">"Du bist nicht zum Öffnen dieser Seite berechtigt."</string>
<string name="text_copied" msgid="4985729524670131385">"Text in Zwischenablage kopiert."</string>
<string name="more_item_label" msgid="4650918923083320495">"Mehr"</string>
<string name="prepend_shortcut_label" msgid="2572214461676015642">"Menü+"</string>
@@ -903,10 +905,10 @@
<string name="aerr_process" msgid="4507058997035697579">"Der Prozess \"<xliff:g id="PROCESS">%1$s</xliff:g>\" wurde beendet."</string>
<string name="aerr_process_silence" msgid="4226685530196000222">"Silence stürzt bei <xliff:g id="PROCESS">%1$s</xliff:g> bis zum Neustart ab."</string>
<string name="anr_title" msgid="4351948481459135709"></string>
- <string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> reagiert nicht.\n\nMöchten Sie die App schließen?"</string>
- <string name="anr_activity_process" msgid="5776209883299089767">"Aktivität \"<xliff:g id="ACTIVITY">%1$s</xliff:g>\" reagiert nicht.\n\nMöchten Sie sie beenden?"</string>
- <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> reagiert nicht. Möchten Sie die App schließen?"</string>
- <string name="anr_process" msgid="6513209874880517125">"Prozess \"<xliff:g id="PROCESS">%1$s</xliff:g>\" reagiert nicht.\n\nMöchten Sie ihn beenden?"</string>
+ <string name="anr_activity_application" msgid="1904477189057199066">"<xliff:g id="APPLICATION">%2$s</xliff:g> reagiert nicht.\n\nMöchtest du die App schließen?"</string>
+ <string name="anr_activity_process" msgid="5776209883299089767">"Aktivität \"<xliff:g id="ACTIVITY">%1$s</xliff:g>\" reagiert nicht.\n\nMöchtest du sie beenden?"</string>
+ <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> reagiert nicht. Möchtest du die App schließen?"</string>
+ <string name="anr_process" msgid="6513209874880517125">"Prozess \"<xliff:g id="PROCESS">%1$s</xliff:g>\" reagiert nicht.\n\nMöchtest du ihn beenden?"</string>
<string name="force_close" msgid="8346072094521265605">"OK"</string>
<string name="report" msgid="4060218260984795706">"Bericht"</string>
<string name="wait" msgid="7147118217226317732">"Warten"</string>
@@ -1094,7 +1096,7 @@
<string name="dial_number_using" msgid="5789176425167573586">"Nummer\nmit <xliff:g id="NUMBER">%s</xliff:g> wählen"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Neuer Kontakt\nmit <xliff:g id="NUMBER">%s</xliff:g> erstellen"</string>
<string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"Die folgenden Apps benötigen die Berechtigung zum aktuellen und zukünftigen Zugriff auf Ihr Konto."</string>
- <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Möchten Sie diese Anfrage zulassen?"</string>
+ <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Möchtest du diese Anfrage zulassen?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"Zugriffsanforderung"</string>
<string name="allow" msgid="7225948811296386551">"Zulassen"</string>
<string name="deny" msgid="2081879885755434506">"Ablehnen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8116b16..3e277c3 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Περιλαμβάνει προσωπικά δεδομένα, όπως είναι οι αριθμοί πιστωτικών καρτών και οι κωδικοί πρόσβασης."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ελέγξτε τη μεγέθυνση της οθόνης"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ελέγξτε το επίπεδο ζουμ και τη θέση της οθόνης."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Εκτέλεση κινήσεων"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Επιτρέπει το πάτημα, την ολίσθηση, το πλησίασμα και άλλες κινήσεις."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"απενεργοποίηση ή τροποποίηση γραμμής κατάστασης"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Επιτρέπει στην εφαρμογή να απενεργοποιεί τη γραμμή κατάστασης ή να προσθέτει και να αφαιρεί εικονίδια συστήματος."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ορισμός ως γραμμής κατάστασης"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 930c310..0edc438 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Control display magnification"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Control the display\'s zoom level and positioning."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Perform gestures"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Can tap, swipe, pinch and perform other gestures."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 930c310..0edc438 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Control display magnification"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Control the display\'s zoom level and positioning."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Perform gestures"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Can tap, swipe, pinch and perform other gestures."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 930c310..0edc438 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Includes personal data such as credit card numbers and passwords."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Control display magnification"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Control the display\'s zoom level and positioning."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Perform gestures"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Can tap, swipe, pinch and perform other gestures."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disable or modify status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Allows the app to disable the status bar or add and remove system icons."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"be the status bar"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 258ef33..bd2f91b 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Incluye datos personales, como números de tarjeta de crédito y contraseñas."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar la ampliación de pantalla"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Usar gestos"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Permite presionar, deslizar, pellizcar y usar otros gestos."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o que agregue y elimine íconos del sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"aparecer en la barra de estado"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 9848bb6..e73b425 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Incluye datos personales como números de tarjetas de crédito y contraseñas."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controla la ampliación de la pantalla"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Realizar gestos"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Puedes tocar y pellizcar la pantalla, deslizar el dedo y hacer otros gestos."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"inhabilitar o modificar la barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que la aplicación inhabilite la barra de estado o añada y elimine iconos del sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"aparecer en la barra de estado"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 466bd5d..e6a122c 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sisaldab isiklikke andmeid, nt krediitkaardi numbreid ja paroole."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekraani suurenduse juhtimine"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Saate juhtida ekraani suumitaset ja asendit."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Liigutuste tegemine"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Saate puudutada, pühkida, sõrmi kokku-lahku liigutada ja teisi liigutusi teha."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"keela või muuda olekuriba"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Võimaldab rakendusel keelata olekuriba või lisada ja eemaldada süsteemiikoone."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"olekuribana kuvamine"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index cdb8622..31a073b 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ez da salbuespenik egiten datu pertsonalekin, hala nola, kreditu-txartelen zenbakiekin eta pasahitzekin."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrolatu pantailaren zoom-maila"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolatu pantailaren zoom-maila eta kokapena."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Keinuak egin"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Sakatu, lerratu, atximurkatu eta beste hainbat keinu egin ditzake."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Desgaitu edo aldatu egoera-barra"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Egoera-barra desgaitzea edo sistema-ikonoak gehitzea edo kentzea baimentzen die aplikazioei."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"Bihurtu egoera-barra"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b0da5fe..f0271d6 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"اطلاعات شخصی مانند شماره کارت اعتباری و گذرواژهها را لحاظ میکند."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"کنترل درشتنمایی نمایشگر"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"سطح و موقعیت بزرگنمایی نمایشگر را کنترل کنید."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"اجرای اشارهها"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"میتوانید ضربه بزنید، انگشتتان را تند بکشید، انگشتانتان را به هم نزدیک یا از هم دور کنید و اشارههای دیگری اجرا کنید."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"غیرفعال کردن یا تغییر نوار وضعیت"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"به برنامه اجازه میدهد تا نوار وضعیت را غیرفعال کند یا نمادهای سیستم را اضافه یا حذف کند."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"نوار وضعیت باشد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index f7db678..3e0c172 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sisältää henkilökohtaisia tietoja, kuten luottokortin numeroita ja salasanoja."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Näytön suurentamisen hallinnointi"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Hallinnoi näytön zoomaustasoa ja asettelua."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Eleiden käyttäminen"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Lupa napauttaa, pyyhkäistä, nipistää ja käyttää muita eleitä."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"poista tilapalkki käytöstä tai muokkaa tilapalkkia"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Antaa sovelluksen poistaa tilapalkin käytöstä ja lisätä tai poistaa järjestelmäkuvakkeita."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"sijaita tilapalkissa"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 50aed95..8450e5c 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclut des données personnelles telles que les numéros de cartes de paiement et les mots de passe."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Contrôler l\'agrandissement de l\'écran"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Contrôler le niveau de zoom et le positionnement de l\'écran."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Effectuer des gestes"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Peut toucher, balayer, pincer et effectuer d\'autres gestes."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"désactiver ou modifier la barre d\'état"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"servir de barre d\'état"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 33d2016..84c43fac 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclut des données personnelles telles que les numéros de cartes de paiement et les mots de passe."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Contrôler l\'agrandissement de l\'écran"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Contrôler le niveau de zoom et le positionnement de l\'écran"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Effectuer des gestes"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Permet d\'appuyer sur l\'écran, de le balayer, de le pincer et d\'effectuer d\'autres gestes."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Désactivation ou modification de la barre d\'état"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permet à l\'application de désactiver la barre d\'état, ou d\'ajouter et de supprimer des icônes système."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"remplacer la barre d\'état"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 7947825..36594e0 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclúe datos persoais como números e contrasinais de tarxetas de crédito."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar ampliación da pantalla"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o nivel do zoom e o posicionamento da pantalla"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Realizar xestos"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Podes tocar, pasar o dedo, beliscar e realizar outros xestos."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar ou modificar a barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite á aplicación desactivar a barra de estado ou engadir e eliminar as iconas do sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"actuar como a barra de estado"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index aa9478b..9d098fc 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ક્રેડિટ કાર્ડ નંબર્સ અને પાસવર્ડ્સ જેવો વ્યક્તિગત ડેટા શામેલ છે."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"પ્રદર્શન વિસ્તૃતિકરણ નિયંત્રિત કરો"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"પ્રદર્શનનું ઝૂમ સ્તર અને સ્થિતિનિર્ધારણ નિયંત્રિત કરો."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"હાવભાવ કરો"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ટૅપ, સ્વાઇપ, પિંચ કરી અને અન્ય હાવભાવ કરી શકે છે."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"સ્થિતિ બાર અક્ષમ કરો અથવા સંશોધિત કરો"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"એપ્લિકેશનને સ્થિતિ બાર અક્ષમ કરવાની અથવા સિસ્ટમ આયકન્સ ઉમેરવા અને દૂર કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"સ્થિતિ બાર થાઓ"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index d3d1e2c..5741d5c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर और पासवर्ड जैसा व्यक्तिगत डेटा शामिल होता है."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"प्रदर्शन आवर्धन नियंत्रित करें"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शन का ज़ूम स्तर और स्थिति निर्धारण नियंत्रित करें."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"हावभाव निष्पादित करें"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"इस सेवा के द्वारा टैप किया जा सकता है, स्वाइप किया जा सकता है, पिंच किया जा सकता है और अन्य हावभाव निष्पादित किए जा सकते हैं."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्थिति बार अक्षम या बदलें"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ऐप्स को स्थिति बार अक्षम करने या सिस्टम आइकन को जोड़ने या निकालने देता है."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"स्थिति बार होने दें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index dc55c57..f635b15 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -257,6 +257,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Uključuje osobne podatke kao što su brojevi kreditnih kartica i zaporke."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrola uvećanja zaslona"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolira razinu zumiranja i položaj zaslona."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Izvođenje pokreta"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Može dodirnuti, prijeći prstom, spojiti prste i izvoditi druge pokrete."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"onemogućavanje ili izmjena trake statusa"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Aplikaciji omogućuje onemogućavanje trake statusa ili dodavanje i uklanjanje sistemskih ikona."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"biti traka statusa"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3176c65..64a674c 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Beleértve a személyes adatokat, például a hitelkártyaszámokat és jelszavakat."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"A kijelző nagyításának vezérlése"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"A kijelző nagyítási/kicsinyítési szintjének és pozíciójának vezérlése"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Kézmozdulatok végrehajtása"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Koppintás, ujjak gyors csúsztatása és összehúzása, illetve egyéb kézmozdulatok végrehajtása."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"állapotsor kikapcsolása vagy módosítása"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Lehetővé teszi az alkalmazás számára az állapotsor kikapcsolását, illetve rendszerikonok hozzáadását és eltávolítását."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"az állapotsor szerepének átvétele"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index d72ee8d..8d6272a 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ներառում է անձնական տվյալներ, ինչպիսիք են վարկային քարտերի համարները և գաղտնաբառերը:"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ցուցասարքի խոշորացման կառավարում"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ցուցասարքի մասշտաբավորման և դիրքավորման կառավարում:"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Կատարել ժեստեր"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Կարող է հպել, թերթել, պտղունցել և կատարել այլ ժեստեր:"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"անջատել կամ փոփոխել կարգավիճակի գոտին"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Թույլ է տալիս հավելվածին անջատել կարգավիճակի գոտին կամ ավելացնել ու հեռացնել համակարգի պատկերակները:"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"լինել կարգավիճակի գոտի"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 024eea4..898f6d1 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Meliputi data pribadi seperti nomor kartu kredit dan sandi."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Mengontrol perbesaran layar"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Mengontrol tingkat zoom dan pemosisian layar."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Melakukan isyarat"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Dapat mengetuk, menggesek, mencubit, dan melakukan isyarat lainnya."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"nonaktifkan atau ubah bilah status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Mengizinkan apl menonaktifkan bilah status atau menambah dan menghapus ikon sistem."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"jadikan bilah status"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 240c715..ebcde5d 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Felur í sér persónuleg gögn á borð við kreditkortanúmer og aðgangsorð."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Stilla skjástærð"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Stjórnaðu aðdrætti og afstöðu skjásins."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Nota bendingar"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Getur ýtt, strokið, fært fingur saman og gert ýmsar aðrar bendingar."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"slökkva á eða breyta stöðustiku"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Leyfir forriti að slökkva á stöðustikunni eða bæta við og fjarlægja kerfistákn."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vera stöðustikan"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ece503d..d064684 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sono inclusi dati personali come numeri di carte di credito e password."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlla l\'ingrandimento del display"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlla il livello di zoom e la posizione del display."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Esegui gesti"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Consente di toccare, far scorrere, pizzicare ed eseguire altri gesti."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"disattivare o modificare la barra di stato"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Consente all\'applicazione di disattivare la barra di stato o di aggiungere e rimuovere icone di sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ruolo di barra di stato"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 144afc0..375bdf0 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -258,6 +258,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"כולל נתונים אישיים כמו מספרי כרטיס אשראי וסיסמאות."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"שליטה בהגדלת התצוגה"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"קבע את המרחק מהתצוגה ואת מיקום התצוגה."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ביצוע תנועות"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"יכול להקיש, להחליק, לעשות תנועת צביטה ולבצע תנועות אחרות."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"השבת או שנה את שורת המצב"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"מאפשר לאפליקציה להשבית את שורת המצב או להוסיף ולהסיר סמלי מערכת."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"להיות שורת הסטטוס"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index edfab22..a32a8ed 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"クレジットカードの番号やパスワードなどの個人データが含まれます。"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"画面の拡大の制御"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"画面のズームレベルと位置を制御します。"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"操作の実行"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"タップ、スワイプ、ピンチ、その他の操作を行えます。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ステータスバーの無効化や変更"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ステータスバーの無効化、システムアイコンの追加や削除をアプリに許可します。"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ステータスバーへの表示"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index ffdd31c..f2fc953 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"შეიცავს ისეთ პირად მონაცემებს, როგორიცაა საკრედიტო ბარათის ნომრები და პაროლები."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ერანის გადიდების მართვა"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ეკრანის მასშტაბირების დონისა და პოზიციის მართვა."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ჟესტების შესრულება"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"შეუძლია შეხება, გადაფურცვლა, მასშტაბირება და სხვა ჟესტების შესრულება."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"სტატუსის ზოლის გათიშვა ან ცვლილება"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"აპს შეეძლება სტატუსების ზოლის გათიშვა და სისტემის ხატულების დამატება/წაშლა."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"სტატუსის ზოლის ჩანაცვლება"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index ff58eaa..ab82fd4 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредит карта нөмірі және кілтсөздер сияқты жеке деректерді қоса."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Дисплей ұлғайтуды басқару"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Дисплейдің масштабтау деңгейін және орналастыруды басқару."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Қимылдарды орындау"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Түртуге, жанауға, қысуға және басқа қимылдарды орындауға болады."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"күйін көрсету тақтасын өшіру немесе өзгерту"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Қолданбаға күй жолағын өшіруге немесе жүйелік белгішелерді қосуға және жоюға рұқсат береді."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"күй жолағы болу"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 261212f..ffb3599 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"រួមបញ្ចូលទិន្នន័យផ្ទាល់ខ្លួន ដូចជាលេខកាតឥណទាន និងពាក្យសម្ងាត់។"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"គ្រប់គ្រងការពង្រីកអេក្រង់"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"គ្រប់គ្រងការកំណត់ទីតាំង និងកម្រិតពង្រីករបស់អេក្រង់"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ធ្វើកាយវិការ"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"អាចប៉ះ អូស ច្បិច និងធ្វើកាយវិការផ្សេងទៀត"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"បិទ ឬកែរបារស្ថានភាព"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ឲ្យកម្មវិធីបិទរបារស្ថានភាព ឬបន្ថែម និងលុបរូបតំណាងប្រព័ន្ធ។"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ធ្វើជារបារស្ថានភាព"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index f083702..ab7ad82 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಗಳು ಮತ್ತು ಪಾಸ್ವರ್ಡ್ಗಳಂತಹ ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ಪ್ರದರ್ಶನದ ವರ್ಧಕವನ್ನು ನಿಯಂತ್ರಿಸಿ"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ಪ್ರದರ್ಶನದ ಝೂಮ್ ಮಟ್ಟ ಮತ್ತು ಸ್ಥಾನ ನಿರ್ಧಾರವನ್ನು ನಿಯಂತ್ರಿಸಿ."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ಗೆಸ್ಚರ್ಗಳನ್ನು ಮಾಡಿ"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ಟ್ಯಾಪ್ ಮಾಡಬಹುದು, ಸ್ವೈಪ್ ಮಾಡಬಹುದು, ಪಿಂಚ್ ಮಾಡಬಹುದು ಮತ್ತು ಇತರ ಗೆಸ್ಚರ್ಗಳನ್ನು ಮಾಡಬಹುದು."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ ಇಲ್ಲವೇ ಮಾರ್ಪಡಿಸಿ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಅಥವಾ ಸೇರಿಸಲು ಮತ್ತು ಸಿಸ್ಟಂ ಐಕಾನ್ಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ಸ್ಥಿತಿ ಪಟ್ಟಿಯಾಗಿರಲು"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c8f08c7..4c478aa 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"신용카드 번호와 비밀번호 등의 개인 데이터를 포함합니다."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"디스플레이 배율 제어"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"디스플레이의 확대/축소 수준 및 위치를 제어합니다."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"동작 실행"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"탭, 스와이프, 확대/축소 및 기타 동작을 실행할 수 있습니다."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"상태 표시줄 사용 중지 또는 수정"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"앱이 상태 표시줄을 사용중지하거나 시스템 아이콘을 추가 및 제거할 수 있도록 허용합니다."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"상태 표시줄에 위치"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 4d84933..e9b01d1 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредиттик карта номурлары жана сырсөздөр сыяктуу өздүк берилиштерди камтыйт."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Дисплейди чоңойтууну башкаруу"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Экрандагы сүрөттүн өлчөмүн өзгөртүү жана жайгаштыруу."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Жаңсоолорду аткаруу"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Таптап, серпип, чымчып жана башка жаңсоолорду аткара алат."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"абал тилкесин өчүрүү же өзгөртүү"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Колдонмого абал тилкесин өчүрүү же тутум сүрөтчөлөрүн кошуу же алып салуу мүмкүнчүлүгүн берет."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"абал тилкесинин милдетин аткаруу"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 12d67bb..cf21944 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ຮວມທັງຂໍ້ມູນສ່ວນໂຕເຊັ່ນ: ເລກບັດເຄຣດິດ ແລະລະຫັດຜ່ານ."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ຄວບຄຸມການຂະຫຍາຍຈໍສະແດງຜົນ"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ຄວບຄຸມລະດັບການຊູມ ແລະການວາງຕຳແໜ່ງຂອງຈໍສະແດງຜົນ."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ດຳເນີນທ່າທາງຕ່າງໆ"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ສາມາດແຕະ, ປັດນີ້ວມື, ຢິບນິ້ວມື ແລະ ດຳເນີນທ່າທາງອື່ນ."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ປິດການນນຳໃຊ້ ຫຼື ແກ້ໄຂແຖບສະຖານະ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ອະນຸຍາດໃຫ້ແອັບຯປິດການເຮັດວຽກຂອງແຖບສະຖານະ ຫຼືເພີ່ມ ແລະລຶບໄອຄອນລະບົບອອກໄດ້."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ເປັນແຖບສະຖານະ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e35edd6..f1143fe 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -258,6 +258,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Įtraukiami asmeniniai duomenys, pavyzdžiui, kredito kortelių numeriai ir slaptažodžiai."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekrano didinimo valdymas"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Valdykite ekrano mastelio keitimo lygį ir pozicijos nustatymą."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Veiksmai gestais"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Galima paliesti, perbraukti, suimti ir atlikti kitus veiksmus gestais."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"išjungti ar keisti būsenos juostą"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Leidžiama programai neleisti būsenos juostos arba pridėti ir pašalinti sistemos piktogramas."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"būti būsenos juosta"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index de02a4f..f864400 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -257,6 +257,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Ietver personas datus, piemēram, kredītkartes numurus un paroles."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Displeja palielinājuma kontrole"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolējiet displeja tālummaiņas līmeni un pozicionēšanu."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Žestu izpilde"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Atbalsta pieskaršanos, vilkšanu, savilkšanu un citus žestus."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"atspējot vai pārveidot statusa joslu"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ļauj lietotnei atspējot statusa joslu vai pievienot un noņemt sistēmas ikonas."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"Būt par statusa joslu"</string>
diff --git a/core/res/res/values-mcc310-mnc160/strings.xml b/core/res/res/values-mcc310-mnc160/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc160/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc200/strings.xml b/core/res/res/values-mcc310-mnc200/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc200/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc210/strings.xml b/core/res/res/values-mcc310-mnc210/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc210/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc220/strings.xml b/core/res/res/values-mcc310-mnc220/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc220/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc230/strings.xml b/core/res/res/values-mcc310-mnc230/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc230/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc240/strings.xml b/core/res/res/values-mcc310-mnc240/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc240/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc250/strings.xml b/core/res/res/values-mcc310-mnc250/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc250/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc270/strings.xml b/core/res/res/values-mcc310-mnc270/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc270/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc310/strings.xml b/core/res/res/values-mcc310-mnc310/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc310/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc490/strings.xml b/core/res/res/values-mcc310-mnc490/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc490/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc660/strings.xml b/core/res/res/values-mcc310-mnc660/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc660/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc800/strings.xml b/core/res/res/values-mcc310-mnc800/strings.xml
new file mode 100644
index 0000000..526d08b
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc800/strings.xml
@@ -0,0 +1,38 @@
+<?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 my obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- WFC Operator Error Codes -->
+ <string-array name="wfcOperatorErrorCodes" translatable="false">
+ <item>REG09</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as alerts -->
+ <string-array name="wfcOperatorErrorAlertMessages">
+ <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
+ </string-array>
+ <!-- WFC Operator Error Messages showed as notifications -->
+ <string-array name="wfcOperatorErrorNotificationMessages">
+ <item>Register with your carrier</item>
+ </string-array>
+ <!-- Template for showing cellular network operator name while WFC is active -->
+ <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
+</resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 96a29a5..484e5a6 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Опфаќа лични податоци како што се броеви на кредитни картички и лозинки."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Контролирајте го зголемувањето на екранот"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Контролирајте го нивото на зумирање и позиционирање на екранот."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Користете движења"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Може да допрете, повлечете, штипнете и да користите други движења."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"оневозможи или измени статусна лента"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дозволува апликацијата да ја оневозможи статусната лента или да додава или отстранува системски икони."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"да стане статусна лента"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index f6869e3..42a9a82 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ക്രെഡിറ്റ് കാർഡ് നമ്പറുകളും പാസ്വേഡുകളും പോലുള്ള വ്യക്തിഗത ഡാറ്റ ഉൾപ്പെടുന്നു."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ഡിസ്പ്ലേ മാഗ്നിഫിക്കേഷൻ നിയന്ത്രിക്കുക"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ഡിസ്പ്ലേയുടെ സൂം നിലയും പൊസിഷനിംഗും നിയന്ത്രിക്കുക."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ജെസ്റ്ററുകൾ നിർവഹിക്കുക"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ടാപ്പുചെയ്യാനോ സ്വൈപ്പുചെയ്യാനോ പിഞ്ചുചെയ്യാനോ മറ്റ് ജെസ്റ്ററുകൾ നിർവഹിക്കാനോ കഴിയും."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"സ്റ്റാറ്റസ് ബാർ പ്രവർത്തനരഹിതമാക്കുക അല്ലെങ്കിൽ പരിഷ്ക്കരിക്കുക"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"നില ബാർ പ്രവർത്തരഹിതമാക്കുന്നതിന് അല്ലെങ്കിൽ സിസ്റ്റം ഐക്കണുകൾ ചേർക്കുന്നതിനും നീക്കംചെയ്യുന്നതിനും അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"സ്റ്റാറ്റസ് ബാർ ആയിരിക്കുക"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index d982ece..91b9ea7 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Кредит картын дугаар болон нууц үг зэрэг хувийн датаг агуулж байна."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Дэлгэцийн өсгөлтийг хянах"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Дэлгэцийн томруулах түвшин болон байршлыг хянах."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Зангах"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Товших, шудрах, жижигрүүлэх болон бусад зангааг хийх боломжтой."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"статус самбарыг идэвхгүй болгох болон өөрчлөх"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Апп нь статус самбарыг идэвхгүй болгох эсвэл систем дүрсийг нэмэх, хасах боломжтой."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"статусын хэсэг болох"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 4087199..0cb37da 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"क्रेडिट कार्ड नंबर आणि संकेतशब्द यासारखा वैयक्तिक डेटा समाविष्ट करते."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"प्रदर्शन विस्तृतीकरण नियंत्रित करा"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शनाचा झूम स्तर आणि स्थिती निर्धारण नियंत्रित करा."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"जेश्चर करा"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"टॅप, स्वाइप, पिंच आणि इतर जेश्चर करू शकते."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्टेटस बार अक्षम करा किंवा सुधारित करा"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"स्टेटस बार अक्षम करण्यासाठी किंवा सिस्टीम चिन्हे जोडण्यासाठी आणि काढण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"स्टेटस बार होऊ द्या"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 0923e91..d40c7d1 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Termasuk data peribadi seperti nombor kad kredit dan kata laluan."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kawal pembesaran paparan"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kawal tahap zum dan kedudukan paparan."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Lakukan gerak isyarat"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Boleh ketik, leret, cubit dan laksanakan gerak isyarat lain."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"lumpuhkan atau ubah suai bar status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Membenarkan apl melumpuhkan bar status atau menambah dan mengalih keluar ikon sistem."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"jadi bar status"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 3ef7e6c..28a4d0f 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"အရေးကြီးသော ကိုယ်ရေးအချက်အလက်များဖြစ်တဲ့ ခရက်ဒစ်ကဒ်နံပါတ်များနှင့် စကားဝှက်များ ပါဝင်ပါတယ်."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"မျက်နှာပြင် ချဲ့ခြင်းကို ထိန်းချုပ်ပါ"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"မျက်နှာပြင် ချဲ့ခြင်းနှင့် နေရာချထားခြင်းကို ထိန်းချုပ်ပါ"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"လက်ဟန်များ အသုံးပြုပါ"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"တို့ခြင်း၊ ပွတ်ဆွဲခြင်း၊ နှင့် အခြား လက်ဟန်များကို အသုံးပြုနိုင်ပါသည်။"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"အခြေအနေပြဘားအား အလုပ်မလုပ်ခိုင်းရန်သို့မဟုတ် မွမ်းမံရန်"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"appအား အခြေအနေပြ ဘားကို ပိတ်ခွင့် သို့မဟတ် စနစ် အိုင်ကွန်များကို ထည့်ခြင်း ဖယ်ရှားခြင်း ပြုလုပ်ခွင့် ပြုသည်။"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"အခြေအနေပြ ဘားဖြစ်ပါစေ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index cd65d85..7e909e4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Dette omfatter personlige data, som kredittkortnumre og passord."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrollér forstørrelse for skjermen"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrollér zoomenivået og plasseringen for skjermen."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Gjøre bevegelser"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan trykke, sveipe, klype og gjøre andre bevegelser."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktivere eller endre statusfeltet"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Lar appen deaktivere statusfeltet eller legge til og fjerne systemikoner."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vise appen i statusfeltet"</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 301094b..15277a2 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"व्यक्तिगत डेटा जस्तै क्रेडिट कार्ड नम्बरहरू र पासवर्डहरू समावेश गर्दछ।"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"प्रदर्शन आवर्धन नियन्त्रण गर्नुहोस्"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"प्रदर्शनको जुम स्तर र स्थिति नियन्त्रण गर्नुहोस्।"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"इसाराहरू सम्बन्धी कार्य गर्नुहोस्"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ट्याप, स्वाइप गर्न, थिच्न र अन्य इसाराहरू सम्बन्धी कार्य गर्न सकिन्छ"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"स्थिति पट्टिलाई अक्षम वा संशोधित गर्नुहोस्"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"स्थिति पट्टि असक्षम पार्न वा प्रणाली आइकनहरू थप्न र हटाउन अनुप्रयोगलाई अनुमति दिन्छ।"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"वस्तुस्थिति पट्टी हुन दिनुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 1407991..b81b46e 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Omvat persoonlijke gegevens zoals creditcardnummers en wachtwoorden."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Schermvergroting bedienen"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Bedien het zoomniveau en de positionering van het scherm."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Gebaren uitvoeren"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan tikken, vegen, samenknijpen en andere gebaren uitvoeren."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"statusbalk uitschakelen of wijzigen"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Hiermee kan de app de statusbalk uitschakelen of systeempictogrammen toevoegen en verwijderen."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"de statusbalk zijn"</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 39f20f5..192c181 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ਇਸ ਵਿੱਚ ਨਿੱਜੀ ਡਾਟਾ ਸ਼ਾਮਲ ਹੈ ਜਿਵੇਂ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੰਬਰ ਅਤੇ ਪਾਸਵਰਡ।"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ਡਿਸਪਲੇ ਵੱਡਦਰਸ਼ੀ ਨੂੰ ਨਿਯੰਤ੍ਰਿਤ ਕਰੋ"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ਡਿਸਪਲੇ ਦੇ ਜ਼ੂਮ ਪੱਧਰ ਅਤੇ ਸਥਿਤੀ ਨੂੰ ਨਿਯੰਤ੍ਰਿਤ ਕਰੋ।"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ਸੰਕੇਤ ਕਰਦੀ ਹੈ"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"ਟੈਪ ਕਰ ਸਕਦੀ ਹੈ, ਸਵਾਈਪ ਕਰ ਸਕਦੀ ਹੈ, ਪਿੰਚ ਕਰ ਸਕਦੀ ਹੈ, ਅਤੇ ਹੋਰ ਸੰਕੇਤ ਕਰ ਸਕਦੀ ਹੈ।"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ਸਥਿਤੀ ਬਾਰ ਅਸਮਰੱਥ ਬਣਾਓ ਜਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰੋ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ਐਪ ਨੂੰ ਸਥਿਤੀ ਬਾਰ ਨੂੰ ਅਸਮਰੱਥ ਬਣਾਉਣ ਜਾਂ ਸਿਸਟਮ ਆਈਕਨਾਂ ਨੂੰ ਜੋੜਨ ਅਤੇ ਹਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ਸਥਿਤੀ ਪੱਟੀ ਬਣਨ ਦਿਓ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index f3e50b5..38437fa 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -258,6 +258,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Obejmuje informacje osobiste, takie jak numery kart kredytowych i hasła."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Regulowanie powiększenia ekranu"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Regulowanie poziomu i obszaru powiększenia ekranu."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Obsługa gestów"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Obsługuje kliknięcia, przesunięcia, ściągnięcia palców i inne gesty."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"wyłączanie lub zmienianie paska stanu"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Pozwala aplikacji na wyłączanie paska stanu oraz dodawanie i usuwanie ikon systemowych."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"działanie jako pasek stanu"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 89bbbe8..91821a4 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartão de crédito e senhas."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar ampliação da tela"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o posicionamento e nível de zoom da tela."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Fazer gestos"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Toque, deslize, faça gestos de pinça e faça outros gestos."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ser a barra de status"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index f559dbb..f37d3a6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartões de crédito e palavras-passe."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar a ampliação do ecrã"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o nível de zoom e o posicionamento do ecrã."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Realizar gestos"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"É possível tocar, deslizar rapidamente, juntar os dedos e realizar outros gestos"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar barra de estado"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite à aplicação desativar a barra de estado ou adicionar e remover ícones do sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ser apresentada na barra de estado"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 89bbbe8..91821a4 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inclui dados pessoais, como números de cartão de crédito e senhas."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlar ampliação da tela"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlar o posicionamento e nível de zoom da tela."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Fazer gestos"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Toque, deslize, faça gestos de pinça e faça outros gestos."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite que o app desative a barra de status ou adicione e remova ícones do sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"ser a barra de status"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 4086e69..b235b7a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -257,6 +257,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Include date personale, cum ar fi numere ale cardurilor de credit sau parole."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Controlați mărirea pe afișaj"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Controlați nivelul de zoom și poziționarea afișajului."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Folosiți gesturi"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Se poate atinge, glisa, ciupi și se pot folosi alte gesturi."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"dezactivare sau modificare bare de stare"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Permite aplicației să dezactiveze bara de stare sau să adauge și să elimine pictograme de sistem."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"să fie bara de stare"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index ee39b83..0c5fb09b 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -258,6 +258,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"В том числе личные данные, например номера кредитных карт и пароли."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Управлять масштабом изображения"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Управлять позиционированием и размером изображения на экране."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Жесты"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Может выполнять жесты нажатия, пролистывания, масштабирования и т. д."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"Отключение/изменение строки состояния"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Приложение сможет отключать строку состояния, а также добавлять и удалять системные значки."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"Замена строки состояния"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 5364c98..17f0f97 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ණයවරපත් අංක සහ මුරපද වැනි පුද්ගලික දත්ත ඇතුළත් වේ."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"සංදර්ශන විශාලන මට්ටම පාලනය කිරීම"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"සංදර්ශනයේ විශාලන මට්ටම සහ පිහිටීම පාලනය කිරීම."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"අභින සිදු කරන්න"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"තට්ටු කිරීමට, ස්වයිප් කිරීමට, පින්ච් කිරීමට, සහ වෙනත් අභින සිදු කිරීමට හැකිය."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"තත්ව තීරුව අබල කරන්න හෝ වෙනස් කරන්න"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"තත්ව තීරුව අක්රිය කිරීමට හෝ පද්ධති නිරූපක එකතු හෝ ඉවත් කිරීමට යෙදුමට අවසර දේ."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"තත්ත්ව තීරුව බවට පත්වීම"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 90ccd54..d9c79f2 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -258,6 +258,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Sledovanie zahŕňa osobné údaje ako sú čísla kreditných kariet a heslá."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ovládanie priblíženia obrazovky"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ovládajte úroveň priblíženia/oddialenia obrazovky a umiestnenie"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Gestá"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Je možné použiť klepnutie, prejdenie, stiahnutie prstami a ďalšie gestá."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zakázanie alebo zmeny stavového riadka"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Umožňuje aplikácii vypnúť stavový riadok alebo pridať a odstrániť systémové ikony."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"vydávanie sa za stavový riadok"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 4e9b1c5..8e293e1 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -258,6 +258,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Vključuje osebne podatke, kot so številke kreditnih kartic in gesla."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Nadziranje povečave prikaza"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Nadziranje stopnje povečave in položaja prikaza."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Izvajanje potez"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Mogoče je izvajanje dotikov, vlečenja, vlečenja prstov skupaj in drugih potez."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"onemogočanje ali spreminjanje vrstice stanja"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Aplikacijam omogoča onemogočenje vrstice stanja ali dodajanje in odstranjevanje ikon sistema."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"postane vrstica stanja"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index be9c709..a24e82f 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Përfshi të dhënat personale si numrat e kartave të kreditit si dhe fjalëkalimet."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrollo zmadhimin e ekranit"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrollo nivelin dhe pozicionimin e zmadhimit të ekranit."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Kryen gjeste"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Mund të trokasë, rrëshqasë, bashkojë gishtat dhe kryejë gjeste të tjera."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"çaktivizo ose modifiko shiritin e statusit"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Lejon aplikacionin të çaktivizojë shiritin e statusit dhe të heqë ikonat e sistemit."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"të bëhet shiriti i statusit"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index a3618ab..c706264 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -257,6 +257,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Обухвата личне податке као што су бројеви кредитних картица и лозинке."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Управљај увећањем приказа"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Управља нивоом зумирања приказа и одређивањем положаја."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Обављање покрета"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Може да додирује, листа, скупља приказ и обавља друге покрете."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"онемогућавање или измена статусне траке"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дозвољава апликацији да онемогући статусну траку или да додаје и уклања системске иконе."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"функционисање као статусна трака"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index b07dc28..ecc6d4e 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Omfattar personuppgifter som kreditkortsnummer och lösenord."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Styr skärmförstoringen"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Styr skärmens zoomnivå och positionering."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Göra rörelser"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Kan trycka, svepa, nypa och göra andra rörelser."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"inaktivera eller ändra statusfält"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Tillåter att appen inaktiverar statusfältet eller lägger till och tar bort systemikoner."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"visas i statusfältet"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index cf0063c..d1e6d97 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -258,6 +258,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Inajumuisha data binafsi kama vile nambari za kadi ya mkopo na manenosiri."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Dhibiti ukuzaji wa onyesho"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Dhibiti kiwango cha kukuza na nafasi cha onyesho."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Tekeleza ishara"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Unaweza kugonga, kutelezesha kidole, kubana na kutekeleza ishara zingine."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"zima au rekebisha mwambaa hali"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Inaruhusu programu kulemaza upau wa hali au kuongeza na kutoa ikoni za mfumo."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"kuwa sehemu ya arifa"</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index ecefb77..eacc9a9 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"கிரெடிட் கார்டு எண்கள் மற்றும் கடவுச்சொற்கள் போன்ற தனிப்பட்ட தகவலும் உள்ளடங்கும்."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"திரையின் உருப்பெருக்கத்தைக் கட்டுப்படுத்துதல்"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"திரையின் ஜூம் அளவையும் நிலையையும் கட்டுப்படுத்தலாம்."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"சைகைகளைச் செயல்படுத்துதல்"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"தட்டலாம், ஸ்வைப் செய்யலாம், பின்ச் செய்யலாம் மற்றும் பிற சைகைகளைச் செயல்படுத்தலாம்."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"நிலைப் பட்டியை முடக்குதல் அல்லது மாற்றுதல்"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"நிலைப் பட்டியை முடக்க அல்லது முறைமையில் ஐகான்களைச் சேர்க்க மற்றும் அகற்ற பயன்பாட்டை அனுமதிக்கிறது."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"நிலைப் பட்டியில் இருக்கும்"</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index b1bccd2..0440134 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"క్రెడిట్ కార్డు నంబర్లు మరియు పాస్వర్డ్ల వంటి వ్యక్తిగత డేటాను కలిగి ఉంటుంది."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"డిస్ప్లే మాగ్నిఫికేషన్ను నియంత్రించండి"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"డిస్ప్లే జూమ్ స్థాయి మరియు స్థానాన్ని నియంత్రిస్తుంది."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"సంజ్ఞలను చేయడం"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"నొక్కగలరు, స్వైప్ చేయగలరు, స్క్రీన్పై రెండు వేళ్లను ఉంచి ఆ వేళ్లను దగ్గరకు లేదా దూరానికి లాగగలరు మరియు ఇతర సంజ్ఞలను చేయగలరు."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"స్థితి బార్ను నిలిపివేయడం లేదా సవరించడం"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"స్థితి బార్ను నిలిపివేయడానికి లేదా సిస్టమ్ చిహ్నాలను జోడించడానికి మరియు తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"స్థితి పట్టీగా ఉండటం"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 0b8e60d..5d27943 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"รวมถึงข้อมูลส่วนบุคคล เช่น หมายเลขบัตรเครดิตและรหัสผ่าน"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ควบคุมการขยายการแสดงผล"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ควบคุมระดับการซูมและการวางตำแหน่งของการแสดงผล"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"ทำท่าทางสัมผัส"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"สามารถแตะ เลื่อน บีบ และทำท่าทางสัมผัสอื่นๆ"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ปิดการใช้งานหรือแก้ไขแถบสถานะ"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"อนุญาตให้แอปพลิเคชันปิดใช้งานแถบสถานะหรือเพิ่มและนำไอคอนระบบออก"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"เป็นแถบสถานะ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 02a3587..479efdf 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"May kasamang personal na data tulad ng mga numero ng credit card at password."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kontrolin ang pag-magnify ng display"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kontrolin ang antas ng pag-zoom at pagpoposisyon ng display."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Magsagawa ng mga galaw"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"May kakayahang mag-tap, mag-swipe, mag-pinch at magsagawa ng iba pang mga galaw."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"huwag paganahin o baguhin ang status bar"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Pinapayagan ang app na huwag paganahin ang status bar o magdagdag at mag-alis ng mga icon ng system."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"maging status bar"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 283bcebb..b0c6d5a 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kredi kartı ve şifre gibi kişisel bilgiler içerir."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekran büyütecini kontrol et"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ekranın yakınlaştırma seviyesini ve konumunu kontrol edin."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Haraketleri yapma"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Hafifçe dokunabilir, hızlıca kaydırabilir, sıkıştırabilir ve diğer hareketleri yapabilirsiniz."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"durum çubuğunu devre dışı bırak veya değiştir"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Uygulamaya, durum çubuğunu devre dışı bırakma ve sistem simgelerini ekleyip kaldırma izni verir."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"durum çubuğunda olma"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index fce4cc3..af1354a 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -258,6 +258,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Включає особисті дані, як-от номери кредитних карток і паролі."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Контролювати збільшення екрана"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Контролювати масштаб і розташування екрана."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Виконання жестів"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Можна торкатися, проводити пальцем, стискати пальці та виконувати інші жести."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"вимикати чи змін. рядок стану"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Дозволяє програмі вимикати рядок стану чи додавати та видаляти піктограми системи."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"відображатися як рядок стану"</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 519f42b..82bf7ab 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"اس میں ذاتی ڈیٹا جیسے کریڈٹ کارڈ نمبرز اور پاس ورڈز شامل ہیں۔"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"ڈسپلے بڑا کرنے کے عمل کو کنٹرول کریں"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"ڈسپلے کے زوم کی سطح اور پوزیشن کو کنٹرول کریں۔"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"اشارے انجام دیں"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"تھپتھپانا، سوائپ کرنا، چٹکی بھرنا اور دیگر اشارے انجام دے سکتی ہے"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"اسٹیٹس بار کو غیر فعال یا اس میں ترمیم کریں"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"ایپ کو اسٹیٹس بار غیر فعال کرنے یا سسٹم آئیکنز شامل کرنے اور ہٹانے کی اجازت دیتا ہے۔"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"بطور اسٹیٹس بار کام لیں"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index e90d921..c4be0b9 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Bunga kredit karta raqamlari va parollar kabi shaxsiy ma’lumotlar kiradi."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Ekranni kattalashtirishni boshqarish"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Ekranni kattalashtirish darajasi va joylashuvini boshqaradi."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Imo-ishoralar bilan boshqarish"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Bosish, surish; jipslashtirish va boshqa imo-ishoralarni amalga oshirish mumkin."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"holat panelini o‘zgartirish yoki o‘chirish"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ilova holat panelini o‘chirib qo‘yishi hamda tizim ikonkalarini qo‘shishi yoki olib tashlashi mumkin."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"holat qatorida ko‘rinishi"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 31e090b..69b0b99 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Bao gồm dữ liệu cá nhân chẳng hạn như số thẻ tín dụng và mật khẩu."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Kiểm soát thu phóng màn hình"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Kiểm soát vị trí và mức thu phóng của màn hình."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Thực hiện cử chỉ"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Có thể nhấn, vuốt, chụm và thực hiện các cử chỉ khác."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"vô hiệu hóa hoặc sửa đổi thanh trạng thái"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Cho phép ứng dụng vô hiệu hóa thanh trạng thái hoặc thêm và xóa biểu tượng hệ thống."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"trở thành thanh trạng thái"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index c5cd59a..4c7bfd3 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包含个人数据,例如信用卡号和密码。"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"控制显示内容放大功能"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"控制显示内容的缩放级别和位置。"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"执行手势"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"可执行点按、滑动、双指张合等手势。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改状态栏"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"允许应用停用状态栏或者增删系统图标。"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"用作状态栏"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 36ba2ad6..d6cacb7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包括個人資料,如信用卡號碼和密碼。"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"控制顯示屏的放大功能"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"控制顯示屏的縮放程度和位置。"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"執行手勢"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"可以輕按、快速滑動和兩指縮放,並執行其他手勢。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或修改狀態列"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"允許應用程式停用狀態列,並可新增或移除系統圖示。"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"成為狀態列"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 72ea523..a47f73d 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"包括個人資料,如信用卡號碼和密碼。"</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"控管顯示畫面放大功能"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"控管顯示畫面的縮放等級和位置。"</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"使用手勢"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"可使用輕按、滑動和雙指撥動等手勢。"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"允許應用程式停用狀態列,並可新增或移除系統圖示。"</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"以狀態列顯示"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index bee7eea..b34594f 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -256,6 +256,8 @@
<string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"Kufaka phakathi idatha yomuntu siqu efana nezinombolo zekhadi lesikweletu namaphasiwedi."</string>
<string name="capability_title_canControlMagnification" msgid="3593493281059424855">"Lawula ukulungiswa kwesibonisi"</string>
<string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"Lawula ileveli yokusondeza yesibonisi nendawo."</string>
+ <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"Yenza ukuthinta"</string>
+ <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"Ingathepha, iswayiphe, incinze, futhi yenze okunye ukuthintwa."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"khubaza noma guqula ibha yomumo"</string>
<string name="permdesc_statusBar" msgid="8434669549504290975">"Ivumela uhlelo lokusebenza ukuthi yenze umudwa ochaza ngesimo ukuthi ungasebenzi noma ukufaka noma ukukhipha izithonjana zohlelo."</string>
<string name="permlab_statusBarService" msgid="4826835508226139688">"yiba yibha yesimo"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 58a77e8..6eba78a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1748,6 +1748,10 @@
<attr name="isolatedProcess" format="boolean" />
<attr name="singleUser" />
<attr name="encryptionAware" />
+ <!-- If the service is an {@link android.R.attr#isolatedProcess} service, this permits a
+ client to bind to the service as if it were running it its own package. The service
+ must also be {@link android.R.attr#exported} if this flag is set. -->
+ <attr name="externalService" format="boolean" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 58c4046..61da1e4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -413,22 +413,29 @@
<!-- Boolean indicating whether or not wifi firmware debugging is enabled -->
<bool translatable="false" name="config_wifi_enable_wifi_firmware_debugging">true</bool>
+ <!-- Boolean indicating whether or not wifi should turn off when emergency call is made -->
+ <bool translatable="false" name="config_wifi_turn_off_during_emergency_call">false</bool>
+
<!-- Integer specifying the basic autojoin parameters -->
<integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_threshold">-65</integer>
- <integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_factor">5</integer>
+ <integer translatable="false" name="config_wifi_framework_5GHz_preference_boost_factor">40</integer>
<integer translatable="false" name="config_wifi_framework_current_association_hysteresis_high">16</integer>
<integer translatable="false" name="config_wifi_framework_current_association_hysteresis_low">10</integer>
<integer translatable="false" name="config_wifi_framework_5GHz_preference_penalty_threshold">-75</integer>
- <integer translatable="false" name="config_wifi_framework_5GHz_preference_penalty_factor">2</integer>
-
+ <integer translatable="false" name="config_wifi_framework_RSSI_SCORE_OFFSET">85</integer>
+ <integer translatable="false" name="config_wifi_framework_RSSI_SCORE_SLOPE">4</integer>
+ <integer translatable="false" name="config_wifi_framework_SAME_BSSID_AWARD">24</integer>
+ <integer translatable="false" name="config_wifi_framework_LAST_SELECTION_AWARD">480</integer>
+ <integer translatable="false" name="config_wifi_framework_PASSPOINT_SECURITY_AWARD">40</integer>
+ <integer translatable="false" name="config_wifi_framework_SECURITY_AWARD">80</integer>
<!-- Integer parameters of the wifi to cellular handover feature
wifi should not stick to bad networks -->
<integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz">-82</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_5GHz">-72</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_5GHz">-60</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz">-87</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz">-77</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz">-65</integer>
+ <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_5GHz">-70</integer>
+ <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_5GHz">-57</integer>
+ <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz">-85</integer>
+ <integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz">-73</integer>
+ <integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz">-60</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_bad_link_speed_24">6</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_bad_link_speed_5">12</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_good_link_speed_24">24</integer>
@@ -502,7 +509,7 @@
<integer translatable="false" name="config_wifi_framework_network_black_list_min_time_milli">120000</integer>
<!-- Integer indicating RSSI boost given to current network -->
- <integer translatable="false" name="config_wifi_framework_current_network_boost">25</integer>
+ <integer translatable="false" name="config_wifi_framework_current_network_boost">16</integer>
<!-- Integer indicating how to handle beacons with uninitialized RSSI value of 0 -->
<integer translatable="false" name="config_wifi_framework_scan_result_rssi_level_patchup_value">-85</integer>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index acea461..c883b1f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2689,6 +2689,7 @@
<public type="attr" name="tickMarkTint" />
<public type="attr" name="tickMarkTintMode" />
<public type="attr" name="canPerformGestures" />
+ <public type="attr" name="externalService" />
<public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
<public type="style" name="Widget.Material.SeekBar.Discrete" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 706dd20..32510397 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -300,6 +300,7 @@
<java-symbol type="bool" name="config_wifi_enable_5GHz_preference" />
<java-symbol type="bool" name="config_wifi_revert_country_code_on_cellular_loss" />
<java-symbol type="bool" name="config_wifi_enable_wifi_firmware_debugging" />
+ <java-symbol type="bool" name="config_wifi_turn_off_during_emergency_call" />
<java-symbol type="bool" name="config_supportMicNearUltrasound" />
<java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
<java-symbol type="bool" name="config_freeformWindowManagement" />
@@ -310,7 +311,12 @@
<java-symbol type="integer" name="config_wifi_framework_current_association_hysteresis_high" />
<java-symbol type="integer" name="config_wifi_framework_current_association_hysteresis_low" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
- <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_factor" />
+ <java-symbol type="integer" name="config_wifi_framework_RSSI_SCORE_OFFSET" />
+ <java-symbol type="integer" name="config_wifi_framework_RSSI_SCORE_SLOPE" />
+ <java-symbol type="integer" name="config_wifi_framework_SAME_BSSID_AWARD" />
+ <java-symbol type="integer" name="config_wifi_framework_LAST_SELECTION_AWARD" />
+ <java-symbol type="integer" name="config_wifi_framework_PASSPOINT_SECURITY_AWARD" />
+ <java-symbol type="integer" name="config_wifi_framework_SECURITY_AWARD" />
<java-symbol type="integer" name="config_wifi_disconnected_short_scan_interval" />
<java-symbol type="integer" name="config_wifi_disconnected_long_scan_interval" />
<java-symbol type="integer" name="config_wifi_associated_short_scan_interval" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 5f1f927..77d2ada 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -33,6 +33,9 @@
and http://mobilcent.com/info-worldwide.asp and extracted from:
http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
+ <!-- Arab Emirates -->
+ <shortcode country="ae" free="3214|1017" />
+
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -43,7 +46,7 @@
<shortcode country="at" pattern="11\\d{4}" premium="09.*" free="116\\d{3}" />
<!-- Australia: 6 or 8 digits starting with "19" -->
- <shortcode country="au" pattern="19(?:\\d{4}|\\d{6})" premium="19998882" />
+ <shortcode country="au" pattern="19(?:\\d{4}|\\d{6})" premium="19998882|19944444" />
<!-- Azerbaijan: 4-5 digits, known premium codes listed -->
<shortcode country="az" pattern="\\d{4,5}" premium="330[12]|87744|901[234]|93(?:94|101)|9426|9525" />
@@ -58,10 +61,10 @@
<shortcode country="by" pattern="\\d{4}" premium="3336|4161|444[4689]|501[34]|7781" />
<!-- Canada: 5-6 digits -->
- <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188" />
+ <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" />
<!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
- <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111" />
+ <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" />
<!-- China: premium shortcodes start with "1066", free shortcodes start with "1065":
http://clients.txtnation.com/entries/197192-china-premium-sms-short-code-requirements -->
@@ -75,7 +78,7 @@
<shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
<!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
- <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215" />
+ <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529" />
<!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
<shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}" />
@@ -89,7 +92,7 @@
<shortcode country="es" premium="[23][57]\\d{3}|280\\d{2}|[79]9[57]\\d{3}" free="116\\d{3}" />
<!-- Finland: 5-6 digits, premium 0600, 0700: http://en.wikipedia.org/wiki/Telephone_numbers_in_Finland -->
- <shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}" />
+ <shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}|14789" />
<!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU:
http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements,
@@ -122,9 +125,15 @@
http://clients.txtnation.com/attachments/token/di5kfblvubttvlw/?name=Italy_CASP_EN.pdf -->
<shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}" />
+ <!-- Japan: 8083 used by SOFTBANK_DCB_2 -->
+ <shortcode country="jp" free="8083" />
+
<!-- Kyrgyzstan: 4 digits, known premium codes listed -->
<shortcode country="kg" pattern="\\d{4}" premium="415[2367]|444[69]" />
+ <!-- Korea: http://www.smsideatechnosolutions.com/chhattisgarh/korea/sms-short-code.html -->
+ <shortcode country="kr" pattern="\\d{4,7}" free="\\*9712|\\*9090|##900" />
+
<!-- Kazakhstan: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-kazakhstan/ -->
<shortcode country="kz" pattern="\\d{4}" premium="335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]" />
@@ -151,27 +160,33 @@
<shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" />
<!-- New Zealand: 3-4 digits, known premium codes listed -->
- <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995" />
+ <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" />
+
+ <!-- Philippines -->
+ <shortcode country="ph" free="2147" />
<!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
- <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}" />
+ <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}|8012" />
<!-- Portugal: 5 digits, plus EU:
http://clients.txtnation.com/entries/158326-portugal-premium-sms-short-code-regulations -->
<shortcode country="pt" premium="6[1289]\\d{3}" free="116\\d{3}" />
<!-- Romania: 4 digits, plus EU: http://www.simplus.ro/en/resources/glossary-of-terms/ -->
- <shortcode country="ro" pattern="\\d{4}" premium="12(?:63|66|88)|13(?:14|80)" free="116\\d{3}" />
+ <shortcode country="ro" pattern="\\d{4}" premium="12(?:63|66|88)|13(?:14|80)" free="116\\d{3}|3654" />
<!-- Russia: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-russia/ -->
- <shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)" />
+ <shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)|8424" />
+
+ <!-- Saudi Arabia -->
+ <shortcode country="sa" free="8145" />
<!-- Sweden: 5 digits (72xxx), plus EU: http://www.viatel.se/en/premium-sms/ -->
<shortcode country="se" premium="72\\d{3}" free="116\\d{3}" />
<!-- Singapore: 5 digits: http://clients.txtnation.com/entries/306442-singapore-premium-sms-short-code-requirements
Free government directory info at 74688: http://app.sgdi.gov.sg/sms_help.asp -->
- <shortcode country="sg" pattern="7\\d{4}" premium="73800" standard="74688" />
+ <shortcode country="sg" pattern="7\\d{4}" premium="73800" standard="74688|70134" />
<!-- Slovenia: 4 digits (premium=3xxx, 6xxx, 8xxx), plus EU: http://www.cmtelecom.com/premium-sms/slovenia -->
<shortcode country="si" pattern="\\d{4}" premium="[368]\\d{3}" free="116\\d{3}" />
@@ -179,9 +194,15 @@
<!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
<shortcode country="sk" premium="\\d{4}" free="116\\d{3}" />
+ <!-- Thailand: 4186001 used by AIS_TH_DCB -->
+ <shortcode country="th" free="4186001" />
+
<!-- Tajikistan: 4 digits, known premium codes listed -->
<shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
+ <!-- Turkey -->
+ <shortcode country="tr" free="7529|5528" />
+
<!-- Ukraine: 4 digits, known premium codes listed -->
<shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
new file mode 100644
index 0000000..2e0e6dc
--- /dev/null
+++ b/core/tests/coretests/src/android/text/StaticLayoutTextMeasuringTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * 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.text;
+
+import android.text.Layout.Alignment;
+import junit.framework.TestCase;
+
+/**
+ * Tests for text measuring methods of StaticLayout.
+ */
+public class StaticLayoutTextMeasuringTest extends TestCase {
+ private static final float SPACE_MULTI = 1.0f;
+ private static final float SPACE_ADD = 0.0f;
+ private static final int DEFAULT_OUTER_WIDTH = 150;
+ private static final Alignment DEFAULT_ALIGN = Alignment.ALIGN_LEFT;
+
+ private TextPaint mDefaultPaint;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (mDefaultPaint == null) {
+ mDefaultPaint = new TextPaint();
+ }
+ }
+
+ public void testGetPrimaryHorizontal_zwnbsp() {
+ // a, ZERO WIDTH NO-BREAK SPACE
+ String testString = "a\uFEFF";
+ StaticLayout layout = new StaticLayout(testString, mDefaultPaint,
+ DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
+
+ assertEquals(0.0f, layout.getPrimaryHorizontal(0));
+ assertEquals(layout.getPrimaryHorizontal(2), layout.getPrimaryHorizontal(1));
+ }
+
+ public void testGetPrimaryHorizontal_devanagari() {
+ // DEVANAGARI LETTER KA, DEVANAGARI VOWEL SIGN AA
+ String testString = "\u0915\u093E";
+ StaticLayout layout = new StaticLayout(testString, mDefaultPaint,
+ DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
+
+ assertEquals(0.0f, layout.getPrimaryHorizontal(0));
+ assertEquals(layout.getPrimaryHorizontal(2), layout.getPrimaryHorizontal(1));
+ }
+
+ public void testGetPrimaryHorizontal_flagEmoji() {
+ // REGIONAL INDICATOR SYMBOL LETTER U, REGIONAL INDICATOR SYMBOL LETTER S, REGIONAL
+ // INDICATOR SYMBOL LETTER Z
+ // First two code points (U and S) forms a US flag.
+ String testString = "\uD83C\uDDFA\uD83C\uDDF8\uD83C\uDDFF";
+ StaticLayout layout = new StaticLayout(testString, mDefaultPaint,
+ DEFAULT_OUTER_WIDTH, DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
+
+ assertEquals(0.0f, layout.getPrimaryHorizontal(0));
+ assertEquals(layout.getPrimaryHorizontal(4), layout.getPrimaryHorizontal(1));
+ assertEquals(layout.getPrimaryHorizontal(4), layout.getPrimaryHorizontal(2));
+ assertEquals(layout.getPrimaryHorizontal(4), layout.getPrimaryHorizontal(3));
+
+ assertTrue(layout.getPrimaryHorizontal(6) > layout.getPrimaryHorizontal(4));
+ assertEquals(layout.getPrimaryHorizontal(6), layout.getPrimaryHorizontal(5));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
index 48590c1..1966313 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java
@@ -22,6 +22,7 @@
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -32,6 +33,8 @@
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
+import java.util.List;
+
/**
* Tests {@link PhoneWindow}'s {@link ActionMode} related methods.
*/
@@ -288,6 +291,9 @@
@Override
public void onActionModeFinished(ActionMode mode) {}
+
+ @Override
+ public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {}
}
private static final class MockActionModeCallback implements ActionMode.Callback {
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index b5b1a25..169ef0b 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -25,14 +25,14 @@
import android.util.DisplayMetrics;
import android.util.Log;
-import dalvik.system.VMRuntime;
-
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
+import libcore.util.NativeAllocationRegistry;
+
public final class Bitmap implements Parcelable {
private static final String TAG = "Bitmap";
@@ -44,6 +44,10 @@
*/
public static final int DENSITY_NONE = 0;
+ // Estimated size of the Bitmap native allocation, not including
+ // pixel data.
+ private static final long NATIVE_ALLOCATION_SIZE = 32;
+
/**
* Backing buffer for the Bitmap.
*/
@@ -51,7 +55,6 @@
// Convenience for JNI access
private final long mNativePtr;
- private final BitmapFinalizer mFinalizer;
private final boolean mIsMutable;
@@ -125,9 +128,13 @@
}
mNativePtr = nativeBitmap;
- mFinalizer = new BitmapFinalizer(nativeBitmap);
- int nativeAllocationByteCount = (buffer == null ? getByteCount() : 0);
- mFinalizer.setNativeAllocationByteCount(nativeAllocationByteCount);
+ long nativeSize = NATIVE_ALLOCATION_SIZE;
+ if (buffer == null) {
+ nativeSize += getByteCount();
+ }
+ NativeAllocationRegistry registry = new NativeAllocationRegistry(
+ nativeGetNativeFinalizer(), nativeSize);
+ registry.registerNativeAllocation(this, nativeBitmap);
}
/**
@@ -253,7 +260,7 @@
throw new IllegalStateException("native-backed bitmaps may not be reconfigured");
}
- nativeReconfigure(mFinalizer.mNativeBitmap, width, height, config.nativeInt,
+ nativeReconfigure(mNativePtr, width, height, config.nativeInt,
mBuffer.length, mRequestPremultiplied);
mWidth = width;
mHeight = height;
@@ -330,8 +337,8 @@
* there are no more references to this bitmap.
*/
public void recycle() {
- if (!mRecycled && mFinalizer.mNativeBitmap != 0) {
- if (nativeRecycle(mFinalizer.mNativeBitmap)) {
+ if (!mRecycled && mNativePtr != 0) {
+ if (nativeRecycle(mNativePtr)) {
// return value indicates whether native pixel object was actually recycled.
// false indicates that it is still in use at the native level and these
// objects should not be collected now. They will be collected later when the
@@ -364,7 +371,7 @@
if (mRecycled) {
Log.w(TAG, "Called getGenerationId() on a recycle()'d bitmap! This is undefined behavior!");
}
- return nativeGenerationId(mFinalizer.mNativeBitmap);
+ return nativeGenerationId(mNativePtr);
}
/**
@@ -520,7 +527,7 @@
throw new RuntimeException("Buffer not large enough for pixels");
}
- nativeCopyPixelsToBuffer(mFinalizer.mNativeBitmap, dst);
+ nativeCopyPixelsToBuffer(mNativePtr, dst);
// now update the buffer's position
int position = dst.position();
@@ -560,7 +567,7 @@
throw new RuntimeException("Buffer not large enough for pixels");
}
- nativeCopyPixelsFromBuffer(mFinalizer.mNativeBitmap, src);
+ nativeCopyPixelsFromBuffer(mNativePtr, src);
// now update the buffer's position
int position = src.position();
@@ -582,7 +589,7 @@
*/
public Bitmap copy(Config config, boolean isMutable) {
checkRecycled("Can't copy a recycled bitmap");
- Bitmap b = nativeCopy(mFinalizer.mNativeBitmap, config.nativeInt, isMutable);
+ Bitmap b = nativeCopy(mNativePtr, config.nativeInt, isMutable);
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
b.mDensity = mDensity;
@@ -598,7 +605,7 @@
*/
public Bitmap createAshmemBitmap() {
checkRecycled("Can't copy a recycled bitmap");
- Bitmap b = nativeCopyAshmem(mFinalizer.mNativeBitmap);
+ Bitmap b = nativeCopyAshmem(mNativePtr);
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
b.mDensity = mDensity;
@@ -859,7 +866,7 @@
}
bm.setHasAlpha(hasAlpha);
if (config == Config.ARGB_8888 && !hasAlpha) {
- nativeErase(bm.mFinalizer.mNativeBitmap, 0xff000000);
+ nativeErase(bm.mNativePtr, 0xff000000);
}
// No need to initialize the bitmap to zeroes with other configs;
// it is backed by a VM byte array which is by definition preinitialized
@@ -1049,7 +1056,7 @@
throw new IllegalArgumentException("quality must be 0..100");
}
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
- boolean result = nativeCompress(mFinalizer.mNativeBitmap, format.nativeInt,
+ boolean result = nativeCompress(mNativePtr, format.nativeInt,
quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
return result;
@@ -1093,7 +1100,7 @@
if (mRecycled) {
Log.w(TAG, "Called isPremultiplied() on a recycle()'d bitmap! This is undefined behavior!");
}
- return nativeIsPremultiplied(mFinalizer.mNativeBitmap);
+ return nativeIsPremultiplied(mNativePtr);
}
/**
@@ -1119,7 +1126,7 @@
public final void setPremultiplied(boolean premultiplied) {
checkRecycled("setPremultiplied called on a recycled bitmap");
mRequestPremultiplied = premultiplied;
- nativeSetPremultiplied(mFinalizer.mNativeBitmap, premultiplied);
+ nativeSetPremultiplied(mNativePtr, premultiplied);
}
/** Returns the bitmap's width */
@@ -1220,7 +1227,7 @@
if (mRecycled) {
Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!");
}
- return nativeRowBytes(mFinalizer.mNativeBitmap);
+ return nativeRowBytes(mNativePtr);
}
/**
@@ -1266,7 +1273,7 @@
if (mRecycled) {
Log.w(TAG, "Called getConfig() on a recycle()'d bitmap! This is undefined behavior!");
}
- return Config.nativeToConfig(nativeConfig(mFinalizer.mNativeBitmap));
+ return Config.nativeToConfig(nativeConfig(mNativePtr));
}
/** Returns true if the bitmap's config supports per-pixel alpha, and
@@ -1281,7 +1288,7 @@
if (mRecycled) {
Log.w(TAG, "Called hasAlpha() on a recycle()'d bitmap! This is undefined behavior!");
}
- return nativeHasAlpha(mFinalizer.mNativeBitmap);
+ return nativeHasAlpha(mNativePtr);
}
/**
@@ -1296,7 +1303,7 @@
*/
public void setHasAlpha(boolean hasAlpha) {
checkRecycled("setHasAlpha called on a recycled bitmap");
- nativeSetHasAlpha(mFinalizer.mNativeBitmap, hasAlpha, mRequestPremultiplied);
+ nativeSetHasAlpha(mNativePtr, hasAlpha, mRequestPremultiplied);
}
/**
@@ -1320,7 +1327,7 @@
if (mRecycled) {
Log.w(TAG, "Called hasMipMap() on a recycle()'d bitmap! This is undefined behavior!");
}
- return nativeHasMipMap(mFinalizer.mNativeBitmap);
+ return nativeHasMipMap(mNativePtr);
}
/**
@@ -1345,7 +1352,7 @@
*/
public final void setHasMipMap(boolean hasMipMap) {
checkRecycled("setHasMipMap called on a recycled bitmap");
- nativeSetHasMipMap(mFinalizer.mNativeBitmap, hasMipMap);
+ nativeSetHasMipMap(mNativePtr, hasMipMap);
}
/**
@@ -1358,7 +1365,7 @@
if (!isMutable()) {
throw new IllegalStateException("cannot erase immutable bitmaps");
}
- nativeErase(mFinalizer.mNativeBitmap, c);
+ nativeErase(mNativePtr, c);
}
/**
@@ -1375,7 +1382,7 @@
public int getPixel(int x, int y) {
checkRecycled("Can't call getPixel() on a recycled bitmap");
checkPixelAccess(x, y);
- return nativeGetPixel(mFinalizer.mNativeBitmap, x, y);
+ return nativeGetPixel(mNativePtr, x, y);
}
/**
@@ -1408,7 +1415,7 @@
return; // nothing to do
}
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
- nativeGetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride,
+ nativeGetPixels(mNativePtr, pixels, offset, stride,
x, y, width, height);
}
@@ -1489,7 +1496,7 @@
throw new IllegalStateException();
}
checkPixelAccess(x, y);
- nativeSetPixel(mFinalizer.mNativeBitmap, x, y, color);
+ nativeSetPixel(mNativePtr, x, y, color);
}
/**
@@ -1525,7 +1532,7 @@
return; // nothing to do
}
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
- nativeSetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride,
+ nativeSetPixels(mNativePtr, pixels, offset, stride,
x, y, width, height);
}
@@ -1563,7 +1570,7 @@
*/
public void writeToParcel(Parcel p, int flags) {
checkRecycled("Can't parcel a recycled bitmap");
- if (!nativeWriteToParcel(mFinalizer.mNativeBitmap, mIsMutable, mDensity, p)) {
+ if (!nativeWriteToParcel(mNativePtr, mIsMutable, mDensity, p)) {
throw new RuntimeException("native writeToParcel failed");
}
}
@@ -1609,7 +1616,7 @@
public Bitmap extractAlpha(Paint paint, int[] offsetXY) {
checkRecycled("Can't extractAlpha on a recycled bitmap");
long nativePaint = paint != null ? paint.getNativeInstance() : 0;
- Bitmap bm = nativeExtractAlpha(mFinalizer.mNativeBitmap, nativePaint, offsetXY);
+ Bitmap bm = nativeExtractAlpha(mNativePtr, nativePaint, offsetXY);
if (bm == null) {
throw new RuntimeException("Failed to extractAlpha on Bitmap");
}
@@ -1629,7 +1636,7 @@
if (other.isRecycled()) {
throw new IllegalArgumentException("Can't compare to a recycled bitmap!");
}
- return nativeSameAs(mFinalizer.mNativeBitmap, other.mFinalizer.mNativeBitmap);
+ return nativeSameAs(mNativePtr, other.mNativePtr);
}
/**
@@ -1660,41 +1667,6 @@
return nativeRefPixelRef(mNativePtr);
}
- private static class BitmapFinalizer {
- private long mNativeBitmap;
-
- // Native memory allocated for the duration of the Bitmap,
- // if pixel data allocated into native memory, instead of java byte[]
- private int mNativeAllocationByteCount;
-
- BitmapFinalizer(long nativeBitmap) {
- mNativeBitmap = nativeBitmap;
- }
-
- public void setNativeAllocationByteCount(int nativeByteCount) {
- if (mNativeAllocationByteCount != 0) {
- VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount);
- }
- mNativeAllocationByteCount = nativeByteCount;
- if (mNativeAllocationByteCount != 0) {
- VMRuntime.getRuntime().registerNativeAllocation(mNativeAllocationByteCount);
- }
- }
-
- @Override
- public void finalize() {
- try {
- super.finalize();
- } catch (Throwable t) {
- // Ignore
- } finally {
- setNativeAllocationByteCount(0);
- nativeDestructor(mNativeBitmap);
- mNativeBitmap = 0;
- }
- }
- }
-
//////////// native methods
private static native Bitmap nativeCreate(int[] colors, int offset,
@@ -1703,7 +1675,7 @@
private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig,
boolean isMutable);
private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
- private static native void nativeDestructor(long nativeBitmap);
+ private static native long nativeGetNativeFinalizer();
private static native boolean nativeRecycle(long nativeBitmap);
private static native void nativeReconfigure(long nativeBitmap, int width, int height,
int config, int allocSize,
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 1cc5346..d4f745d 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -31,6 +31,8 @@
import javax.microedition.khronos.opengles.GL;
+import libcore.util.NativeAllocationRegistry;
+
/**
* The Canvas class holds the "draw" calls. To draw something, you need
* 4 basic components: A Bitmap to hold the pixels, a Canvas to host
@@ -50,7 +52,7 @@
/**
* Should only be assigned in constructors (or setBitmap if software canvas),
- * freed in finalizer.
+ * freed by NativeAllocation.
* @hide
*/
protected long mNativeCanvasWrapper;
@@ -85,32 +87,15 @@
// (see SkCanvas.cpp, SkDraw.cpp)
private static final int MAXMIMUM_BITMAP_SIZE = 32766;
+ // The approximate size of the native allocation associated with
+ // a Canvas object.
+ private static final long NATIVE_ALLOCATION_SIZE = 525;
+
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ getNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
+
// This field is used to finalize the native Canvas properly
- private final CanvasFinalizer mFinalizer;
-
- private static final class CanvasFinalizer {
- private long mNativeCanvasWrapper;
-
- public CanvasFinalizer(long nativeCanvas) {
- mNativeCanvasWrapper = nativeCanvas;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- dispose();
- } finally {
- super.finalize();
- }
- }
-
- public void dispose() {
- if (mNativeCanvasWrapper != 0) {
- finalizer(mNativeCanvasWrapper);
- mNativeCanvasWrapper = 0;
- }
- }
- }
+ private Runnable mFinalizer;
/**
* Construct an empty raster canvas. Use setBitmap() to specify a bitmap to
@@ -122,7 +107,7 @@
if (!isHardwareAccelerated()) {
// 0 means no native bitmap
mNativeCanvasWrapper = initRaster(null);
- mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
+ mFinalizer = sRegistry.registerNativeAllocation(this, mNativeCanvasWrapper);
} else {
mFinalizer = null;
}
@@ -143,7 +128,7 @@
}
throwIfCannotDraw(bitmap);
mNativeCanvasWrapper = initRaster(bitmap);
- mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
+ mFinalizer = sRegistry.registerNativeAllocation(this, mNativeCanvasWrapper);
mBitmap = bitmap;
mDensity = bitmap.mDensity;
}
@@ -154,7 +139,7 @@
throw new IllegalStateException();
}
mNativeCanvasWrapper = nativeCanvas;
- mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper);
+ mFinalizer = sRegistry.registerNativeAllocation(this, mNativeCanvasWrapper);
mDensity = Bitmap.getDefaultDensity();
}
@@ -1980,7 +1965,11 @@
* @hide
*/
public void release() {
- mFinalizer.dispose();
+ mNativeCanvasWrapper = 0;
+ if (mFinalizer != null) {
+ mFinalizer.run();
+ mFinalizer = null;
+ }
}
/**
@@ -2148,5 +2137,5 @@
float hOffset,
float vOffset,
int flags, long nativePaint, long nativeTypeface);
- private static native void finalizer(long nativeCanvas);
+ private static native long getNativeFinalizer();
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 90522f7..dfb8bb8 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -30,6 +30,8 @@
import java.util.HashMap;
import java.util.Locale;
+import libcore.util.NativeAllocationRegistry;
+
/**
* The Paint class holds the style and color information about how to draw
* geometries, text and bitmaps.
@@ -39,6 +41,12 @@
private long mNativePaint;
private long mNativeShader = 0;
+ // The approximate size of a native paint object.
+ private static final long NATIVE_PAINT_SIZE = 98;
+
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ nGetNativeFinalizer(), NATIVE_PAINT_SIZE);
+
/**
* @hide
*/
@@ -444,6 +452,7 @@
*/
public Paint(int flags) {
mNativePaint = nInit();
+ sRegistry.registerNativeAllocation(this, mNativePaint);
setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
@@ -462,12 +471,12 @@
*/
public Paint(Paint paint) {
mNativePaint = nInitWithPaint(paint.getNativeInstance());
+ sRegistry.registerNativeAllocation(this, mNativePaint);
setClassVariablesFrom(paint);
}
/** Restores the paint to its default settings. */
public void reset() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nReset(mNativePaint);
setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
@@ -502,8 +511,6 @@
* methods on this.
*/
public void set(Paint src) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
- if (src.mNativePaint == 0) throw new NullPointerException("Source is already finalized!");
if (this != src) {
// copy over the native settings
nSet(mNativePaint, src.mNativePaint);
@@ -554,7 +561,6 @@
* @hide
*/
public long getNativeInstance() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
if (newNativeShader != mNativeShader) {
mNativeShader = newNativeShader;
@@ -592,7 +598,6 @@
* @return the paint's flags (see enums ending in _Flag for bit masks)
*/
public int getFlags() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetFlags(mNativePaint);
}
@@ -604,7 +609,6 @@
* @param flags The new flag bits for the paint
*/
public void setFlags(int flags) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetFlags(mNativePaint, flags);
}
@@ -615,7 +619,6 @@
* {@link #HINTING_OFF} or {@link #HINTING_ON}.
*/
public int getHinting() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetHinting(mNativePaint);
}
@@ -626,7 +629,6 @@
* {@link #HINTING_OFF} or {@link #HINTING_ON}.
*/
public void setHinting(int mode) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetHinting(mNativePaint, mode);
}
@@ -653,7 +655,6 @@
* @param aa true to set the antialias bit in the flags, false to clear it
*/
public void setAntiAlias(boolean aa) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetAntiAlias(mNativePaint, aa);
}
@@ -684,7 +685,6 @@
* @param dither true to set the dithering bit in flags, false to clear it
*/
public void setDither(boolean dither) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetDither(mNativePaint, dither);
}
@@ -706,7 +706,6 @@
* false to clear it.
*/
public void setLinearText(boolean linearText) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetLinearText(mNativePaint, linearText);
}
@@ -728,7 +727,6 @@
* flags, false to clear it.
*/
public void setSubpixelText(boolean subpixelText) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetSubpixelText(mNativePaint, subpixelText);
}
@@ -750,7 +748,6 @@
* flags, false to clear it.
*/
public void setUnderlineText(boolean underlineText) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetUnderlineText(mNativePaint, underlineText);
}
@@ -772,7 +769,6 @@
* flags, false to clear it.
*/
public void setStrikeThruText(boolean strikeThruText) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetStrikeThruText(mNativePaint, strikeThruText);
}
@@ -794,7 +790,6 @@
* flags, false to clear it.
*/
public void setFakeBoldText(boolean fakeBoldText) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetFakeBoldText(mNativePaint, fakeBoldText);
}
@@ -822,7 +817,6 @@
* flags, false to clear it.
*/
public void setFilterBitmap(boolean filter) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetFilterBitmap(mNativePaint, filter);
}
@@ -836,7 +830,6 @@
* @return the paint's style setting (Fill, Stroke, StrokeAndFill)
*/
public Style getStyle() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return sStyleArray[nGetStyle(mNativePaint)];
}
@@ -848,7 +841,6 @@
* @param style The new style to set in the paint
*/
public void setStyle(Style style) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetStyle(mNativePaint, style.nativeInt);
}
@@ -862,7 +854,6 @@
*/
@ColorInt
public int getColor() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetColor(mNativePaint);
}
@@ -877,7 +868,6 @@
* @param color The new color (including alpha) to set in the paint.
*/
public void setColor(@ColorInt int color) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetColor(mNativePaint, color);
}
@@ -891,7 +881,6 @@
* @return the alpha component of the paint's color.
*/
public int getAlpha() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetAlpha(mNativePaint);
}
@@ -905,7 +894,6 @@
* @param a set the alpha component [0..255] of the paint's color.
*/
public void setAlpha(int a) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetAlpha(mNativePaint, a);
}
@@ -933,7 +921,6 @@
* Stroke or StrokeAndFill.
*/
public float getStrokeWidth() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetStrokeWidth(mNativePaint);
}
@@ -948,7 +935,6 @@
* style is Stroke or StrokeAndFill.
*/
public void setStrokeWidth(float width) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetStrokeWidth(mNativePaint, width);
}
@@ -962,7 +948,6 @@
* Stroke or StrokeAndFill.
*/
public float getStrokeMiter() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetStrokeMiter(mNativePaint);
}
@@ -976,7 +961,6 @@
* style is Stroke or StrokeAndFill.
*/
public void setStrokeMiter(float miter) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetStrokeMiter(mNativePaint, miter);
}
@@ -990,7 +974,6 @@
* style is Stroke or StrokeAndFill.
*/
public Cap getStrokeCap() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return sCapArray[nGetStrokeCap(mNativePaint)];
}
@@ -1001,7 +984,6 @@
* style is Stroke or StrokeAndFill.
*/
public void setStrokeCap(Cap cap) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetStrokeCap(mNativePaint, cap.nativeInt);
}
@@ -1011,7 +993,6 @@
* @return the paint's Join.
*/
public Join getStrokeJoin() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return sJoinArray[nGetStrokeJoin(mNativePaint)];
}
@@ -1022,7 +1003,6 @@
* Stroke or StrokeAndFill.
*/
public void setStrokeJoin(Join join) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetStrokeJoin(mNativePaint, join.nativeInt);
}
@@ -1038,7 +1018,6 @@
* drawn with a hairline (width == 0)
*/
public boolean getFillPath(Path src, Path dst) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetFillPath(mNativePaint, src.ni(), dst.ni());
}
@@ -1061,6 +1040,11 @@
* @return shader
*/
public Shader setShader(Shader shader) {
+ // If mShader changes, cached value of native shader aren't valid, since
+ // old shader's pointer may be reused by another shader allocation later
+ if (mShader != shader) {
+ mNativeShader = -1;
+ }
// Defer setting the shader natively until getNativeInstance() is called
mShader = shader;
return shader;
@@ -1082,7 +1066,6 @@
* @return filter
*/
public ColorFilter setColorFilter(ColorFilter filter) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long filterNative = 0;
if (filter != null)
filterNative = filter.native_instance;
@@ -1110,7 +1093,6 @@
* @return xfermode
*/
public Xfermode setXfermode(Xfermode xfermode) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long xfermodeNative = 0;
if (xfermode != null)
xfermodeNative = xfermode.native_instance;
@@ -1138,7 +1120,6 @@
* @return effect
*/
public PathEffect setPathEffect(PathEffect effect) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long effectNative = 0;
if (effect != null) {
effectNative = effect.native_instance;
@@ -1168,7 +1149,6 @@
* @return maskfilter
*/
public MaskFilter setMaskFilter(MaskFilter maskfilter) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long maskfilterNative = 0;
if (maskfilter != null) {
maskfilterNative = maskfilter.native_instance;
@@ -1200,7 +1180,6 @@
* @return typeface
*/
public Typeface setTypeface(Typeface typeface) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long typefaceNative = 0;
if (typeface != null) {
typefaceNative = typeface.native_instance;
@@ -1239,7 +1218,6 @@
*/
@Deprecated
public Rasterizer setRasterizer(Rasterizer rasterizer) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long rasterizerNative = 0;
if (rasterizer != null) {
rasterizerNative = rasterizer.native_instance;
@@ -1262,7 +1240,6 @@
* opaque, or the alpha from the shadow color if not.
*/
public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
}
@@ -1280,7 +1257,6 @@
* @hide
*/
public boolean hasShadowLayer() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nHasShadowLayer(mNativePaint);
}
@@ -1293,7 +1269,6 @@
* @return the paint's Align value for drawing text.
*/
public Align getTextAlign() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return sAlignArray[nGetTextAlign(mNativePaint)];
}
@@ -1306,7 +1281,6 @@
* @param align set the paint's Align value for drawing text.
*/
public void setTextAlign(Align align) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetTextAlign(mNativePaint, align.nativeInt);
}
@@ -1340,7 +1314,6 @@
* @param locale the paint's locale value for drawing text, must not be null.
*/
public void setTextLocale(@NonNull Locale locale) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (locale == null) {
throw new IllegalArgumentException("locale cannot be null");
}
@@ -1379,7 +1352,6 @@
* @param locales the paint's locale list for drawing text, must not be null or empty.
*/
public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (locales == null || locales.isEmpty()) {
throw new IllegalArgumentException("locales cannot be null or empty");
}
@@ -1408,7 +1380,6 @@
* @return true if elegant metrics are enabled for text drawing.
*/
public boolean isElegantTextHeight() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nIsElegantTextHeight(mNativePaint);
}
@@ -1422,7 +1393,6 @@
* @param elegant set the paint's elegant metrics flag for drawing text.
*/
public void setElegantTextHeight(boolean elegant) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetElegantTextHeight(mNativePaint, elegant);
}
@@ -1434,7 +1404,6 @@
* @return the paint's text size.
*/
public float getTextSize() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetTextSize(mNativePaint);
}
@@ -1446,7 +1415,6 @@
* @param textSize set the paint's text size.
*/
public void setTextSize(float textSize) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetTextSize(mNativePaint, textSize);
}
@@ -1459,7 +1427,6 @@
* @return the paint's scale factor in X for drawing/measuring text
*/
public float getTextScaleX() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetTextScaleX(mNativePaint);
}
@@ -1473,7 +1440,6 @@
* @param scaleX set the paint's scale in X for drawing/measuring text.
*/
public void setTextScaleX(float scaleX) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetTextScaleX(mNativePaint, scaleX);
}
@@ -1486,7 +1452,6 @@
* @return the paint's skew factor in X for drawing text.
*/
public float getTextSkewX() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetTextSkewX(mNativePaint);
}
@@ -1499,7 +1464,6 @@
* @param skewX set the paint's skew factor in X for drawing text.
*/
public void setTextSkewX(float skewX) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetTextSkewX(mNativePaint, skewX);
}
@@ -1512,7 +1476,6 @@
* @return the paint's letter-spacing for drawing text.
*/
public float getLetterSpacing() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetLetterSpacing(mNativePaint);
}
@@ -1524,7 +1487,6 @@
* @param letterSpacing set the paint's letter-spacing for drawing text.
*/
public void setLetterSpacing(float letterSpacing) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetLetterSpacing(mNativePaint, letterSpacing);
}
@@ -1546,7 +1508,6 @@
* @param settings the font feature settings string to use, may be null.
*/
public void setFontFeatureSettings(String settings) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (settings != null && settings.equals("")) {
settings = null;
}
@@ -1566,7 +1527,6 @@
* @hide
*/
public int getHyphenEdit() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetHyphenEdit(mNativePaint);
}
@@ -1579,7 +1539,6 @@
* @hide
*/
public void setHyphenEdit(int hyphen) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
nSetHyphenEdit(mNativePaint, hyphen);
}
@@ -1591,7 +1550,6 @@
* current typeface and text size.
*/
public float ascent() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nAscent(mNativePaint, mNativeTypeface);
}
@@ -1605,7 +1563,6 @@
* the current typeface and text size.
*/
public float descent() {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nDescent(mNativePaint, mNativeTypeface);
}
@@ -1652,7 +1609,6 @@
* @return the font's recommended interline spacing.
*/
public float getFontMetrics(FontMetrics metrics) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
}
@@ -1698,7 +1654,6 @@
* @return the font's interline spacing.
*/
public int getFontMetricsInt(FontMetricsInt fmi) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
}
@@ -1731,7 +1686,6 @@
* @return The width of the text
*/
public float measureText(char[] text, int index, int count) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1764,7 +1718,6 @@
* @return The width of the text
*/
public float measureText(String text, int start, int end) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1794,7 +1747,6 @@
* @return The width of the text
*/
public float measureText(String text) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1855,7 +1807,6 @@
*/
public int breakText(char[] text, int index, int count,
float maxWidth, float[] measuredWidth) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1903,7 +1854,6 @@
public int breakText(CharSequence text, int start, int end,
boolean measureForwards,
float maxWidth, float[] measuredWidth) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1952,7 +1902,6 @@
*/
public int breakText(String text, boolean measureForwards,
float maxWidth, float[] measuredWidth) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1990,7 +1939,6 @@
*/
public int getTextWidths(char[] text, int index, int count,
float[] widths) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2074,7 +2022,6 @@
* @return the number of code units in the specified text.
*/
public int getTextWidths(String text, int start, int end, float[] widths) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2128,7 +2075,6 @@
int contextIndex, int contextCount, boolean isRtl, float[] advances,
int advancesIndex) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (chars == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2175,7 +2121,6 @@
public float getTextRunAdvances(CharSequence text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, float[] advances,
int advancesIndex) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2257,7 +2202,6 @@
*/
public float getTextRunAdvances(String text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2322,7 +2266,6 @@
*/
public int getTextRunCursor(char[] text, int contextStart, int contextLength,
int dir, int offset, int cursorOpt) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
int contextEnd = contextStart + contextLength;
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
@@ -2410,7 +2353,6 @@
*/
public int getTextRunCursor(String text, int contextStart, int contextEnd,
int dir, int offset, int cursorOpt) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
| (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2437,7 +2379,6 @@
*/
public void getTextPath(char[] text, int index, int count,
float x, float y, Path path) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -2460,7 +2401,6 @@
*/
public void getTextPath(String text, int start, int end,
float x, float y, Path path) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
@@ -2479,7 +2419,6 @@
* allocated by the caller.
*/
public void getTextBounds(String text, int start, int end, Rect bounds) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
@@ -2500,7 +2439,6 @@
* allocated by the caller.
*/
public void getTextBounds(char[] text, int index, int count, Rect bounds) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -2528,7 +2466,6 @@
* @return true if the typeface has a glyph for the string
*/
public boolean hasGlyph(String string) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
}
@@ -2570,7 +2507,6 @@
*/
public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd,
boolean isRtl, int offset) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2601,7 +2537,6 @@
*/
public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, int offset) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2652,7 +2587,6 @@
*/
public int getOffsetForAdvance(char[] text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, float advance) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2680,7 +2614,6 @@
*/
public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, float advance) {
- if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2698,18 +2631,6 @@
return result;
}
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mNativePaint != 0) {
- nFinalizer(mNativePaint);
- mNativePaint = 0;
- }
- } finally {
- super.finalize();
- }
- }
-
private static native long nInit();
private static native long nInitWithPaint(long paint);
private static native void nReset(long paintPtr);
@@ -2765,7 +2686,7 @@
String text, int start, int end, int bidiFlags, Rect bounds);
private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
char[] text, int index, int count, int bidiFlags, Rect bounds);
- private static native void nFinalizer(long nativePaint);
+ private static native long nGetNativeFinalizer();
private static native void nSetShadowLayer(long paintPtr,
float radius, float dx, float dy, int color);
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 651b453..e2150c0 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -82,8 +82,13 @@
*/
public static final int PADDING_MODE_STACK = 1;
- /** Value used for undefined start and end insets. */
- private static final int UNDEFINED_INSET = Integer.MIN_VALUE;
+ /**
+ * Value used for undefined start and end insets.
+ *
+ * @see #getLayerInsetStart(int)
+ * @see #getLayerInsetEnd(int)
+ */
+ public static final int UNDEFINED_INSET = Integer.MIN_VALUE;
LayerState mLayerState;
@@ -867,7 +872,8 @@
/**
* @param index the index of the layer
- * @return number of pixels to inset from the start bound
+ * @return the number of pixels to inset from the start bound, or
+ * {@link #UNDEFINED_INSET} if not specified
* @attr ref android.R.styleable#LayerDrawableItem_start
*/
public int getLayerInsetStart(int index) {
@@ -877,7 +883,8 @@
/**
* @param index the index of the layer to adjust
- * @param e number of pixels to inset from the end bound
+ * @param e number of pixels to inset from the end bound, or
+ * {@link #UNDEFINED_INSET} if not specified
* @attr ref android.R.styleable#LayerDrawableItem_end
*/
public void setLayerInsetEnd(int index, int e) {
@@ -977,34 +984,33 @@
computeStackedPadding(padding);
}
+ final int paddingT = layerState.mPaddingTop;
+ final int paddingB = layerState.mPaddingBottom;
+
+ // Resolve padding for RTL. Relative padding overrides absolute
+ // padding.
+ final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL;
+ final int paddingRtlL = isLayoutRtl ? layerState.mPaddingEnd : layerState.mPaddingStart;
+ final int paddingRtlR = isLayoutRtl ? layerState.mPaddingStart : layerState.mPaddingEnd;
+ final int paddingL = paddingRtlL >= 0 ? paddingRtlL : layerState.mPaddingLeft;
+ final int paddingR = paddingRtlR >= 0 ? paddingRtlR : layerState.mPaddingRight;
+
// If padding was explicitly specified (e.g. not -1) then override the
// computed padding in that dimension.
- if (layerState.mPaddingTop >= 0) {
- padding.top = layerState.mPaddingTop;
+ if (paddingL >= 0) {
+ padding.left = paddingL;
}
- if (layerState.mPaddingBottom >= 0) {
- padding.bottom = layerState.mPaddingBottom;
+ if (paddingT >= 0) {
+ padding.top = paddingT;
}
- final int paddingRtlLeft;
- final int paddingRtlRight;
- if (getLayoutDirection() == LayoutDirection.RTL) {
- paddingRtlLeft = layerState.mPaddingEnd;
- paddingRtlRight = layerState.mPaddingStart;
- } else {
- paddingRtlLeft = layerState.mPaddingStart;
- paddingRtlRight = layerState.mPaddingEnd;
+ if (paddingR >= 0) {
+ padding.right = paddingR;
}
- final int paddingLeft = paddingRtlLeft >= 0 ? paddingRtlLeft : layerState.mPaddingLeft;
- if (paddingLeft >= 0) {
- padding.left = paddingLeft;
- }
-
- final int paddingRight = paddingRtlRight >= 0 ? paddingRtlRight : layerState.mPaddingRight;
- if (paddingRight >= 0) {
- padding.right = paddingRight;
+ if (paddingB >= 0) {
+ padding.bottom = paddingB;
}
return padding.left != 0 || padding.top != 0 || padding.right != 0 || padding.bottom != 0;
@@ -1471,58 +1477,59 @@
}
private void updateLayerBounds(Rect bounds) {
- int padL = 0;
- int padT = 0;
- int padR = 0;
- int padB = 0;
+ int paddingL = 0;
+ int paddingT = 0;
+ int paddingR = 0;
+ int paddingB = 0;
final Rect outRect = mTmpOutRect;
final int layoutDirection = getLayoutDirection();
- final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
+ final boolean isLayoutRtl = layoutDirection == LayoutDirection.RTL;
+ final boolean isPaddingNested = mLayerState.mPaddingMode == PADDING_MODE_NEST;
final ChildDrawable[] array = mLayerState.mChildren;
- final int N = mLayerState.mNum;
- for (int i = 0; i < N; i++) {
+
+ for (int i = 0, count = mLayerState.mNum; i < count; i++) {
final ChildDrawable r = array[i];
final Drawable d = r.mDrawable;
if (d == null) {
continue;
}
- final Rect container = mTmpContainer;
- container.set(d.getBounds());
+ final int insetT = r.mInsetT;
+ final int insetB = r.mInsetB;
- // Take the resolved layout direction into account. If start / end
- // padding are defined, they will be resolved (hence overriding) to
- // left / right or right / left depending on the resolved layout
- // direction. If start / end padding are not defined, use the
- // left / right ones.
- final int insetL, insetR;
- if (layoutDirection == LayoutDirection.RTL) {
- insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
- insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
- } else {
- insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
- insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
- }
+ // Resolve insets for RTL. Relative insets override absolute
+ // 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;
// Establish containing region based on aggregate padding and
// requested insets for the current layer.
- container.set(bounds.left + insetL + padL, bounds.top + r.mInsetT + padT,
- bounds.right - insetR - padR, bounds.bottom - r.mInsetB - padB);
+ final Rect container = mTmpContainer;
+ container.set(bounds.left + insetL + paddingL, bounds.top + insetT + paddingT,
+ bounds.right - insetR - paddingR, bounds.bottom - insetB - paddingB);
- // Apply resolved gravity to drawable based on resolved size.
- final int gravity = resolveGravity(r.mGravity, r.mWidth, r.mHeight,
- d.getIntrinsicWidth(), d.getIntrinsicHeight());
- final int w = r.mWidth < 0 ? d.getIntrinsicWidth() : r.mWidth;
- final int h = r.mHeight < 0 ? d.getIntrinsicHeight() : r.mHeight;
- Gravity.apply(gravity, w, h, container, outRect, layoutDirection);
+ // Compute a reasonable default gravity based on the intrinsic and
+ // explicit dimensions, if specified.
+ final int intrinsicW = d.getIntrinsicWidth();
+ final int intrinsicH = d.getIntrinsicHeight();
+ final int layerW = r.mWidth;
+ final int layerH = r.mHeight;
+ final int gravity = resolveGravity(r.mGravity, layerW, layerH, intrinsicW, intrinsicH);
+
+ // Explicit dimensions override intrinsic dimensions.
+ final int resolvedW = layerW < 0 ? intrinsicW : layerW;
+ final int resolvedH = layerH < 0 ? intrinsicH : layerH;
+ Gravity.apply(gravity, resolvedW, resolvedH, container, outRect, layoutDirection);
d.setBounds(outRect);
- if (nest) {
- padL += mPaddingL[i];
- padR += mPaddingR[i];
- padT += mPaddingT[i];
- padB += mPaddingB[i];
+ if (isPaddingNested) {
+ paddingL += mPaddingL[i];
+ paddingR += mPaddingR[i];
+ paddingT += mPaddingT[i];
+ paddingB += mPaddingB[i];
}
}
}
@@ -1578,6 +1585,7 @@
int padR = 0;
final boolean nest = mLayerState.mPaddingMode == PADDING_MODE_NEST;
+ final boolean isLayoutRtl = getLayoutDirection() == LayoutDirection.RTL;
final ChildDrawable[] array = mLayerState.mChildren;
final int N = mLayerState.mNum;
for (int i = 0; i < N; i++) {
@@ -1591,15 +1599,10 @@
// left / right or right / left depending on the resolved layout
// direction. If start / end padding are not defined, use the
// left / right ones.
- final int insetL, insetR;
- final int layoutDirection = getLayoutDirection();
- if (layoutDirection == LayoutDirection.RTL) {
- insetL = r.mInsetE == UNDEFINED_INSET ? r.mInsetL : r.mInsetE;
- insetR = r.mInsetS == UNDEFINED_INSET ? r.mInsetR : r.mInsetS;
- } else {
- insetL = r.mInsetS == UNDEFINED_INSET ? r.mInsetL : r.mInsetS;
- insetR = r.mInsetE == UNDEFINED_INSET ? r.mInsetR : r.mInsetE;
- }
+ 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;
// Don't apply padding and insets for children that don't have
// an intrinsic dimension.
@@ -1659,8 +1662,8 @@
if (r.mDrawable != null) {
final Rect rect = mTmpRect;
r.mDrawable.getPadding(rect);
- if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i] ||
- rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
+ if (rect.left != mPaddingL[i] || rect.top != mPaddingT[i]
+ || rect.right != mPaddingR[i] || rect.bottom != mPaddingB[i]) {
mPaddingL[i] = rect.left;
mPaddingT[i] = rect.top;
mPaddingR[i] = rect.right;
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 78424e3..c0dfe77 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -204,11 +204,13 @@
/**
* Sets the X position around which the drawable is rotated.
+ * <p>
+ * If the X pivot is relative (as specified by
+ * {@link #setPivotXRelative(boolean)}), then the position represents a
+ * fraction of the drawable width. Otherwise, the position represents an
+ * absolute value in pixels.
*
- * @param pivotX X position around which to rotate. If the X pivot is
- * relative, the position represents a fraction of the drawable
- * width. Otherwise, the position represents an absolute value in
- * pixels.
+ * @param pivotX X position around which to rotate
* @see #setPivotXRelative(boolean)
* @attr ref android.R.styleable#RotateDrawable_pivotX
*/
@@ -254,11 +256,13 @@
/**
* Sets the Y position around which the drawable is rotated.
+ * <p>
+ * If the Y pivot is relative (as specified by
+ * {@link #setPivotYRelative(boolean)}), then the position represents a
+ * fraction of the drawable height. Otherwise, the position represents an
+ * absolute value in pixels.
*
- * @param pivotY Y position around which to rotate. If the Y pivot is
- * relative, the position represents a fraction of the drawable
- * height. Otherwise, the position represents an absolute value
- * in pixels.
+ * @param pivotY Y position around which to rotate
* @see #getPivotY()
* @attr ref android.R.styleable#RotateDrawable_pivotY
*/
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 0932e89..e2350b6 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
hwui_src_files := \
font/CacheTexture.cpp \
@@ -53,6 +53,7 @@
FrameInfoVisualizer.cpp \
GammaFontRenderer.cpp \
GlopBuilder.cpp \
+ GpuMemoryTracker.cpp \
GradientCache.cpp \
Image.cpp \
Interpolator.cpp \
@@ -109,8 +110,8 @@
BakedOpDispatcher.cpp \
BakedOpRenderer.cpp \
BakedOpState.cpp \
- FrameReorderer.cpp \
- LayerReorderer.cpp \
+ FrameBuilder.cpp \
+ LayerBuilder.cpp \
RecordingCanvas.cpp
hwui_cflags += -DHWUI_NEW_OPS
@@ -225,9 +226,11 @@
$(hwui_test_common_src_files) \
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/GpuMemoryTrackerTests.cpp \
tests/unit/LayerUpdateQueueTests.cpp \
tests/unit/LinearAllocatorTests.cpp \
tests/unit/VectorDrawableTests.cpp \
@@ -237,7 +240,7 @@
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
tests/unit/BakedOpStateTests.cpp \
- tests/unit/FrameReordererTests.cpp \
+ tests/unit/FrameBuilderTests.cpp \
tests/unit/RecordingCanvasTests.cpp
endif
@@ -297,9 +300,9 @@
tests/microbench/PathParserBench.cpp \
tests/microbench/ShadowBench.cpp
-ifeq (true, $(HWUI_NEW_OPS))
- LOCAL_SRC_FILES += \
- tests/microbench/FrameReordererBench.cpp
-endif
+# ifeq (true, $(HWUI_NEW_OPS))
+# LOCAL_SRC_FILES += \
+# tests/microbench/FrameBuilderBench.cpp
+# endif
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
index 41411a9..6afff1b 100644
--- a/libs/hwui/AssetAtlas.cpp
+++ b/libs/hwui/AssetAtlas.cpp
@@ -39,40 +39,22 @@
if (!mTexture) {
Caches& caches = Caches::getInstance();
mTexture = new Texture(caches);
- mTexture->width = buffer->getWidth();
- mTexture->height = buffer->getHeight();
+ mTexture->wrap(mImage->getTexture(),
+ buffer->getWidth(), buffer->getHeight(), GL_RGBA);
createEntries(caches, map, count);
}
} else {
ALOGW("Could not create atlas image");
- delete mImage;
- mImage = nullptr;
+ terminate();
}
-
- updateTextureId();
}
void AssetAtlas::terminate() {
- if (mImage) {
- delete mImage;
- mImage = nullptr;
- updateTextureId();
- }
-}
-
-
-void AssetAtlas::updateTextureId() {
- mTexture->id = mImage ? mImage->getTexture() : 0;
- if (mTexture->id) {
- // Texture ID changed, force-set to defaults to sync the wrapper & GL
- // state objects
- mTexture->setWrap(GL_CLAMP_TO_EDGE, false, true);
- mTexture->setFilter(GL_NEAREST, false, true);
- }
- for (size_t i = 0; i < mEntries.size(); i++) {
- AssetAtlas::Entry* entry = mEntries.valueAt(i);
- entry->texture->id = mTexture->id;
- }
+ delete mImage;
+ mImage = nullptr;
+ delete mTexture;
+ mTexture = nullptr;
+ mEntries.clear();
}
///////////////////////////////////////////////////////////////////////////////
@@ -80,13 +62,13 @@
///////////////////////////////////////////////////////////////////////////////
AssetAtlas::Entry* AssetAtlas::getEntry(const SkPixelRef* pixelRef) const {
- ssize_t index = mEntries.indexOfKey(pixelRef);
- return index >= 0 ? mEntries.valueAt(index) : nullptr;
+ auto result = mEntries.find(pixelRef);
+ return result != mEntries.end() ? result->second.get() : nullptr;
}
Texture* AssetAtlas::getEntryTexture(const SkPixelRef* pixelRef) const {
- ssize_t index = mEntries.indexOfKey(pixelRef);
- return index >= 0 ? mEntries.valueAt(index)->texture : nullptr;
+ auto result = mEntries.find(pixelRef);
+ return result != mEntries.end() ? result->second->texture : nullptr;
}
/**
@@ -94,7 +76,8 @@
* instead of applying the changes to the virtual textures.
*/
struct DelegateTexture: public Texture {
- DelegateTexture(Caches& caches, Texture* delegate): Texture(caches), mDelegate(delegate) { }
+ DelegateTexture(Caches& caches, Texture* delegate)
+ : Texture(caches), mDelegate(delegate) { }
virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override {
@@ -111,8 +94,8 @@
}; // struct DelegateTexture
void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) {
- const float width = float(mTexture->width);
- const float height = float(mTexture->height);
+ const float width = float(mTexture->width());
+ const float height = float(mTexture->height());
for (int i = 0; i < count; ) {
SkPixelRef* pixelRef = reinterpret_cast<SkPixelRef*>(map[i++]);
@@ -133,13 +116,13 @@
Texture* texture = new DelegateTexture(caches, mTexture);
texture->blend = !SkAlphaTypeIsOpaque(pixelRef->info().alphaType());
- texture->width = pixelRef->info().width();
- texture->height = pixelRef->info().height();
+ texture->wrap(mTexture->id(), pixelRef->info().width(),
+ pixelRef->info().height(), mTexture->format());
- Entry* entry = new Entry(pixelRef, texture, mapper, *this);
+ std::unique_ptr<Entry> entry(new Entry(pixelRef, texture, mapper, *this));
texture->uvMapper = &entry->uvMapper;
- mEntries.add(entry->pixelRef, entry);
+ mEntries.emplace(entry->pixelRef, std::move(entry));
}
}
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
index a037725..75400ff 100644
--- a/libs/hwui/AssetAtlas.h
+++ b/libs/hwui/AssetAtlas.h
@@ -17,19 +17,17 @@
#ifndef ANDROID_HWUI_ASSET_ATLAS_H
#define ANDROID_HWUI_ASSET_ATLAS_H
-#include <GLES2/gl2.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <utils/KeyedVector.h>
-
-#include <cutils/compiler.h>
-
-#include <SkBitmap.h>
-
#include "Texture.h"
#include "UvMapper.h"
+#include <cutils/compiler.h>
+#include <GLES2/gl2.h>
+#include <ui/GraphicBuffer.h>
+#include <SkBitmap.h>
+
+#include <memory>
+#include <unordered_map>
+
namespace android {
namespace uirenderer {
@@ -71,6 +69,10 @@
return texture->blend ? &atlas.mBlendKey : &atlas.mOpaqueKey;
}
+ ~Entry() {
+ delete texture;
+ }
+
private:
/**
* The pixel ref that generated this atlas entry.
@@ -90,10 +92,6 @@
, atlas(atlas) {
}
- ~Entry() {
- delete texture;
- }
-
friend class AssetAtlas;
};
@@ -127,7 +125,7 @@
* Can return 0 if the atlas is not initialized.
*/
uint32_t getWidth() const {
- return mTexture ? mTexture->width : 0;
+ return mTexture ? mTexture->width() : 0;
}
/**
@@ -135,7 +133,7 @@
* Can return 0 if the atlas is not initialized.
*/
uint32_t getHeight() const {
- return mTexture ? mTexture->height : 0;
+ return mTexture ? mTexture->height() : 0;
}
/**
@@ -143,7 +141,7 @@
* Can return 0 if the atlas is not initialized.
*/
GLuint getTexture() const {
- return mTexture ? mTexture->id : 0;
+ return mTexture ? mTexture->id() : 0;
}
/**
@@ -160,7 +158,6 @@
private:
void createEntries(Caches& caches, int64_t* map, int count);
- void updateTextureId();
Texture* mTexture;
Image* mImage;
@@ -168,7 +165,7 @@
const bool mBlendKey;
const bool mOpaqueKey;
- KeyedVector<const SkPixelRef*, Entry*> mEntries;
+ std::unordered_map<const SkPixelRef*, std::unique_ptr<Entry>> mEntries;
}; // class AssetAtlas
}; // namespace uirenderer
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 23aca89..6b0c149 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -217,7 +217,7 @@
.setMeshTexturedUnitQuad(nullptr)
.setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha)
.setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
+ .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
.build();
renderer.renderGlop(state, glop);
}
@@ -337,7 +337,7 @@
static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
PathTexture& texture, const RecordedOp& op) {
- Rect dest(texture.width, texture.height);
+ Rect dest(texture.width(), texture.height());
dest.translate(texture.left - texture.offset,
texture.top - texture.offset);
Glop glop;
@@ -399,7 +399,7 @@
.setMeshTexturedUnitQuad(texture->uvMapper)
.setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
.setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height))
+ .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
.build();
renderer.renderGlop(state, glop);
}
@@ -483,10 +483,10 @@
if (!texture) return;
const AutoTexture autoCleanup(texture);
- Rect uv(std::max(0.0f, op.src.left / texture->width),
- std::max(0.0f, op.src.top / texture->height),
- std::min(1.0f, op.src.right / texture->width),
- std::min(1.0f, op.src.bottom / texture->height));
+ Rect uv(std::max(0.0f, op.src.left / texture->width()),
+ std::max(0.0f, op.src.top / texture->height()),
+ std::min(1.0f, op.src.right / texture->width()),
+ std::min(1.0f, op.src.bottom / texture->height()));
const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
@@ -545,19 +545,19 @@
op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
- if (!texture) return;
- const AutoTexture autoCleanup(texture);
- Glop glop;
- GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
- .setRoundRectClipState(state.roundRectClipState)
- .setMeshPatchQuads(*mesh)
- .setMeshTexturedUnitQuad(texture->uvMapper)
- .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
- .setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
- Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
- .build();
- renderer.renderGlop(state, glop);
+ if (CC_LIKELY(texture)) {
+ const AutoTexture autoCleanup(texture);
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshPatchQuads(*mesh)
+ .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+ Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
+ .build();
+ renderer.renderGlop(state, glop);
+ }
}
void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index b9c13e6..4fbff0d 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -48,7 +48,7 @@
// attach the texture to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- offscreenBuffer->texture.id, 0);
+ offscreenBuffer->texture.id(), 0);
LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
"framebuffer incomplete!");
@@ -84,7 +84,7 @@
area.getWidth(), area.getHeight());
if (!area.isEmpty()) {
mCaches.textureState().activateTexture(0);
- mCaches.textureState().bindTexture(buffer->texture.id);
+ mCaches.textureState().bindTexture(buffer->texture.id());
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
area.left, mRenderTarget.viewportHeight - area.bottom,
@@ -161,7 +161,7 @@
}
void BakedOpRenderer::clearColorBuffer(const Rect& rect) {
- if (Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight).contains(rect)) {
+ if (rect.contains(Rect(mRenderTarget.viewportWidth, mRenderTarget.viewportHeight))) {
// Full viewport is being cleared - disable scissor
mRenderState.scissor().setEnabled(false);
} else {
@@ -201,6 +201,7 @@
}
void BakedOpRenderer::setupStencilRectList(const ClipBase* clip) {
+ LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::RectangleList, "can't rectlist clip without rectlist");
auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList;
int quadCount = rectList.getTransformedRectanglesCount();
std::vector<Vertex> rectangleVertices;
@@ -234,6 +235,7 @@
}
void BakedOpRenderer::setupStencilRegion(const ClipBase* clip) {
+ LOG_ALWAYS_FATAL_IF(clip->mode != ClipMode::Region, "can't region clip without region");
auto&& region = reinterpret_cast<const ClipRegion*>(clip)->region;
std::vector<Vertex> regionVertices;
@@ -270,7 +272,7 @@
OffscreenBuffer* layer = mRenderTarget.offscreenBuffer;
mRenderTarget.stencil = mCaches.renderBufferCache.get(
Stencil::getLayerStencilFormat(),
- layer->texture.width, layer->texture.height);
+ layer->texture.width(), layer->texture.height());
// stencil is bound + allocated - associate it with current FBO
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, mRenderTarget.stencil->getName());
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index f1cc846..87844f9 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -48,10 +48,10 @@
const Rect& clipRect = clipState->rect;
if (CC_UNLIKELY(clipRect.isEmpty() || !clippedBounds.intersects(clipRect))) {
// Rejected based on either empty clip, or bounds not intersecting with clip
- if (clipState) {
- allocator.rewindIfLastAlloc(clipState);
- clipState = nullptr;
- }
+
+ // Note: we could rewind the clipState object in situations where the clipRect is empty,
+ // but *only* if the caching logic within ClipArea was aware of the rewind.
+ clipState = nullptr;
clippedBounds.setEmpty();
} else {
// Not rejected! compute true clippedBounds and clipSideFlags
@@ -71,7 +71,6 @@
clipState = snapshot.mutateClipArea().serializeClip(allocator);
LOG_ALWAYS_FATAL_IF(!clipState, "clipState required");
clippedBounds = clipState->rect;
- transform.mapRect(clippedBounds);
clipSideFlags = OpClipSideFlags::Full;
}
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 5c7b43f..3db28c9 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -99,6 +99,7 @@
public:
static BakedOpState* tryConstruct(LinearAllocator& allocator,
Snapshot& snapshot, const RecordedOp& recordedOp) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
BakedOpState* bakedState = new (allocator) BakedOpState(
allocator, snapshot, recordedOp, false);
if (bakedState->computedState.clippedBounds.isEmpty()) {
@@ -118,6 +119,7 @@
static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
: true;
@@ -126,6 +128,7 @@
allocator, snapshot, recordedOp, expandForStroke);
if (bakedState->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
+ // NOTE: this won't succeed if a clip was allocated
allocator.rewindIfLastAlloc(bakedState);
return nullptr;
}
@@ -134,7 +137,7 @@
static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
- if (snapshot.getRenderTargetClip().isEmpty()) return nullptr;
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
// clip isn't empty, so construct the op
return new (allocator) BakedOpState(allocator, snapshot, shadowOpPtr);
diff --git a/libs/hwui/FrameReorderer.cpp b/libs/hwui/FrameBuilder.cpp
similarity index 86%
rename from libs/hwui/FrameReorderer.cpp
rename to libs/hwui/FrameBuilder.cpp
index 4bfc0b4..c4c655b 100644
--- a/libs/hwui/FrameReorderer.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "FrameReorderer.h"
+#include "FrameBuilder.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
@@ -30,40 +30,46 @@
namespace android {
namespace uirenderer {
-FrameReorderer::FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
- mLayerReorderers.reserve(layers.entries().size());
+ mLayerBuilders.reserve(layers.entries().size());
mLayerStack.reserve(layers.entries().size());
// Prepare to defer Fbo0
- mLayerReorderers.emplace_back(viewportWidth, viewportHeight, Rect(clip));
+ auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip));
+ mLayerBuilders.push_back(fbo0);
mLayerStack.push_back(0);
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
lightCenter);
// Render all layers to be updated, in order. Defer in reverse order, so that they'll be
- // updated in the order they're passed in (mLayerReorderers are issued to Renderer in reverse)
+ // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse)
for (int i = layers.entries().size() - 1; i >= 0; i--) {
RenderNode* layerNode = layers.entries()[i].renderNode;
- const Rect& layerDamage = layers.entries()[i].damage;
- layerNode->computeOrdering();
+ // only schedule repaint if node still on layer - possible it may have been
+ // removed during a dropped frame, but layers may still remain scheduled so
+ // as not to lose info on what portion is damaged
+ if (CC_LIKELY(layerNode->getLayer() != nullptr)) {
+ const Rect& layerDamage = layers.entries()[i].damage;
+ layerNode->computeOrdering();
- // map current light center into RenderNode's coordinate space
- Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
- layerNode->getLayer()->inverseTransformInWindow.mapPoint3d(lightCenter);
+ // map current light center into RenderNode's coordinate space
+ Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter();
+ layerNode->getLayer()->inverseTransformInWindow.mapPoint3d(lightCenter);
- saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0,
- layerDamage, lightCenter, nullptr, layerNode);
+ saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0,
+ layerDamage, lightCenter, nullptr, layerNode);
- if (layerNode->getDisplayList()) {
- deferNodeOps(*layerNode);
+ if (layerNode->getDisplayList()) {
+ deferNodeOps(*layerNode);
+ }
+ restoreForLayer();
}
- restoreForLayer();
}
// Defer Fbo0
@@ -77,11 +83,11 @@
}
}
-void FrameReorderer::onViewportInitialized() {}
+void FrameBuilder::onViewportInitialized() {}
-void FrameReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+void FrameBuilder::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
-void FrameReorderer::deferNodePropsAndOps(RenderNode& node) {
+void FrameBuilder::deferNodePropsAndOps(RenderNode& node) {
const RenderProperties& properties = node.properties();
const Outline& outline = properties.getOutline();
if (properties.getAlpha() <= 0
@@ -213,7 +219,7 @@
}
template <typename V>
-void FrameReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+void FrameBuilder::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
const int size = zTranslatedNodes.size();
if (size == 0
|| (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
@@ -263,7 +269,7 @@
}
}
-void FrameReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
+void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) {
auto& node = *casterNodeOp.renderNode;
auto& properties = node.properties();
@@ -319,7 +325,7 @@
}
}
-void FrameReorderer::deferProjectedChildren(const RenderNode& renderNode) {
+void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) {
const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -352,15 +358,15 @@
}
/**
- * Used to define a list of lambdas referencing private FrameReorderer::onXX::defer() methods.
+ * Used to define a list of lambdas referencing private FrameBuilder::onXX::defer() methods.
*
* This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
- * E.g. a BitmapOp op then would be dispatched to FrameReorderer::onBitmapOp(const BitmapOp&)
+ * E.g. a BitmapOp op then would be dispatched to FrameBuilder::onBitmapOp(const BitmapOp&)
*/
#define OP_RECEIVER(Type) \
- [](FrameReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
-void FrameReorderer::deferNodeOps(const RenderNode& renderNode) {
- typedef void (*OpDispatcher) (FrameReorderer& reorderer, const RecordedOp& op);
+ [](FrameBuilder& frameBuilder, const RecordedOp& op) { frameBuilder.defer##Type(static_cast<const Type&>(op)); },
+void FrameBuilder::deferNodeOps(const RenderNode& renderNode) {
+ typedef void (*OpDispatcher) (FrameBuilder& frameBuilder, const RecordedOp& op);
static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER);
// can't be null, since DL=null node rejection happens before deferNodePropsAndOps
@@ -384,7 +390,7 @@
}
}
-void FrameReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
+void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) {
if (op.renderNode->nothingToDraw()) return;
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
@@ -399,7 +405,7 @@
mCanvasState.restoreToCount(count);
}
-void FrameReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) {
if (!op.skipInOrderDraw) {
deferRenderNodeOpImpl(op);
}
@@ -409,7 +415,7 @@
* Defers an unmergeable, strokeable op, accounting correctly
* for paint's style on the bounds being computed.
*/
-void FrameReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+void FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
BakedOpState::StrokeBehavior strokeBehavior) {
// Note: here we account for stroke when baking the op
BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
@@ -431,7 +437,7 @@
: (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
}
-void FrameReorderer::deferArcOp(const ArcOp& op) {
+void FrameBuilder::deferArcOp(const ArcOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
@@ -440,7 +446,7 @@
|| state.computedState.clipState->mode == ClipMode::Rectangle;
}
-void FrameReorderer::deferBitmapOp(const BitmapOp& op) {
+void FrameBuilder::deferBitmapOp(const BitmapOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
@@ -452,7 +458,7 @@
&& PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
&& op.bitmap->colorType() != kAlpha_8_SkColorType
&& hasMergeableClip(*bakedState)) {
- mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+ mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
// TODO: AssetAtlas in mergeId
currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId);
} else {
@@ -460,19 +466,19 @@
}
}
-void FrameReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
+void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void FrameReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
+void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void FrameReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
+void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
// allocate a temporary oval op (with mAllocator, so it persists until render), so the
// renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
float x = *(op.x);
@@ -487,29 +493,29 @@
deferOvalOp(*resolvedOp);
}
-void FrameReorderer::deferFunctorOp(const FunctorOp& op) {
+void FrameBuilder::deferFunctorOp(const FunctorOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);
}
-void FrameReorderer::deferLinesOp(const LinesOp& op) {
+void FrameBuilder::deferLinesOp(const LinesOp& op) {
batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
}
-void FrameReorderer::deferOvalOp(const OvalOp& op) {
+void FrameBuilder::deferOvalOp(const OvalOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
-void FrameReorderer::deferPatchOp(const PatchOp& op) {
+void FrameBuilder::deferPatchOp(const PatchOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
if (bakedState->computedState.transform.isPureTranslate()
&& PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode
&& hasMergeableClip(*bakedState)) {
- mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+ mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID());
// TODO: AssetAtlas in mergeId
// Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
@@ -520,24 +526,24 @@
}
}
-void FrameReorderer::deferPathOp(const PathOp& op) {
+void FrameBuilder::deferPathOp(const PathOp& op) {
deferStrokeableOp(op, OpBatchType::Bitmap);
}
-void FrameReorderer::deferPointsOp(const PointsOp& op) {
+void FrameBuilder::deferPointsOp(const PointsOp& op) {
batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
}
-void FrameReorderer::deferRectOp(const RectOp& op) {
+void FrameBuilder::deferRectOp(const RectOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
-void FrameReorderer::deferRoundRectOp(const RoundRectOp& op) {
+void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
-void FrameReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
+void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
// allocate a temporary round rect op (with mAllocator, so it persists until render), so the
// renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
@@ -548,7 +554,7 @@
deferRoundRectOp(*resolvedOp);
}
-void FrameReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
+void FrameBuilder::deferSimpleRectsOp(const SimpleRectsOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
@@ -559,7 +565,7 @@
return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText;
}
-void FrameReorderer::deferTextOp(const TextOp& op) {
+void FrameBuilder::deferTextOp(const TextOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
@@ -574,19 +580,19 @@
}
}
-void FrameReorderer::deferTextOnPathOp(const TextOnPathOp& op) {
+void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
}
-void FrameReorderer::deferTextureLayerOp(const TextureLayerOp& op) {
+void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
}
-void FrameReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
float contentTranslateX, float contentTranslateY,
const Rect& repaintRect,
const Vector3& lightCenter,
@@ -601,11 +607,13 @@
repaintRect.left, repaintRect.top, repaintRect.right, repaintRect.bottom);
// create a new layer repaint, and push its index on the stack
- mLayerStack.push_back(mLayerReorderers.size());
- mLayerReorderers.emplace_back(layerWidth, layerHeight, repaintRect, beginLayerOp, renderNode);
+ mLayerStack.push_back(mLayerBuilders.size());
+ auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight,
+ repaintRect, beginLayerOp, renderNode);
+ mLayerBuilders.push_back(newFbo);
}
-void FrameReorderer::restoreForLayer() {
+void FrameBuilder::restoreForLayer() {
// restore canvas, and pop finished layer off of the stack
mCanvasState.restore();
mLayerStack.pop_back();
@@ -613,7 +621,7 @@
// TODO: defer time rejection (when bounds become empty) + tests
// Option - just skip layers with no bounds at playback + defer?
-void FrameReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
+void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) {
uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
@@ -658,7 +666,7 @@
&op, nullptr);
}
-void FrameReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
+void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) {
const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
int finishedLayerIndex = mLayerStack.back();
@@ -671,7 +679,7 @@
beginLayerOp.localMatrix,
beginLayerOp.localClip,
beginLayerOp.paint,
- &mLayerReorderers[finishedLayerIndex].offscreenBuffer);
+ &(mLayerBuilders[finishedLayerIndex]->offscreenBuffer));
BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
if (bakedOpState) {
@@ -681,12 +689,12 @@
// Layer won't be drawn - delete its drawing batches to prevent it from doing any work
// TODO: need to prevent any render work from being done
// - create layerop earlier for reject purposes?
- mLayerReorderers[finishedLayerIndex].clear();
+ mLayerBuilders[finishedLayerIndex]->clear();
return;
}
}
-void FrameReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
+void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));
boundsTransform.multiply(op.localMatrix);
@@ -721,7 +729,7 @@
currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
}
-void FrameReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
+void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
diff --git a/libs/hwui/FrameReorderer.h b/libs/hwui/FrameBuilder.h
similarity index 89%
rename from libs/hwui/FrameReorderer.h
rename to libs/hwui/FrameBuilder.h
index 562e6a1..3ba73f0 100644
--- a/libs/hwui/FrameReorderer.h
+++ b/libs/hwui/FrameBuilder.h
@@ -19,7 +19,7 @@
#include "BakedOpState.h"
#include "CanvasState.h"
#include "DisplayList.h"
-#include "LayerReorderer.h"
+#include "LayerBuilder.h"
#include "RecordedOp.h"
#include <vector>
@@ -42,7 +42,7 @@
* Resolves final drawing state for each operation (including clip, alpha and matrix), and then
* reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
* from the LayerUpdateQueue, or temporary layers created by saveLayer operations in the
- * draw stream) will create different reorder contexts, each in its own LayerReorderer.
+ * draw stream) will create different reorder contexts, each in its own LayerBuilder.
*
* Then the prepared or 'baked' drawing commands can be issued by calling the templated
* replayBakedOps() function, which will dispatch them (including any created merged op collections)
@@ -52,13 +52,13 @@
* This class is also the authoritative source for traversing RenderNodes, both for standard op
* traversal within a DisplayList, and for out of order RenderNode traversal for Z and projection.
*/
-class FrameReorderer : public CanvasStateClient {
+class FrameBuilder : public CanvasStateClient {
public:
- FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+ FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
- virtual ~FrameReorderer() {}
+ virtual ~FrameBuilder() {}
/**
* replayBakedOps() is templated based on what class will receive ops being replayed.
@@ -98,8 +98,8 @@
// Relay through layers in reverse order, since layers
// later in the list will be drawn by earlier ones
- for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
- LayerReorderer& layer = mLayerReorderers[i];
+ for (int i = mLayerBuilders.size() - 1; i >= 1; i--) {
+ LayerBuilder& layer = *(mLayerBuilders[i]);
if (layer.renderNode) {
// cached HW layer - can't skip layer if empty
renderer.startRepaintLayer(layer.offscreenBuffer, layer.repaintRect);
@@ -112,15 +112,15 @@
}
}
- const LayerReorderer& fbo0 = mLayerReorderers[0];
+ const LayerBuilder& fbo0 = *(mLayerBuilders[0]);
renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect);
fbo0.replayBakedOpsImpl((void*)&renderer, unmergedReceivers, mergedReceivers);
renderer.endFrame(fbo0.repaintRect);
}
void dump() const {
- for (auto&& layer : mLayerReorderers) {
- layer.dump();
+ for (auto&& layer : mLayerBuilders) {
+ layer->dump();
}
}
@@ -143,7 +143,7 @@
const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
void restoreForLayer();
- LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
+ LayerBuilder& currentLayer() { return *(mLayerBuilders[mLayerStack.back()]); }
BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
return BakedOpState::tryConstruct(mAllocator, *mCanvasState.writableSnapshot(), recordedOp);
@@ -173,7 +173,7 @@
BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
/**
- * Declares all FrameReorderer::deferXXXXOp() methods for every RecordedOp type.
+ * Declares all FrameBuilder::deferXXXXOp() methods for every RecordedOp type.
*
* These private methods are called from within deferImpl to defer each individual op
* type differently.
@@ -183,17 +183,17 @@
#undef X
// List of every deferred layer's render state. Replayed in reverse order to render a frame.
- std::vector<LayerReorderer> mLayerReorderers;
+ std::vector<LayerBuilder*> mLayerBuilders;
/*
- * Stack of indices within mLayerReorderers representing currently active layers. If drawing
+ * Stack of indices within mLayerBuilders representing currently active layers. If drawing
* layerA within a layerB, will contain, in order:
* - 0 (representing FBO 0, always present)
* - layerB's index
* - layerA's index
*
- * Note that this doesn't vector doesn't always map onto all values of mLayerReorderers. When a
- * layer is finished deferring, it will still be represented in mLayerReorderers, but it's index
+ * Note that this doesn't vector doesn't always map onto all values of mLayerBuilders. When a
+ * layer is finished deferring, it will still be represented in mLayerBuilders, but it's index
* won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
* ops added to it.
*/
diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp
new file mode 100644
index 0000000..4fb5701
--- /dev/null
+++ b/libs/hwui/GpuMemoryTracker.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "utils/StringUtils.h"
+#include "Texture.h"
+
+#include <cutils/compiler.h>
+#include <GpuMemoryTracker.h>
+#include <utils/Trace.h>
+#include <array>
+#include <sstream>
+#include <unordered_set>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+pthread_t gGpuThread = 0;
+
+#define NUM_TYPES static_cast<int>(GpuObjectType::TypeCount)
+
+const char* TYPE_NAMES[] = {
+ "Texture",
+ "OffscreenBuffer",
+ "Layer",
+};
+
+struct TypeStats {
+ int totalSize = 0;
+ int count = 0;
+};
+
+static std::array<TypeStats, NUM_TYPES> gObjectStats;
+static std::unordered_set<GpuMemoryTracker*> gObjectSet;
+
+void GpuMemoryTracker::notifySizeChanged(int newSize) {
+ int delta = newSize - mSize;
+ mSize = newSize;
+ gObjectStats[static_cast<int>(mType)].totalSize += delta;
+}
+
+void GpuMemoryTracker::startTrackingObject() {
+ auto result = gObjectSet.insert(this);
+ LOG_ALWAYS_FATAL_IF(!result.second,
+ "startTrackingObject() on %p failed, already being tracked!", this);
+ gObjectStats[static_cast<int>(mType)].count++;
+}
+
+void GpuMemoryTracker::stopTrackingObject() {
+ size_t removed = gObjectSet.erase(this);
+ LOG_ALWAYS_FATAL_IF(removed != 1,
+ "stopTrackingObject removed %zd, is %p not being tracked?",
+ removed, this);
+ gObjectStats[static_cast<int>(mType)].count--;
+}
+
+void GpuMemoryTracker::onGLContextCreated() {
+ LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a GL thread? "
+ "current = %lu, gl thread = %lu", pthread_self(), gGpuThread);
+ gGpuThread = pthread_self();
+}
+
+void GpuMemoryTracker::onGLContextDestroyed() {
+ gGpuThread = 0;
+ if (CC_UNLIKELY(gObjectSet.size() > 0)) {
+ std::stringstream os;
+ dump(os);
+ ALOGE("%s", os.str().c_str());
+ LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size());
+ }
+}
+
+void GpuMemoryTracker::dump() {
+ std::stringstream strout;
+ dump(strout);
+ ALOGD("%s", strout.str().c_str());
+}
+
+void GpuMemoryTracker::dump(std::ostream& stream) {
+ for (int type = 0; type < NUM_TYPES; type++) {
+ const TypeStats& stats = gObjectStats[type];
+ stream << TYPE_NAMES[type];
+ stream << " is using " << SizePrinter{stats.totalSize};
+ stream << ", count = " << stats.count;
+ stream << std::endl;
+ }
+}
+
+int GpuMemoryTracker::getInstanceCount(GpuObjectType type) {
+ return gObjectStats[static_cast<int>(type)].count;
+}
+
+int GpuMemoryTracker::getTotalSize(GpuObjectType type) {
+ return gObjectStats[static_cast<int>(type)].totalSize;
+}
+
+void GpuMemoryTracker::onFrameCompleted() {
+ if (ATRACE_ENABLED()) {
+ char buf[128];
+ for (int type = 0; type < NUM_TYPES; type++) {
+ snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]);
+ const TypeStats& stats = gObjectStats[type];
+ ATRACE_INT(buf, stats.totalSize);
+ snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]);
+ ATRACE_INT(buf, stats.count);
+ }
+ }
+
+ std::vector<const Texture*> freeList;
+ for (const auto& obj : gObjectSet) {
+ if (obj->objectType() == GpuObjectType::Texture) {
+ const Texture* texture = static_cast<Texture*>(obj);
+ if (texture->cleanup) {
+ ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u",
+ texture->id(), texture->width(), texture->height());
+ freeList.push_back(texture);
+ }
+ }
+ }
+ for (auto& texture : freeList) {
+ const_cast<Texture*>(texture)->deleteTexture();
+ delete texture;
+ }
+}
+
+} // namespace uirenderer
+} // namespace android;
diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h
new file mode 100644
index 0000000..851aeae
--- /dev/null
+++ b/libs/hwui/GpuMemoryTracker.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <cutils/log.h>
+#include <pthread.h>
+#include <ostream>
+
+namespace android {
+namespace uirenderer {
+
+extern pthread_t gGpuThread;
+
+#define ASSERT_GPU_THREAD() LOG_ALWAYS_FATAL_IF( \
+ !pthread_equal(gGpuThread, pthread_self()), \
+ "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \
+ "!= gpu thread %lu", this, static_cast<int>(mType), mSize, \
+ pthread_self(), gGpuThread)
+
+enum class GpuObjectType {
+ Texture = 0,
+ OffscreenBuffer,
+ Layer,
+
+ TypeCount,
+};
+
+class GpuMemoryTracker {
+public:
+ GpuObjectType objectType() { return mType; }
+ int objectSize() { return mSize; }
+
+ static void onGLContextCreated();
+ static void onGLContextDestroyed();
+ static void dump();
+ static void dump(std::ostream& stream);
+ static int getInstanceCount(GpuObjectType type);
+ static int getTotalSize(GpuObjectType type);
+ static void onFrameCompleted();
+
+protected:
+ GpuMemoryTracker(GpuObjectType type) : mType(type) {
+ ASSERT_GPU_THREAD();
+ startTrackingObject();
+ }
+
+ ~GpuMemoryTracker() {
+ notifySizeChanged(0);
+ stopTrackingObject();
+ }
+
+ void notifySizeChanged(int newSize);
+
+private:
+ void startTrackingObject();
+ void stopTrackingObject();
+
+ int mSize = 0;
+ GpuObjectType mType;
+};
+
+} // namespace uirenderer
+} // namespace android;
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 8c46450..522aa96 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -110,9 +110,7 @@
void GradientCache::operator()(GradientCacheEntry&, Texture*& texture) {
if (texture) {
- const uint32_t size = texture->width * texture->height * bytesPerPixel();
- mSize -= size;
-
+ mSize -= texture->objectSize();
texture->deleteTexture();
delete texture;
}
@@ -167,18 +165,16 @@
getGradientInfo(colors, count, info);
Texture* texture = new Texture(Caches::getInstance());
- texture->width = info.width;
- texture->height = 2;
texture->blend = info.hasAlpha;
texture->generation = 1;
// Asume the cache is always big enough
- const uint32_t size = texture->width * texture->height * bytesPerPixel();
+ const uint32_t size = info.width * 2 * bytesPerPixel();
while (getSize() + size > mMaxSize) {
mCache.removeOldest();
}
- generateTexture(colors, positions, texture);
+ generateTexture(colors, positions, info.width, 2, texture);
mSize += size;
mCache.put(gradient, texture);
@@ -231,10 +227,10 @@
dst += 4 * sizeof(float);
}
-void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* texture) {
- const uint32_t width = texture->width;
+void GradientCache::generateTexture(uint32_t* colors, float* positions,
+ const uint32_t width, const uint32_t height, Texture* texture) {
const GLsizei rowBytes = width * bytesPerPixel();
- uint8_t pixels[rowBytes * texture->height];
+ uint8_t pixels[rowBytes * height];
static ChannelSplitter gSplitters[] = {
&android::uirenderer::GradientCache::splitToBytes,
@@ -277,17 +273,13 @@
memcpy(pixels + rowBytes, pixels, rowBytes);
- glGenTextures(1, &texture->id);
- Caches::getInstance().textureState().bindTexture(texture->id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
if (mUseFloatTexture) {
// We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0,
- GL_RGBA, GL_FLOAT, pixels);
+ texture->upload(width, height, GL_RGBA16F, GL_RGBA, GL_FLOAT, pixels);
} else {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+ texture->upload(width, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
}
texture->setFilter(GL_LINEAR);
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 7534c5d..b762ca7 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -143,7 +143,8 @@
Texture* addLinearGradient(GradientCacheEntry& gradient,
uint32_t* colors, float* positions, int count);
- void generateTexture(uint32_t* colors, float* positions, Texture* texture);
+ void generateTexture(uint32_t* colors, float* positions,
+ const uint32_t width, const uint32_t height, Texture* texture);
struct GradientInfo {
uint32_t width;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 0fe20ad..8369266 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -36,7 +36,8 @@
namespace uirenderer {
Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight)
- : state(State::Uncached)
+ : GpuMemoryTracker(GpuObjectType::Layer)
+ , state(State::Uncached)
, caches(Caches::getInstance())
, renderState(renderState)
, texture(caches)
@@ -45,8 +46,8 @@
// preserves the old inc/dec ref locations. This should be changed...
incStrong(nullptr);
renderTarget = GL_TEXTURE_2D;
- texture.width = layerWidth;
- texture.height = layerHeight;
+ texture.mWidth = layerWidth;
+ texture.mHeight = layerHeight;
renderState.registerLayer(this);
}
@@ -54,10 +55,9 @@
renderState.unregisterLayer(this);
SkSafeUnref(colorFilter);
- if (stencil || fbo || texture.id) {
- renderState.requireGLContext();
+ if (stencil || fbo || texture.mId) {
removeFbo();
- deleteTexture();
+ texture.deleteTexture();
}
delete[] mesh;
@@ -65,7 +65,7 @@
void Layer::onGlContextLost() {
removeFbo();
- deleteTexture();
+ texture.deleteTexture();
}
uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
@@ -179,8 +179,8 @@
}
void Layer::bindTexture() const {
- if (texture.id) {
- caches.textureState().bindTexture(renderTarget, texture.id);
+ if (texture.mId) {
+ caches.textureState().bindTexture(renderTarget, texture.mId);
}
}
@@ -191,28 +191,22 @@
}
void Layer::generateTexture() {
- if (!texture.id) {
- glGenTextures(1, &texture.id);
- }
-}
-
-void Layer::deleteTexture() {
- if (texture.id) {
- texture.deleteTexture();
- texture.id = 0;
+ if (!texture.mId) {
+ glGenTextures(1, &texture.mId);
}
}
void Layer::clearTexture() {
- caches.textureState().unbindTexture(texture.id);
- texture.id = 0;
+ caches.textureState().unbindTexture(texture.mId);
+ texture.mId = 0;
}
void Layer::allocateTexture() {
#if DEBUG_LAYERS
ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
#endif
- if (texture.id) {
+ if (texture.mId) {
+ texture.updateSize(getWidth(), getHeight(), GL_RGBA);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e90f055..e00ae66 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -24,6 +24,7 @@
#include <memory>
#include <GLES2/gl2.h>
+#include <GpuMemoryTracker.h>
#include <ui/Region.h>
@@ -54,7 +55,7 @@
/**
* A layer has dimensions and is backed by an OpenGL texture or FBO.
*/
-class Layer : public VirtualLightRefBase {
+class Layer : public VirtualLightRefBase, GpuMemoryTracker {
public:
enum class Type {
Texture,
@@ -94,8 +95,8 @@
regionRect.set(bounds.leftTop().x, bounds.leftTop().y,
bounds.rightBottom().x, bounds.rightBottom().y);
- const float texX = 1.0f / float(texture.width);
- const float texY = 1.0f / float(texture.height);
+ const float texX = 1.0f / float(texture.mWidth);
+ const float texY = 1.0f / float(texture.mHeight);
const float height = layer.getHeight();
texCoords.set(
regionRect.left * texX, (height - regionRect.top) * texY,
@@ -112,11 +113,11 @@
void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom);
inline uint32_t getWidth() const {
- return texture.width;
+ return texture.mWidth;
}
inline uint32_t getHeight() const {
- return texture.height;
+ return texture.mHeight;
}
/**
@@ -131,8 +132,7 @@
bool resize(const uint32_t width, const uint32_t height);
void setSize(uint32_t width, uint32_t height) {
- texture.width = width;
- texture.height = height;
+ texture.updateSize(width, height, texture.format());
}
ANDROID_API void setPaint(const SkPaint* paint);
@@ -201,7 +201,7 @@
}
inline GLuint getTextureId() const {
- return texture.id;
+ return texture.id();
}
inline Texture& getTexture() {
@@ -263,7 +263,6 @@
void bindTexture() const;
void generateTexture();
void allocateTexture();
- void deleteTexture();
/**
* When the caller frees the texture itself, the caller
diff --git a/libs/hwui/LayerReorderer.cpp b/libs/hwui/LayerBuilder.cpp
similarity index 95%
rename from libs/hwui/LayerReorderer.cpp
rename to libs/hwui/LayerBuilder.cpp
index 9a17e93..7170d4f 100644
--- a/libs/hwui/LayerReorderer.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "LayerReorderer.h"
+#include "LayerBuilder.h"
#include "BakedOpState.h"
#include "RenderNode.h"
@@ -53,7 +53,7 @@
void dump() const {
ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
- this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
+ this, mBatchId, mMerging, (int) mOps.size(), RECT_ARGS(mBounds));
}
protected:
batchid_t mBatchId;
@@ -202,7 +202,7 @@
int mClipSideFlags;
};
-LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
+LayerBuilder::LayerBuilder(uint32_t width, uint32_t height,
const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
: width(width)
, height(height)
@@ -214,7 +214,7 @@
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
-void LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
+void LayerBuilder::locateInsertIndex(int batchId, const Rect& clippedBounds,
BatchBase** targetBatch, size_t* insertBatchIndex) const {
for (int i = mBatches.size() - 1; i >= 0; i--) {
BatchBase* overBatch = mBatches[i];
@@ -237,11 +237,11 @@
}
}
-void LayerReorderer::deferLayerClear(const Rect& rect) {
+void LayerBuilder::deferLayerClear(const Rect& rect) {
mClearRects.push_back(rect);
}
-void LayerReorderer::flushLayerClears(LinearAllocator& allocator) {
+void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
if (CC_UNLIKELY(!mClearRects.empty())) {
const int vertCount = mClearRects.size() * 4;
// put the verts in the frame allocator, since
@@ -269,13 +269,11 @@
verts, vertCount);
BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
&viewportClip, bounds, *op);
-
-
deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
}
}
-void LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
+void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator,
BakedOpState* op, batchid_t batchId) {
if (batchId != OpBatchType::CopyToLayer) {
// if first op after one or more unclipped saveLayers, flush the layer clears
@@ -300,7 +298,7 @@
}
}
-void LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
+void LayerBuilder::deferMergeableOp(LinearAllocator& allocator,
BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
if (batchId != OpBatchType::CopyToLayer) {
// if first op after one or more unclipped saveLayers, flush the layer clears
@@ -332,7 +330,7 @@
}
}
-void LayerReorderer::replayBakedOpsImpl(void* arg,
+void LayerBuilder::replayBakedOpsImpl(void* arg,
BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const {
ATRACE_NAME("flush drawing commands");
for (const BatchBase* batch : mBatches) {
@@ -355,8 +353,8 @@
}
}
-void LayerReorderer::dump() const {
- ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
+void LayerBuilder::dump() const {
+ ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p",
this, width, height, offscreenBuffer, beginLayerOp, renderNode);
for (const BatchBase* batch : mBatches) {
batch->dump();
diff --git a/libs/hwui/LayerReorderer.h b/libs/hwui/LayerBuilder.h
similarity index 87%
rename from libs/hwui/LayerReorderer.h
rename to libs/hwui/LayerBuilder.h
index 83cda81..99968e1 100644
--- a/libs/hwui/LayerReorderer.h
+++ b/libs/hwui/LayerBuilder.h
@@ -18,6 +18,7 @@
#include "ClipArea.h"
#include "Rect.h"
+#include "utils/Macros.h"
#include <vector>
#include <unordered_map>
@@ -66,15 +67,17 @@
* Stores the deferred render operations and state used to compute ordering
* for a single FBO/layer.
*/
-class LayerReorderer {
+class LayerBuilder {
+// Prevent copy/assign because users may stash pointer to offscreenBuffer and viewportClip
+PREVENT_COPY_AND_ASSIGN(LayerBuilder);
public:
- // Create LayerReorderer for Fbo0
- LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
- : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
+ // Create LayerBuilder for Fbo0
+ LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect)
+ : LayerBuilder(width, height, repaintRect, nullptr, nullptr) {};
- // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
+ // Create LayerBuilder for an offscreen layer, where beginLayerOp is present for a
// saveLayer, renderNode is present for a HW layer.
- LayerReorderer(uint32_t width, uint32_t height,
+ LayerBuilder(uint32_t width, uint32_t height,
const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
// iterate back toward target to see if anything drawn since should overlap the new op
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index b117754..f5681ce 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -112,7 +112,6 @@
layer->bindTexture();
layer->setFilter(GL_NEAREST);
layer->setWrap(GL_CLAMP_TO_EDGE, false);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
#if DEBUG_LAYERS
dump();
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index be612d2..5b1a854 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -42,7 +42,7 @@
LayerUpdateQueue() {}
void enqueueLayerWithDamage(RenderNode* renderNode, Rect dirty);
void clear();
- const std::vector<Entry> entries() const { return mEntries; }
+ const std::vector<Entry>& entries() const { return mEntries; }
private:
std::vector<Entry> mEntries;
};
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 92b758d..0cd763d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <GpuMemoryTracker.h>
#include "OpenGLRenderer.h"
#include "DeferredDisplayList.h"
@@ -200,6 +201,7 @@
#if DEBUG_MEMORY_USAGE
mCaches.dumpMemoryUsage();
+ GPUMemoryTracker::dump();
#else
if (Properties::debugLevel & kDebugMemory) {
mCaches.dumpMemoryUsage();
@@ -1497,7 +1499,7 @@
.setMeshTexturedUnitQuad(texture->uvMapper)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height))
+ .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height()))
.build();
renderGlop(glop);
}
@@ -1601,10 +1603,10 @@
if (!texture) return;
const AutoTexture autoCleanup(texture);
- Rect uv(std::max(0.0f, src.left / texture->width),
- std::max(0.0f, src.top / texture->height),
- std::min(1.0f, src.right / texture->width),
- std::min(1.0f, src.bottom / texture->height));
+ Rect uv(std::max(0.0f, src.left / texture->width()),
+ std::max(0.0f, src.top / texture->height()),
+ std::min(1.0f, src.right / texture->width()),
+ std::min(1.0f, src.bottom / texture->height()));
const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
@@ -1977,7 +1979,7 @@
.setMeshTexturedUnitQuad(nullptr)
.setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height))
+ .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height()))
.build();
renderGlop(glop);
}
@@ -2316,7 +2318,7 @@
void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y,
const SkPaint* paint) {
- if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) {
+ if (quickRejectSetupScissor(x, y, x + texture->width(), y + texture->height())) {
return;
}
@@ -2326,7 +2328,7 @@
.setMeshTexturedUnitQuad(nullptr)
.setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRect(Rect(x, y, x + texture->width, y + texture->height))
+ .setModelViewMapUnitToRect(Rect(x, y, x + texture->width(), y + texture->height()))
.build();
renderGlop(glop);
}
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 06ea55a..bfabc1d 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -185,7 +185,7 @@
void PathCache::removeTexture(PathTexture* texture) {
if (texture) {
- const uint32_t size = texture->width * texture->height;
+ const uint32_t size = texture->width() * texture->height();
// If there is a pending task we must wait for it to return
// before attempting our cleanup
@@ -209,9 +209,7 @@
ALOGD("Shape deleted, size = %d", size);
}
- if (texture->id) {
- Caches::getInstance().textureState().deleteTexture(texture->id);
- }
+ texture->deleteTexture();
delete texture;
}
}
@@ -248,8 +246,7 @@
drawPath(path, paint, bitmap, left, top, offset, width, height);
PathTexture* texture = new PathTexture(Caches::getInstance(),
- left, top, offset, width, height,
- path->getGenerationID());
+ left, top, offset, path->getGenerationID());
generateTexture(entry, &bitmap, texture);
return texture;
@@ -262,7 +259,7 @@
// Note here that we upload to a texture even if it's bigger than mMaxSize.
// Such an entry in mCache will only be temporary, since it will be evicted
// immediately on trim, or on any other Path entering the cache.
- uint32_t size = texture->width * texture->height;
+ uint32_t size = texture->width() * texture->height();
mSize += size;
PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d",
texture->id, size, mSize);
@@ -280,24 +277,8 @@
void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
ATRACE_NAME("Upload Path Texture");
- SkAutoLockPixels alp(bitmap);
- if (!bitmap.readyToDraw()) {
- ALOGE("Cannot generate texture from bitmap");
- return;
- }
-
- glGenTextures(1, &texture->id);
-
- Caches::getInstance().textureState().bindTexture(texture->id);
- // Textures are Alpha8
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
- texture->blend = true;
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
-
+ texture->upload(bitmap);
texture->setFilter(GL_LINEAR);
- texture->setWrap(GL_CLAMP_TO_EDGE);
}
///////////////////////////////////////////////////////////////////////////////
@@ -320,16 +301,12 @@
texture->left = left;
texture->top = top;
texture->offset = offset;
- texture->width = width;
- texture->height = height;
if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
SkBitmap* bitmap = new SkBitmap();
drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height);
t->setResult(bitmap);
} else {
- texture->width = 0;
- texture->height = 0;
t->setResult(nullptr);
}
}
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 302e9f8..18f380f 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -61,13 +61,11 @@
*/
struct PathTexture: public Texture {
PathTexture(Caches& caches, float left, float top,
- float offset, int width, int height, int generation)
+ float offset, int generation)
: Texture(caches)
, left(left)
, top(top)
, offset(offset) {
- this->width = width;
- this->height = height;
this->generation = generation;
}
PathTexture(Caches& caches, int generation)
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 612cdfd..8e4a3df 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -47,11 +47,11 @@
class DisplayListCanvas;
class DisplayListOp;
class OpenGLRenderer;
-class OpReorderer;
class Rect;
class SkiaShader;
#if HWUI_NEW_OPS
+class FrameBuilder;
class OffscreenBuffer;
struct RenderNodeOp;
typedef OffscreenBuffer layer_t;
@@ -87,7 +87,7 @@
*/
class RenderNode : public VirtualLightRefBase {
friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
-friend class FrameReorderer;
+friend class FrameBuilder;
public:
enum DirtyPropertyMask {
GENERIC = 1 << 1,
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 130cc80..976f775 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -162,15 +162,15 @@
mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
}
-SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr,
- const SkPaint* paint, SaveFlags flags) {
+SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) {
SkRect rect;
- if (rectPtr) {
- rect = *rectPtr;
- } else if(!mCanvas->getClipBounds(&rect)) {
+ if (saveLayerRec.fBounds) {
+ rect = *saveLayerRec.fBounds;
+ } else if (!mCanvas->getClipBounds(&rect)) {
rect = SkRect::MakeEmpty();
}
- mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags);
+ mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
+ (SkCanvas::SaveFlags) SaveLayerFlagsToSaveFlags(saveLayerRec.fSaveLayerFlags));
return SkCanvas::kNoLayer_SaveLayerStrategy;
}
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index 0089fb5..e342d19 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -47,7 +47,7 @@
virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
virtual void willSave() override;
- virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+ virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
virtual void willRestore() override;
virtual void didConcat(const SkMatrix&) override;
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 83652c6..6f4a683 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -57,7 +57,7 @@
}
static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
- caches->textureState().bindTexture(texture->id);
+ caches->textureState().bindTexture(texture->id());
texture->setWrapST(wrapS, wrapT);
}
@@ -219,8 +219,8 @@
outData->bitmapSampler = (*textureUnit)++;
- const float width = outData->bitmapTexture->width;
- const float height = outData->bitmapTexture->height;
+ const float width = outData->bitmapTexture->width();
+ const float height = outData->bitmapTexture->height();
description->hasBitmap = true;
if (!caches.extensions().hasNPot()
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 51f1652..f1e28b7 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -187,13 +187,10 @@
texture = new ShadowTexture(caches);
texture->left = shadow.penX;
texture->top = shadow.penY;
- texture->width = shadow.width;
- texture->height = shadow.height;
texture->generation = 0;
texture->blend = true;
const uint32_t size = shadow.width * shadow.height;
- texture->bitmapSize = size;
// Don't even try to cache a bitmap that's bigger than the cache
if (size < mMaxSize) {
@@ -202,15 +199,11 @@
}
}
- glGenTextures(1, &texture->id);
-
- caches.textureState().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
+ texture->upload(GL_ALPHA, shadow.width, shadow.height,
GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image);
-
texture->setFilter(GL_LINEAR);
texture->setWrap(GL_CLAMP_TO_EDGE);
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 5195b45..8a6b28d 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -14,14 +14,29 @@
* limitations under the License.
*/
-#include <utils/Log.h>
-
#include "Caches.h"
#include "Texture.h"
+#include "utils/TraceUtils.h"
+
+#include <utils/Log.h>
+
+#include <SkCanvas.h>
namespace android {
namespace uirenderer {
+static int bytesPerPixel(GLint glFormat) {
+ switch (glFormat) {
+ case GL_ALPHA:
+ return 1;
+ case GL_RGB:
+ return 3;
+ case GL_RGBA:
+ default:
+ return 4;
+ }
+}
+
void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
GLenum renderTarget) {
@@ -32,7 +47,7 @@
mWrapT = wrapT;
if (bindTexture) {
- mCaches.textureState().bindTexture(renderTarget, id);
+ mCaches.textureState().bindTexture(renderTarget, mId);
}
glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
@@ -50,7 +65,7 @@
mMagFilter = mag;
if (bindTexture) {
- mCaches.textureState().bindTexture(renderTarget, id);
+ mCaches.textureState().bindTexture(renderTarget, mId);
}
if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
@@ -60,8 +75,189 @@
}
}
-void Texture::deleteTexture() const {
- mCaches.textureState().deleteTexture(id);
+void Texture::deleteTexture() {
+ mCaches.textureState().deleteTexture(mId);
+ mId = 0;
+}
+
+bool Texture::updateSize(uint32_t width, uint32_t height, GLint format) {
+ if (mWidth == width && mHeight == height && mFormat == format) {
+ return false;
+ }
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ notifySizeChanged(mWidth * mHeight * bytesPerPixel(mFormat));
+ return true;
+}
+
+void Texture::upload(GLint internalformat, uint32_t width, uint32_t height,
+ GLenum format, GLenum type, const void* pixels) {
+ bool needsAlloc = updateSize(width, height, internalformat);
+ if (!needsAlloc && !pixels) {
+ return;
+ }
+ mCaches.textureState().activateTexture(0);
+ if (!mId) {
+ glGenTextures(1, &mId);
+ needsAlloc = true;
+ }
+ mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
+ if (needsAlloc) {
+ glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
+ format, type, pixels);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
+ format, type, pixels);
+ }
+}
+
+static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
+ GLsizei width, GLsizei height, const GLvoid * data) {
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
+ const bool useStride = stride != width
+ && Caches::getInstance().extensions().hasUnpackRowLength();
+ if ((stride == width) || useStride) {
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ }
+
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
+ }
+
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
+ } else {
+ // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+ // if the stride doesn't match the width
+
+ GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
+ if (!temp) return;
+
+ uint8_t * pDst = (uint8_t *)temp;
+ uint8_t * pSrc = (uint8_t *)data;
+ for (GLsizei i = 0; i < height; i++) {
+ memcpy(pDst, pSrc, width * bpp);
+ pDst += width * bpp;
+ pSrc += stride * bpp;
+ }
+
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
+ }
+
+ free(temp);
+ }
+}
+
+static void uploadSkBitmapToTexture(const SkBitmap& bitmap,
+ bool resize, GLenum format, GLenum type) {
+ uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(),
+ bitmap.width(), bitmap.height(), bitmap.getPixels());
+}
+
+static void colorTypeToGlFormatAndType(SkColorType colorType,
+ GLint* outFormat, GLint* outType) {
+ switch (colorType) {
+ case kAlpha_8_SkColorType:
+ *outFormat = GL_ALPHA;
+ *outType = GL_UNSIGNED_BYTE;
+ break;
+ case kRGB_565_SkColorType:
+ *outFormat = GL_RGB;
+ *outType = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ // ARGB_4444 and Index_8 are both upconverted to RGBA_8888
+ case kARGB_4444_SkColorType:
+ case kIndex_8_SkColorType:
+ case kN32_SkColorType:
+ *outFormat = GL_RGBA;
+ *outType = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
+ break;
+ }
+}
+
+void Texture::upload(const SkBitmap& bitmap) {
+ SkAutoLockPixels alp(bitmap);
+
+ if (!bitmap.readyToDraw()) {
+ ALOGE("Cannot generate texture from bitmap");
+ return;
+ }
+
+ ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
+
+ // We could also enable mipmapping if both bitmap dimensions are powers
+ // of 2 but we'd have to deal with size changes. Let's keep this simple
+ const bool canMipMap = mCaches.extensions().hasNPot();
+
+ // If the texture had mipmap enabled but not anymore,
+ // force a glTexImage2D to discard the mipmap levels
+ bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
+
+ if (!mId) {
+ glGenTextures(1, &mId);
+ needsAlloc = true;
+ }
+
+ GLint format, type;
+ colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type);
+
+ if (updateSize(bitmap.width(), bitmap.height(), format)) {
+ needsAlloc = true;
+ }
+
+ blend = !bitmap.isOpaque();
+ mCaches.textureState().bindTexture(mId);
+
+ if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType
+ || bitmap.colorType() == kIndex_8_SkColorType)) {
+ SkBitmap rgbaBitmap;
+ rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight,
+ bitmap.alphaType()));
+ rgbaBitmap.eraseColor(0);
+
+ SkCanvas canvas(rgbaBitmap);
+ canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
+
+ uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type);
+ } else {
+ uploadSkBitmapToTexture(bitmap, needsAlloc, format, type);
+ }
+
+ if (canMipMap) {
+ mipMap = bitmap.hasHardwareMipMap();
+ if (mipMap) {
+ glGenerateMipmap(GL_TEXTURE_2D);
+ }
+ }
+
+ if (mFirstFilter) {
+ setFilter(GL_NEAREST);
+ }
+
+ if (mFirstWrap) {
+ setWrap(GL_CLAMP_TO_EDGE);
+ }
+}
+
+void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint format) {
+ mId = id;
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ // We're wrapping an existing texture, so don't double count this memory
+ notifySizeChanged(0);
}
}; // namespace uirenderer
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 1c544b9..4e8e6dc 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -17,20 +17,27 @@
#ifndef ANDROID_HWUI_TEXTURE_H
#define ANDROID_HWUI_TEXTURE_H
+#include "GpuMemoryTracker.h"
+
#include <GLES2/gl2.h>
+#include <SkBitmap.h>
namespace android {
namespace uirenderer {
class Caches;
class UvMapper;
+class Layer;
/**
* Represents an OpenGL texture.
*/
-class Texture {
+class Texture : public GpuMemoryTracker {
public:
- Texture(Caches& caches) : mCaches(caches) { }
+ Texture(Caches& caches)
+ : GpuMemoryTracker(GpuObjectType::Texture)
+ , mCaches(caches)
+ { }
virtual ~Texture() { }
@@ -53,12 +60,55 @@
/**
* Convenience method to call glDeleteTextures() on this texture's id.
*/
- void deleteTexture() const;
+ void deleteTexture();
/**
- * Name of the texture.
+ * Sets the width, height, and format of the texture along with allocating
+ * the texture ID. Does nothing if the width, height, and format are already
+ * the requested values.
+ *
+ * The image data is undefined after calling this.
*/
- GLuint id = 0;
+ void resize(uint32_t width, uint32_t height, GLint format) {
+ upload(format, width, height, format, GL_UNSIGNED_BYTE, nullptr);
+ }
+
+ /**
+ * Updates this Texture with the contents of the provided SkBitmap,
+ * also setting the appropriate width, height, and format. It is not necessary
+ * to call resize() prior to this.
+ *
+ * Note this does not set the generation from the SkBitmap.
+ */
+ void upload(const SkBitmap& source);
+
+ /**
+ * Basically glTexImage2D/glTexSubImage2D.
+ */
+ void upload(GLint internalformat, uint32_t width, uint32_t height,
+ GLenum format, GLenum type, const void* pixels);
+
+ /**
+ * Wraps an existing texture.
+ */
+ void wrap(GLuint id, uint32_t width, uint32_t height, GLint format);
+
+ GLuint id() const {
+ return mId;
+ }
+
+ uint32_t width() const {
+ return mWidth;
+ }
+
+ uint32_t height() const {
+ return mHeight;
+ }
+
+ GLint format() const {
+ return mFormat;
+ }
+
/**
* Generation of the backing bitmap,
*/
@@ -68,14 +118,6 @@
*/
bool blend = false;
/**
- * Width of the backing bitmap.
- */
- uint32_t width = 0;
- /**
- * Height of the backing bitmap.
- */
- uint32_t height = 0;
- /**
* Indicates whether this texture should be cleaned up after use.
*/
bool cleanup = false;
@@ -100,6 +142,19 @@
void* isInUse = nullptr;
private:
+ // TODO: Temporarily grant private access to Layer, remove once
+ // Layer can be de-tangled from being a dual-purpose render target
+ // and external texture wrapper
+ friend class Layer;
+
+ // Returns true if the size changed, false if it was the same
+ bool updateSize(uint32_t width, uint32_t height, GLint format);
+
+ GLuint mId = 0;
+ uint32_t mWidth = 0;
+ uint32_t mHeight = 0;
+ GLint mFormat = 0;
+
/**
* Last wrap modes set on this texture.
*/
@@ -120,7 +175,7 @@
class AutoTexture {
public:
- AutoTexture(const Texture* texture)
+ AutoTexture(Texture* texture)
: texture(texture) {}
~AutoTexture() {
if (texture && texture->cleanup) {
@@ -129,7 +184,7 @@
}
}
- const Texture *const texture;
+ Texture* const texture;
}; // class AutoTexture
}; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 21901cf..31bfa3a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -166,7 +166,8 @@
if (canCache) {
texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
- Caches::getInstance().textureState().generateTexture(bitmap, texture, false);
+ texture->generation = bitmap->getGenerationID();
+ texture->upload(*bitmap);
mSize += size;
TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d",
@@ -179,7 +180,8 @@
} else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
// Texture was in the cache but is dirty, re-upload
// TODO: Re-adjust the cache size if the bitmap's dimensions have changed
- Caches::getInstance().textureState().generateTexture(bitmap, texture, true);
+ texture->upload(*bitmap);
+ texture->generation = bitmap->getGenerationID();
}
return texture;
@@ -204,7 +206,8 @@
const uint32_t size = bitmap->rowBytes() * bitmap->height();
texture = new Texture(Caches::getInstance());
texture->bitmapSize = size;
- Caches::getInstance().textureState().generateTexture(bitmap, texture, false);
+ texture->upload(*bitmap);
+ texture->generation = bitmap->getGenerationID();
texture->cleanup = true;
}
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 191c8a8..463450c 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -25,6 +25,7 @@
#include "Debug.h"
#include <vector>
+#include <unordered_map>
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 56cb104..793df92 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -407,8 +407,9 @@
float canvasScaleY = 1.0f;
if (mCanvasMatrix.getSkewX() == 0 && mCanvasMatrix.getSkewY() == 0) {
// Only use the scale value when there's no skew or rotation in the canvas matrix.
- canvasScaleX = mCanvasMatrix.getScaleX();
- canvasScaleY = mCanvasMatrix.getScaleY();
+ // TODO: Add a cts test for drawing VD on a canvas with negative scaling factors.
+ canvasScaleX = fabs(mCanvasMatrix.getScaleX());
+ canvasScaleY = fabs(mCanvasMatrix.getScaleY());
}
int scaledWidth = (int) (mBounds.width() * canvasScaleX);
int scaledHeight = (int) (mBounds.height() * canvasScaleY);
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index d2685da..8ba4761 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -111,11 +111,11 @@
CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount)
: mTexture(Caches::getInstance())
+ , mWidth(width)
+ , mHeight(height)
, mFormat(format)
, mMaxQuadCount(maxQuadCount)
, mCaches(Caches::getInstance()) {
- mTexture.width = width;
- mTexture.height = height;
mTexture.blend = true;
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
@@ -160,10 +160,7 @@
delete mPixelBuffer;
mPixelBuffer = nullptr;
}
- if (mTexture.id) {
- mCaches.textureState().deleteTexture(mTexture.id);
- mTexture.id = 0;
- }
+ mTexture.deleteTexture();
mDirty = false;
mCurrentQuad = 0;
}
@@ -183,22 +180,9 @@
mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight());
}
- if (!mTexture.id) {
- glGenTextures(1, &mTexture.id);
-
- mCaches.textureState().bindTexture(mTexture.id);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- // Initialize texture dimensions
- glTexImage2D(GL_TEXTURE_2D, 0, mFormat, getWidth(), getHeight(), 0,
- mFormat, GL_UNSIGNED_BYTE, nullptr);
-
- const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
+ mTexture.resize(mWidth, mHeight, mFormat);
+ mTexture.setFilter(getLinearFiltering() ? GL_LINEAR : GL_NEAREST);
+ mTexture.setWrap(GL_CLAMP_TO_EDGE);
}
bool CacheTexture::upload() {
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 6dabc76..5510666 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -92,11 +92,11 @@
bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
inline uint16_t getWidth() const {
- return mTexture.width;
+ return mWidth;
}
inline uint16_t getHeight() const {
- return mTexture.height;
+ return mHeight;
}
inline GLenum getFormat() const {
@@ -122,7 +122,7 @@
GLuint getTextureId() {
allocatePixelBuffer();
- return mTexture.id;
+ return mTexture.id();
}
inline bool isDirty() const {
@@ -183,6 +183,7 @@
PixelBuffer* mPixelBuffer = nullptr;
Texture mTexture;
+ uint32_t mWidth, mHeight;
GLenum mFormat;
bool mLinearFiltering = false;
bool mDirty = false;
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
index 227b640..98c94df 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.cpp
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -34,29 +34,22 @@
OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
uint32_t viewportWidth, uint32_t viewportHeight)
- : renderState(renderState)
+ : GpuMemoryTracker(GpuObjectType::OffscreenBuffer)
+ , renderState(renderState)
, viewportWidth(viewportWidth)
, viewportHeight(viewportHeight)
, texture(caches) {
- texture.width = computeIdealDimension(viewportWidth);
- texture.height = computeIdealDimension(viewportHeight);
+ uint32_t width = computeIdealDimension(viewportWidth);
+ uint32_t height = computeIdealDimension(viewportHeight);
+ texture.resize(width, height, GL_RGBA);
texture.blend = true;
-
- caches.textureState().activateTexture(0);
- glGenTextures(1, &texture.id);
- caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);
-
- texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D);
+ texture.setWrap(GL_CLAMP_TO_EDGE);
// not setting filter on texture, since it's set when drawing, based on transform
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
Rect OffscreenBuffer::getTextureCoordinates() {
- const float texX = 1.0f / float(texture.width);
- const float texY = 1.0f / float(texture.height);
+ const float texX = 1.0f / static_cast<float>(texture.width());
+ const float texY = 1.0f / static_cast<float>(texture.height());
return Rect(0, viewportHeight * texY, viewportWidth * texX, 0);
}
@@ -69,8 +62,8 @@
size_t count;
const android::Rect* rects = safeRegion.getArray(&count);
- const float texX = 1.0f / float(texture.width);
- const float texY = 1.0f / float(texture.height);
+ const float texX = 1.0f / float(texture.width());
+ const float texY = 1.0f / float(texture.height());
FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
TextureVertex* mesh = &meshVector[0];
@@ -157,8 +150,8 @@
OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer,
const uint32_t width, const uint32_t height) {
RenderState& renderState = layer->renderState;
- if (layer->texture.width == OffscreenBuffer::computeIdealDimension(width)
- && layer->texture.height == OffscreenBuffer::computeIdealDimension(height)) {
+ if (layer->texture.width() == OffscreenBuffer::computeIdealDimension(width)
+ && layer->texture.height() == OffscreenBuffer::computeIdealDimension(height)) {
// resize in place
layer->viewportWidth = width;
layer->viewportHeight = height;
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
index 2d8d529..94155ef 100644
--- a/libs/hwui/renderstate/OffscreenBufferPool.h
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -17,10 +17,10 @@
#ifndef ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
#define ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
+#include <GpuMemoryTracker.h>
#include "Caches.h"
#include "Texture.h"
#include "utils/Macros.h"
-
#include <ui/Region.h>
#include <set>
@@ -40,7 +40,7 @@
* viewport bounds, since textures are always allocated with width / height as a multiple of 64, for
* the purpose of improving reuse.
*/
-class OffscreenBuffer {
+class OffscreenBuffer : GpuMemoryTracker {
public:
OffscreenBuffer(RenderState& renderState, Caches& caches,
uint32_t viewportWidth, uint32_t viewportHeight);
@@ -58,7 +58,7 @@
static uint32_t computeIdealDimension(uint32_t dimension);
- uint32_t getSizeInBytes() { return texture.width * texture.height * 4; }
+ uint32_t getSizeInBytes() { return texture.objectSize(); }
RenderState& renderState;
@@ -124,8 +124,8 @@
Entry(OffscreenBuffer* layer)
: layer(layer)
- , width(layer->texture.width)
- , height(layer->texture.height) {
+ , width(layer->texture.width())
+ , height(layer->texture.height()) {
}
static int compare(const Entry& lhs, const Entry& rhs);
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 4fa8200..b6dba02 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <GpuMemoryTracker.h>
#include "renderstate/RenderState.h"
#include "renderthread/CanvasContext.h"
#include "renderthread/EglManager.h"
#include "utils/GLUtils.h"
-
#include <algorithm>
namespace android {
@@ -40,6 +40,8 @@
void RenderState::onGLContextCreated() {
LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
"State object lifecycle not managed correctly");
+ GpuMemoryTracker::onGLContextCreated();
+
mBlend = new Blend();
mMeshState = new MeshState();
mScissor = new Scissor();
@@ -106,6 +108,8 @@
mScissor = nullptr;
delete mStencil;
mStencil = nullptr;
+
+ GpuMemoryTracker::onGLContextDestroyed();
}
void RenderState::flush(Caches::FlushMode mode) {
@@ -205,17 +209,6 @@
}
}
-void RenderState::requireGLContext() {
- assertOnGLThread();
- LOG_ALWAYS_FATAL_IF(!mRenderThread.eglManager().hasEglContext(),
- "No GL context!");
-}
-
-void RenderState::assertOnGLThread() {
- pthread_t curr = pthread_self();
- LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!");
-}
-
class DecStrongTask : public renderthread::RenderTask {
public:
DecStrongTask(VirtualLightRefBase* object) : mObject(object) {}
@@ -231,7 +224,11 @@
};
void RenderState::postDecStrong(VirtualLightRefBase* object) {
- mRenderThread.queue(new DecStrongTask(object));
+ if (pthread_equal(mThreadId, pthread_self())) {
+ object->decStrong(nullptr);
+ } else {
+ mRenderThread.queue(new DecStrongTask(object));
+ }
}
///////////////////////////////////////////////////////////////////////////////
@@ -310,7 +307,7 @@
texture.texture->setFilter(texture.filter, true, false, texture.target);
}
- mCaches->textureState().bindTexture(texture.target, texture.texture->id);
+ mCaches->textureState().bindTexture(texture.target, texture.texture->id());
meshState().enableTexCoordsVertexArray();
meshState().bindTexCoordsVertexPointer(force, vertices.texCoord, vertices.stride);
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index dcd5ea6..e5d3e79 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -86,8 +86,6 @@
mRegisteredContexts.erase(context);
}
- void requireGLContext();
-
// TODO: This system is a little clunky feeling, this could use some
// more thinking...
void postDecStrong(VirtualLightRefBase* object);
@@ -107,7 +105,6 @@
private:
void interruptForFunctorInvoke();
void resumeFromFunctorInvoke();
- void assertOnGLThread();
RenderState(renderthread::RenderThread& thread);
~RenderState();
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
index 1f50f71..26ebdee 100644
--- a/libs/hwui/renderstate/TextureState.cpp
+++ b/libs/hwui/renderstate/TextureState.cpp
@@ -34,134 +34,6 @@
GL_TEXTURE3
};
-static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp,
- GLsizei width, GLsizei height, const GLvoid * data) {
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, bpp);
- const bool useStride = stride != width
- && Caches::getInstance().extensions().hasUnpackRowLength();
- if ((stride == width) || useStride) {
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
- }
-
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
- } else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
- }
-
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- }
- } else {
- // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
- // if the stride doesn't match the width
-
- GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
- if (!temp) return;
-
- uint8_t * pDst = (uint8_t *)temp;
- uint8_t * pSrc = (uint8_t *)data;
- for (GLsizei i = 0; i < height; i++) {
- memcpy(pDst, pSrc, width * bpp);
- pDst += width * bpp;
- pSrc += stride * bpp;
- }
-
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
- } else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
- }
-
- free(temp);
- }
-}
-
-static void uploadSkBitmapToTexture(const SkBitmap& bitmap,
- bool resize, GLenum format, GLenum type) {
- uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(),
- bitmap.width(), bitmap.height(), bitmap.getPixels());
-}
-
-void TextureState::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) {
- SkAutoLockPixels alp(*bitmap);
-
- if (!bitmap->readyToDraw()) {
- ALOGE("Cannot generate texture from bitmap");
- return;
- }
-
- ATRACE_FORMAT("Upload %ux%u Texture", bitmap->width(), bitmap->height());
-
- // We could also enable mipmapping if both bitmap dimensions are powers
- // of 2 but we'd have to deal with size changes. Let's keep this simple
- const bool canMipMap = Caches::getInstance().extensions().hasNPot();
-
- // If the texture had mipmap enabled but not anymore,
- // force a glTexImage2D to discard the mipmap levels
- const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
- bitmap->height() != int(texture->height) ||
- (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap());
-
- if (!regenerate) {
- glGenTextures(1, &texture->id);
- }
-
- texture->generation = bitmap->getGenerationID();
- texture->width = bitmap->width();
- texture->height = bitmap->height();
-
- bindTexture(texture->id);
-
- switch (bitmap->colorType()) {
- case kAlpha_8_SkColorType:
- uploadSkBitmapToTexture(*bitmap, resize, GL_ALPHA, GL_UNSIGNED_BYTE);
- texture->blend = true;
- break;
- case kRGB_565_SkColorType:
- uploadSkBitmapToTexture(*bitmap, resize, GL_RGB, GL_UNSIGNED_SHORT_5_6_5);
- texture->blend = false;
- break;
- case kN32_SkColorType:
- uploadSkBitmapToTexture(*bitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE);
- // Do this after calling getPixels() to make sure Skia's deferred
- // decoding happened
- texture->blend = !bitmap->isOpaque();
- break;
- case kARGB_4444_SkColorType:
- case kIndex_8_SkColorType: {
- SkBitmap rgbaBitmap;
- rgbaBitmap.allocPixels(SkImageInfo::MakeN32(texture->width, texture->height,
- bitmap->alphaType()));
- rgbaBitmap.eraseColor(0);
-
- SkCanvas canvas(rgbaBitmap);
- canvas.drawBitmap(*bitmap, 0.0f, 0.0f, nullptr);
-
- uploadSkBitmapToTexture(rgbaBitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE);
- texture->blend = !bitmap->isOpaque();
- break;
- }
- default:
- ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType());
- break;
- }
-
- if (canMipMap) {
- texture->mipMap = bitmap->hasHardwareMipMap();
- if (texture->mipMap) {
- glGenerateMipmap(GL_TEXTURE_2D);
- }
- }
-
- if (!regenerate) {
- texture->setFilter(GL_NEAREST);
- texture->setWrap(GL_CLAMP_TO_EDGE);
- }
-}
-
TextureState::TextureState()
: mTextureUnit(0) {
glActiveTexture(kTextureUnits[0]);
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
index 3a2b85a..ec94d7e 100644
--- a/libs/hwui/renderstate/TextureState.h
+++ b/libs/hwui/renderstate/TextureState.h
@@ -76,13 +76,6 @@
*/
void unbindTexture(GLuint texture);
- /**
- * Generates the texture from a bitmap into the specified texture structure.
- *
- * @param regenerate If true, the bitmap data is reuploaded into the texture, but
- * no new texture is generated.
- */
- void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate);
private:
// total number of texture units available for use
static const int kTextureUnitsCount = 4;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index fff8e09..dd48a83 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <GpuMemoryTracker.h>
#include "CanvasContext.h"
#include "AnimationContext.h"
@@ -31,7 +32,7 @@
#include "utils/TimeUtils.h"
#if HWUI_NEW_OPS
-#include "FrameReorderer.h"
+#include "FrameBuilder.h"
#endif
#include <cutils/properties.h>
@@ -338,14 +339,13 @@
mEglManager.damageFrame(frame, dirty);
#if HWUI_NEW_OPS
- FrameReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
+ FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
mRenderNodes, mLightCenter);
mLayerUpdateQueue.clear();
BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
mOpaque, mLightInfo);
// TODO: profiler().draw(mCanvas);
- reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
-
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
bool drew = renderer.didDraw();
#else
@@ -498,6 +498,8 @@
mJankTracker.addFrame(*mCurrentFrameInfo);
mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
+
+ GpuMemoryTracker::onFrameCompleted();
}
// Called by choreographer to do an RT-driven animation
@@ -513,11 +515,11 @@
void CanvasContext::prepareAndDraw(RenderNode* node) {
ATRACE_CALL();
+ nsecs_t vsync = mRenderThread.timeLord().computeFrameTimeNanos();
int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE];
UiFrameInfoBuilder(frameInfo)
.addFlag(FrameInfoFlags::RTAnimation)
- .setVsync(mRenderThread.timeLord().computeFrameTimeNanos(),
- mRenderThread.timeLord().latestVsync());
+ .setVsync(vsync, vsync);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *this);
prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node);
@@ -579,7 +581,7 @@
node->setPropertyFieldsDirty(RenderNode::GENERIC);
#if HWUI_NEW_OPS
- LOG_ALWAYS_FATAL("unsupported");
+ // TODO: support buildLayer
#else
mCanvas->markLayersAsBuildLayers();
mCanvas->flushLayerUpdates();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 43282c9..72c7e4e 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -450,6 +450,11 @@
} else {
fprintf(file, "\nNo caches instance.\n");
}
+#if HWUI_NEW_OPS
+ fprintf(file, "\nPipeline=FrameBuilder\n");
+#else
+ fprintf(file, "\nPipeline=OpenGLRenderer\n");
+#endif
fflush(file);
return nullptr;
}
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 624f3bd..d56693c 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -19,6 +19,10 @@
#include "DeferredLayerUpdater.h"
#include "LayerRenderer.h"
+#include <unistd.h>
+#include <signal.h>
+#include <setjmp.h>
+
namespace android {
namespace uirenderer {
@@ -121,5 +125,41 @@
canvas->drawTextOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
}
+static void defaultCrashHandler() {
+ fprintf(stderr, "RenderThread crashed!");
+}
+
+static jmp_buf gErrJmpBuff;
+static std::function<void()> gCrashHandler = defaultCrashHandler;
+
+static void signalHandler(int sig) {
+ longjmp(gErrJmpBuff, 1);
+}
+
+void TestUtils::setRenderThreadCrashHandler(std::function<void()> crashHandler) {
+ gCrashHandler = crashHandler;
+}
+
+void TestUtils::TestTask::run() {
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = signalHandler;
+
+ if (setjmp(gErrJmpBuff)) {
+ gCrashHandler();
+ return;
+ }
+
+ sigaction(SIGABRT, &act, nullptr);
+
+
+ // RenderState only valid once RenderThread is running, so queried here
+ RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
+
+ renderState.onGLContextCreated();
+ rtCallback(renderthread::RenderThread::getInstance());
+ renderState.onGLContextDestroyed();
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index edde31e..c506bb3 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -173,19 +173,14 @@
typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
+ static void setRenderThreadCrashHandler(std::function<void()> crashHandler);
+
class TestTask : public renderthread::RenderTask {
public:
TestTask(RtCallback rtCallback)
: rtCallback(rtCallback) {}
virtual ~TestTask() {}
- virtual void run() override {
- // RenderState only valid once RenderThread is running, so queried here
- RenderState& renderState = renderthread::RenderThread::getInstance().renderState();
-
- renderState.onGLContextCreated();
- rtCallback(renderthread::RenderThread::getInstance());
- renderState.onGLContextDestroyed();
- };
+ virtual void run() override;
RtCallback rtCallback;
};
@@ -197,6 +192,10 @@
renderthread::RenderThread::getInstance().queueAndWait(&task);
}
+ static bool isRenderThreadRunning() {
+ return renderthread::RenderThread::hasInstance();
+ }
+
static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
static void drawTextToCanvas(TestCanvas* canvas, const char* text,
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 1616a95..02a3950 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <unordered_map>
#include <vector>
+#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
diff --git a/libs/hwui/tests/microbench/FrameReordererBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
similarity index 90%
rename from libs/hwui/tests/microbench/FrameReordererBench.cpp
rename to libs/hwui/tests/microbench/FrameBuilderBench.cpp
index b4c9a36..67c95e2 100644
--- a/libs/hwui/tests/microbench/FrameReordererBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -19,7 +19,7 @@
#include "BakedOpState.h"
#include "BakedOpDispatcher.h"
#include "BakedOpRenderer.h"
-#include "FrameReorderer.h"
+#include "FrameBuilder.h"
#include "LayerUpdateQueue.h"
#include "RecordedOp.h"
#include "RecordingCanvas.h"
@@ -66,9 +66,9 @@
auto nodes = createTestNodeList();
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
nodes, sLightCenter);
- MicroBench::DoNotOptimize(&reorderer);
+ MicroBench::DoNotOptimize(&frameBuilder);
}
StopBenchmarkTiming();
}
@@ -84,11 +84,11 @@
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
nodes, sLightCenter);
BakedOpRenderer renderer(caches, renderState, true, lightInfo);
- reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
MicroBench::DoNotOptimize(&renderer);
}
StopBenchmarkTiming();
@@ -117,10 +117,10 @@
auto nodes = getSyncedSceneNodes(sceneName);
benchmark.StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- FrameReorderer reorderer(sEmptyLayerUpdateQueue,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
nodes, sLightCenter);
- MicroBench::DoNotOptimize(&reorderer);
+ MicroBench::DoNotOptimize(&frameBuilder);
}
benchmark.StopBenchmarkTiming();
}
@@ -136,12 +136,12 @@
benchmark.StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- FrameReorderer reorderer(sEmptyLayerUpdateQueue,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
nodes, sLightCenter);
BakedOpRenderer renderer(caches, renderState, true, lightInfo);
- reorderer.replayBakedOps<BakedOpDispatcher>(renderer);
+ frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
MicroBench::DoNotOptimize(&renderer);
}
benchmark.StopBenchmarkTiming();
diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp
index 3fd822d..0f8e047 100644
--- a/libs/hwui/tests/unit/BakedOpStateTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp
@@ -176,46 +176,50 @@
}
TEST(BakedOpState, tryConstruct) {
- LinearAllocator allocator;
-
Matrix4 translate100x0;
translate100x0.loadTranslate(100, 0, 0);
SkPaint paint;
ClipRect clip(Rect(100, 200));
- {
- RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint);
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
- BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
- EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
- EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
- }
- {
- RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint);
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
- BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
+ LinearAllocator allocator;
+ RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+ EXPECT_NE(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, successOp))
+ << "successOp NOT rejected by clip, so should be constructed";
+ size_t successAllocSize = allocator.usedSize();
+ EXPECT_LE(64u, successAllocSize) << "relatively large alloc for non-rejected op";
- EXPECT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
- EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
- }
+ RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint);
+ EXPECT_EQ(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, rejectOp))
+ << "rejectOp rejected by clip, so should not be constructed";
+
+ // NOTE: this relies on the clip having already been serialized by the op above
+ EXPECT_EQ(successAllocSize, allocator.usedSize()) << "no extra allocation used for rejected op";
}
TEST(BakedOpState, tryShadowOpConstruct) {
+ Matrix4 translate10x20;
+ translate10x20.loadTranslate(10, 20, 0);
+
LinearAllocator allocator;
{
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
+ auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect()); // Note: empty clip
BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
- EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
- EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
+ EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed";
+ EXPECT_EQ(0u, allocator.usedSize()) << "no serialization, even for clip,"
+ "since op is quick rejected based on snapshot clip";
}
{
- auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+ auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
- ASSERT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
- EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
+ ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed";
+ EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op";
+
+ EXPECT_MATRIX_APPROX_EQ(translate10x20, bakedState->computedState.transform);
+ EXPECT_EQ(Rect(100, 200), bakedState->computedState.clippedBounds);
}
}
diff --git a/libs/hwui/tests/unit/CrashHandlerInjector.cpp b/libs/hwui/tests/unit/CrashHandlerInjector.cpp
new file mode 100644
index 0000000..685c264
--- /dev/null
+++ b/libs/hwui/tests/unit/CrashHandlerInjector.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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>
+
+using namespace android::uirenderer;
+
+static void gunitCrashHandler() {
+ FAIL() << "RenderThread fatal exception!";
+}
+
+static void hookError() {
+ TestUtils::setRenderThreadCrashHandler(gunitCrashHandler);
+}
+
+class HookErrorInit {
+public:
+ HookErrorInit() { hookError(); }
+};
+
+static HookErrorInit sInit;
diff --git a/libs/hwui/tests/unit/FrameReordererTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
similarity index 90%
rename from libs/hwui/tests/unit/FrameReordererTests.cpp
rename to libs/hwui/tests/unit/FrameBuilderTests.cpp
index 9d2eb98..bded50a 100644
--- a/libs/hwui/tests/unit/FrameReordererTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -18,7 +18,7 @@
#include <BakedOpState.h>
#include <DeferredLayerUpdater.h>
-#include <FrameReorderer.h>
+#include <FrameBuilder.h>
#include <LayerUpdateQueue.h>
#include <RecordedOp.h>
#include <RecordingCanvas.h>
@@ -113,7 +113,7 @@
class FailRenderer : public TestRendererBase {};
-TEST(FrameReorderer, simple) {
+TEST(FrameBuilder, simple) {
class SimpleTestRenderer : public TestRendererBase {
public:
void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -138,14 +138,14 @@
canvas.drawRect(0, 0, 100, 200, SkPaint());
canvas.drawBitmap(bitmap, 10, 10, nullptr);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
createSyncedNodeList(node), sLightCenter);
SimpleTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
}
-TEST(FrameReorderer, simpleStroke) {
+TEST(FrameBuilder, simpleStroke) {
class SimpleStrokeTestRenderer : public TestRendererBase {
public:
void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -164,14 +164,14 @@
strokedPaint.setStrokeWidth(10);
canvas.drawPoint(50, 50, strokedPaint);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
createSyncedNodeList(node), sLightCenter);
SimpleStrokeTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
}
-TEST(FrameReorderer, simpleRejection) {
+TEST(FrameBuilder, simpleRejection) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -179,14 +179,14 @@
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
FailRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(FrameReorderer, simpleBatching) {
+TEST(FrameBuilder, simpleBatching) {
const int LOOPS = 5;
class SimpleBatchingTestRenderer : public TestRendererBase {
public:
@@ -214,15 +214,15 @@
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SimpleBatchingTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2 * LOOPS, renderer.getIndex())
<< "Expect number of ops = 2 * loop count";
}
-TEST(FrameReorderer, clippedMerging) {
+TEST(FrameBuilder, clippedMerging) {
class ClippedMergingTestRenderer : public TestRendererBase {
public:
void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -255,14 +255,14 @@
canvas.drawBitmap(bitmap, 40, 70, nullptr);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
createSyncedNodeList(node), sLightCenter);
ClippedMergingTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-TEST(FrameReorderer, textMerging) {
+TEST(FrameBuilder, textMerging) {
class TextMergingTestRenderer : public TestRendererBase {
public:
void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -283,14 +283,14 @@
TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
createSyncedNodeList(node), sLightCenter);
TextMergingTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
}
-TEST(FrameReorderer, textStrikethrough) {
+TEST(FrameBuilder, textStrikethrough) {
const int LOOPS = 5;
class TextStrikethroughTestRenderer : public TestRendererBase {
public:
@@ -314,15 +314,15 @@
TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
}
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
createSyncedNodeList(node), sLightCenter);
TextStrikethroughTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2 * LOOPS, renderer.getIndex())
<< "Expect number of ops = 2 * loop count";
}
-RENDERTHREAD_TEST(FrameReorderer, textureLayer) {
+RENDERTHREAD_TEST(FrameBuilder, textureLayer) {
class TextureLayerTestRenderer : public TestRendererBase {
public:
void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -348,14 +348,14 @@
canvas.drawLayer(layerUpdater.get());
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
TextureLayerTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
}
-TEST(FrameReorderer, renderNode) {
+TEST(FrameBuilder, renderNode) {
class RenderNodeTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -393,13 +393,13 @@
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), sLightCenter);
RenderNodeTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(FrameReorderer, clipped) {
+TEST(FrameBuilder, clipped) {
class ClippedTestRenderer : public TestRendererBase {
public:
void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
@@ -416,14 +416,14 @@
canvas.drawBitmap(bitmap, 0, 0, nullptr);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
200, 200, createSyncedNodeList(node), sLightCenter);
ClippedTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(FrameReorderer, saveLayer_simple) {
+TEST(FrameBuilder, saveLayer_simple) {
class SaveLayerSimpleTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -459,14 +459,14 @@
canvas.drawRect(10, 10, 190, 190, SkPaint());
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SaveLayerSimpleTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-TEST(FrameReorderer, saveLayer_nested) {
+TEST(FrameBuilder, saveLayer_nested) {
/* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
* - startTemporaryLayer2, rect2 endLayer2
* - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -531,14 +531,14 @@
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
createSyncedNodeList(node), sLightCenter);
SaveLayerNestedTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
}
-TEST(FrameReorderer, saveLayer_contentRejection) {
+TEST(FrameBuilder, saveLayer_contentRejection) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -551,15 +551,15 @@
canvas.restore();
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
FailRenderer renderer;
// should see no ops, even within the layer, since the layer should be rejected
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(FrameReorderer, saveLayerUnclipped_simple) {
+TEST(FrameBuilder, saveLayerUnclipped_simple) {
class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -594,14 +594,14 @@
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SaveLayerUnclippedSimpleTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-TEST(FrameReorderer, saveLayerUnclipped_mergedClears) {
+TEST(FrameBuilder, saveLayerUnclipped_mergedClears) {
class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -648,10 +648,10 @@
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.restoreToCount(restoreTo);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SaveLayerUnclippedMergedClearsTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex())
<< "Expect 4 copyTos, 4 copyFroms, 1 clear SimpleRects, and 1 rect.";
}
@@ -660,7 +660,7 @@
* - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
* - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
*/
-TEST(FrameReorderer, saveLayerUnclipped_complex) {
+TEST(FrameBuilder, saveLayerUnclipped_complex) {
class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
@@ -710,14 +710,14 @@
canvas.restore();
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
createSyncedNodeList(node), sLightCenter);
SaveLayerUnclippedComplexTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(12, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameReorderer, hwLayer_simple) {
+RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) {
class HwLayerSimpleTestRenderer : public TestRendererBase {
public:
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -768,17 +768,17 @@
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
- FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
syncedNodeList, sLightCenter);
HwLayerSimpleTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(6, renderer.getIndex());
// clean up layer pointer, so we can safely destruct RenderNode
*layerHandle = nullptr;
}
-RENDERTHREAD_TEST(FrameReorderer, hwLayer_complex) {
+RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) {
/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
* - startRepaintLayer(child), rect(grey), endLayer
* - startTemporaryLayer, drawLayer(child), endLayer
@@ -869,10 +869,10 @@
layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
- FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
syncedList, sLightCenter);
HwLayerComplexTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(13, renderer.getIndex());
// clean up layer pointers, so we can safely destruct RenderNodes
@@ -894,7 +894,7 @@
node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
}
-TEST(FrameReorderer, zReorder) {
+TEST(FrameBuilder, zReorder) {
class ZReorderTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -918,14 +918,14 @@
drawOrderedRect(&canvas, 8);
drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
createSyncedNodeList(parent), sLightCenter);
ZReorderTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
};
-TEST(FrameReorderer, projectionReorder) {
+TEST(FrameBuilder, projectionReorder) {
static const int scrollX = 5;
static const int scrollY = 10;
class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -1001,10 +1001,10 @@
canvas.restore();
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
createSyncedNodeList(parent), sLightCenter);
ProjectionReorderTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(3, renderer.getIndex());
}
@@ -1020,7 +1020,7 @@
});
}
-TEST(FrameReorderer, shadow) {
+TEST(FrameBuilder, shadow) {
class ShadowTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1044,14 +1044,14 @@
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), sLightCenter);
ShadowTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex());
}
-TEST(FrameReorderer, shadowSaveLayer) {
+TEST(FrameBuilder, shadowSaveLayer) {
class ShadowSaveLayerTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -1085,14 +1085,14 @@
canvas.restoreToCount(count);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
ShadowSaveLayerTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(5, renderer.getIndex());
}
-RENDERTHREAD_TEST(FrameReorderer, shadowHwLayer) {
+RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) {
class ShadowHwLayerTestRenderer : public TestRendererBase {
public:
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1135,17 +1135,17 @@
auto syncedList = createSyncedNodeList(parent);
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
- FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
syncedList, (Vector3) { 100, 100, 100 });
ShadowHwLayerTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(5, renderer.getIndex());
// clean up layer pointer, so we can safely destruct RenderNode
*layerHandle = nullptr;
}
-TEST(FrameReorderer, shadowLayering) {
+TEST(FrameBuilder, shadowLayering) {
class ShadowLayeringTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1164,10 +1164,10 @@
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), sLightCenter);
ShadowLayeringTestRenderer renderer;
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
@@ -1192,14 +1192,14 @@
canvas.drawRect(0, 0, 100, 100, paint);
});
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
createSyncedNodeList(node), sLightCenter);
PropertyTestRenderer renderer(opValidateCallback);
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
}
-TEST(FrameReorderer, renderPropOverlappingRenderingAlpha) {
+TEST(FrameBuilder, renderPropOverlappingRenderingAlpha) {
testProperty([](RenderProperties& properties) {
properties.setAlpha(0.5f);
properties.setHasOverlappingRendering(false);
@@ -1208,7 +1208,7 @@
});
}
-TEST(FrameReorderer, renderPropClipping) {
+TEST(FrameBuilder, renderPropClipping) {
testProperty([](RenderProperties& properties) {
properties.setClipToBounds(true);
properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -1218,7 +1218,7 @@
});
}
-TEST(FrameReorderer, renderPropRevealClip) {
+TEST(FrameBuilder, renderPropRevealClip) {
testProperty([](RenderProperties& properties) {
properties.mutableRevealClip().set(true, 50, 50, 25);
}, [](const RectOp& op, const BakedOpState& state) {
@@ -1229,7 +1229,7 @@
});
}
-TEST(FrameReorderer, renderPropOutlineClip) {
+TEST(FrameBuilder, renderPropOutlineClip) {
testProperty([](RenderProperties& properties) {
properties.mutableOutline().setShouldClip(true);
properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -1241,7 +1241,7 @@
});
}
-TEST(FrameReorderer, renderPropTransform) {
+TEST(FrameBuilder, renderPropTransform) {
testProperty([](RenderProperties& properties) {
properties.setLeftTopRightBottom(10, 10, 110, 110);
@@ -1334,15 +1334,15 @@
});
auto nodes = createSyncedNodeList(node); // sync before querying height
- FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
SaveLayerAlphaClipTestRenderer renderer(outObservedData);
- reorderer.replayBakedOps<TestDispatcher>(renderer);
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
// assert, since output won't be valid if we haven't seen a save layer triggered
ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
}
-TEST(FrameReorderer, renderPropSaveLayerAlphaClipBig) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaClipBig) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
properties.setTranslationX(10); // offset rendering content
@@ -1358,7 +1358,7 @@
<< "expect content to be translated as part of being clipped";
}
-TEST(FrameReorderer, renderPropSaveLayerAlphaRotate) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaRotate) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
// Translate and rotate the view so that the only visible part is the top left corner of
@@ -1377,7 +1377,7 @@
EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
}
-TEST(FrameReorderer, renderPropSaveLayerAlphaScale) {
+TEST(FrameBuilder, renderPropSaveLayerAlphaScale) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
properties.setPivotX(0);
diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
new file mode 100644
index 0000000..aa1dcb2
--- /dev/null
+++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <GpuMemoryTracker.h>
+
+#include "renderthread/EglManager.h"
+#include "renderthread/RenderThread.h"
+#include "tests/common/TestUtils.h"
+
+#include <utils/StrongPointer.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+class TestGPUObject : public GpuMemoryTracker {
+public:
+ TestGPUObject() : GpuMemoryTracker(GpuObjectType::Texture) {}
+
+ void changeSize(int newSize) {
+ notifySizeChanged(newSize);
+ }
+};
+
+// Other tests may have created a renderthread and EGL context.
+// This will destroy the EGLContext on RenderThread if it exists so that the
+// current thread can spoof being a GPU thread
+static void destroyEglContext() {
+ if (TestUtils::isRenderThreadRunning()) {
+ TestUtils::runOnRenderThread([](RenderThread& thread) {
+ thread.eglManager().destroy();
+ });
+ }
+}
+
+TEST(GpuMemoryTracker, sizeCheck) {
+ destroyEglContext();
+
+ GpuMemoryTracker::onGLContextCreated();
+ ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+ ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
+ {
+ TestGPUObject myObj;
+ ASSERT_EQ(1, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
+ myObj.changeSize(500);
+ ASSERT_EQ(500, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+ myObj.changeSize(1000);
+ ASSERT_EQ(1000, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+ myObj.changeSize(300);
+ ASSERT_EQ(300, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+ }
+ ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture));
+ ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture));
+ GpuMemoryTracker::onGLContextDestroyed();
+}
diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
index e96e9ba..2fd8795 100644
--- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
+++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp
@@ -36,8 +36,8 @@
EXPECT_EQ(49u, layer.viewportWidth);
EXPECT_EQ(149u, layer.viewportHeight);
- EXPECT_EQ(64u, layer.texture.width);
- EXPECT_EQ(192u, layer.texture.height);
+ EXPECT_EQ(64u, layer.texture.width());
+ EXPECT_EQ(192u, layer.texture.height());
EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
});
@@ -100,8 +100,8 @@
ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
EXPECT_EQ(60u, layer->viewportWidth);
EXPECT_EQ(55u, layer->viewportHeight);
- EXPECT_EQ(64u, layer->texture.width);
- EXPECT_EQ(64u, layer->texture.height);
+ EXPECT_EQ(64u, layer->texture.width());
+ EXPECT_EQ(64u, layer->texture.height());
// resized to use different object in pool
auto layer2 = pool.get(thread.renderState(), 128u, 128u);
@@ -110,12 +110,14 @@
ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
EXPECT_EQ(120u, layer2->viewportWidth);
EXPECT_EQ(125u, layer2->viewportHeight);
- EXPECT_EQ(128u, layer2->texture.width);
- EXPECT_EQ(128u, layer2->texture.height);
+ EXPECT_EQ(128u, layer2->texture.width());
+ EXPECT_EQ(128u, layer2->texture.height());
// original allocation now only thing in pool
EXPECT_EQ(1u, pool.getCount());
EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
+
+ pool.putOrDelete(layer2);
});
}
diff --git a/libs/hwui/tests/unit/StringUtilsTests.cpp b/libs/hwui/tests/unit/StringUtilsTests.cpp
index 6b2e265..b60e96c 100644
--- a/libs/hwui/tests/unit/StringUtilsTests.cpp
+++ b/libs/hwui/tests/unit/StringUtilsTests.cpp
@@ -36,3 +36,18 @@
EXPECT_TRUE(collection.has("GL_ext1"));
EXPECT_FALSE(collection.has("GL_ext")); // string present, but not in list
}
+
+TEST(StringUtils, sizePrinter) {
+ std::stringstream os;
+ os << SizePrinter{500};
+ EXPECT_EQ("500.00B", os.str());
+ os.str("");
+ os << SizePrinter{46080};
+ EXPECT_EQ("45.00KiB", os.str());
+ os.str("");
+ os << SizePrinter{5 * 1024 * 1024 + 520 * 1024};
+ EXPECT_EQ("5.51MiB", os.str());
+ os.str("");
+ os << SizePrinter{2147483647};
+ EXPECT_EQ("2048.00MiB", os.str());
+}
diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h
index 055869f..05a3d59 100644
--- a/libs/hwui/utils/StringUtils.h
+++ b/libs/hwui/utils/StringUtils.h
@@ -18,6 +18,8 @@
#include <string>
#include <unordered_set>
+#include <ostream>
+#include <iomanip>
namespace android {
namespace uirenderer {
@@ -34,6 +36,21 @@
static unordered_string_set split(const char* spacedList);
};
+struct SizePrinter {
+ int bytes;
+ friend std::ostream& operator<<(std::ostream& stream, const SizePrinter& d) {
+ static const char* SUFFIXES[] = {"B", "KiB", "MiB"};
+ size_t suffix = 0;
+ double temp = d.bytes;
+ while (temp > 1000 && suffix < 2) {
+ temp /= 1024.0;
+ suffix++;
+ }
+ stream << std::fixed << std::setprecision(2) << temp << SUFFIXES[suffix];
+ return stream;
+ }
+};
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index 05b4a72..dcc4946 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -18,6 +18,7 @@
#include "AnimationContext.h"
#include "DisplayListCanvas.h"
#include "IContextFactory.h"
+#include "RecordingCanvas.h"
#include "RenderNode.h"
#include "SkTypes.h"
#include "gui/BufferQueue.h"
@@ -88,9 +89,11 @@
mProxy->setup(mSize.width(), mSize.height(), 800.0f,
255 * 0.075f, 255 * 0.15f);
mProxy->setLightCenter(lightVector);
- mCanvas.reset(new
- android::uirenderer::DisplayListCanvas(mSize.width(),
- mSize.height()));
+#if HWUI_NEW_OPS
+ mCanvas.reset(new android::uirenderer::RecordingCanvas(mSize.width(), mSize.height()));
+#else
+ mCanvas.reset(new android::uirenderer::DisplayListCanvas(mSize.width(), mSize.height()));
+#endif
}
SkCanvas* prepareToDraw() {
@@ -168,7 +171,11 @@
std::unique_ptr<android::uirenderer::RenderNode> mRootNode;
std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy;
+#if HWUI_NEW_OPS
+ std::unique_ptr<android::uirenderer::RecordingCanvas> mCanvas;
+#else
std::unique_ptr<android::uirenderer::DisplayListCanvas> mCanvas;
+#endif
android::sp<android::IGraphicBufferProducer> mProducer;
android::sp<android::IGraphicBufferConsumer> mConsumer;
android::sp<android::CpuConsumer> mCpuConsumer;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a092408..ea1690f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -51,6 +51,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
/**
* AudioManager provides access to volume and ringer mode control.
@@ -2158,36 +2159,73 @@
}
/**
- * Handler for audio focus events coming from the audio service.
+ * Handler for events (audio focus change, recording config change) coming from the
+ * audio service.
*/
- private final FocusEventHandlerDelegate mAudioFocusEventHandlerDelegate =
- new FocusEventHandlerDelegate();
+ private final ServiceEventHandlerDelegate mServiceEventHandlerDelegate =
+ new ServiceEventHandlerDelegate();
/**
- * Helper class to handle the forwarding of audio focus events to the appropriate listener
+ * Event types
*/
- private class FocusEventHandlerDelegate {
+ private final static int MSSG_FOCUS_CHANGE = 0;
+ private final static int MSSG_RECORDING_CONFIG_CHANGE = 1;
+
+ /**
+ * Helper class to handle the forwarding of audio service events to the appropriate listener
+ */
+ private class ServiceEventHandlerDelegate {
private final Handler mHandler;
- FocusEventHandlerDelegate() {
+ ServiceEventHandlerDelegate() {
Looper looper;
if ((looper = Looper.myLooper()) == null) {
looper = Looper.getMainLooper();
}
if (looper != null) {
- // implement the event handler delegate to receive audio focus events
+ // implement the event handler delegate to receive events from audio service
mHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
- OnAudioFocusChangeListener listener = null;
- synchronized(mFocusListenerLock) {
- listener = findFocusListener((String)msg.obj);
- }
- if (listener != null) {
- Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
- + msg.what + ") for " + msg.obj);
- listener.onAudioFocusChange(msg.what);
+ switch (msg.what) {
+ case MSSG_FOCUS_CHANGE:
+ OnAudioFocusChangeListener listener = null;
+ synchronized(mFocusListenerLock) {
+ listener = findFocusListener((String)msg.obj);
+ }
+ if (listener != null) {
+ Log.d(TAG, "AudioManager dispatching onAudioFocusChange("
+ + msg.what + ") for " + msg.obj);
+ listener.onAudioFocusChange(msg.arg1);
+ }
+ break;
+ case MSSG_RECORDING_CONFIG_CHANGE:
+ // optimizing for the case of a single callback
+ AudioRecordingCallback singleCallback = null;
+ ArrayList<AudioRecordingCallback> multipleCallbacks = null;
+ synchronized(mRecordCallbackLock) {
+ if ((mRecordCallbackList != null)
+ && (mRecordCallbackList.size() != 0)) {
+ if (mRecordCallbackList.size() == 1) {
+ singleCallback = mRecordCallbackList.get(0);
+ } else {
+ multipleCallbacks =
+ new ArrayList<AudioRecordingCallback>(
+ mRecordCallbackList);
+ }
+ }
+ }
+ if (singleCallback != null) {
+ singleCallback.onRecordConfigChanged();
+ } else if (multipleCallbacks != null) {
+ for (int i=0 ; i < multipleCallbacks.size() ; i++) {
+ multipleCallbacks.get(i).onRecordConfigChanged();
+ }
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown event " + msg.what);
}
}
};
@@ -2204,8 +2242,9 @@
private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
public void dispatchAudioFocusChange(int focusChange, String id) {
- Message m = mAudioFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id);
- mAudioFocusEventHandlerDelegate.getHandler().sendMessage(m);
+ final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
+ MSSG_FOCUS_CHANGE/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/, id/*obj*/);
+ mServiceEventHandlerDelegate.getHandler().sendMessage(m);
}
};
@@ -2702,6 +2741,8 @@
}
+ //====================================================================
+ // Audio policy
/**
* @hide
* Register the given {@link AudioPolicy}.
@@ -2754,6 +2795,131 @@
}
+ //====================================================================
+ // Recording configuration
+ /**
+ * @hide
+ * candidate for public API
+ */
+ public static abstract class AudioRecordingCallback {
+ /**
+ * @hide
+ * candidate for public API
+ */
+ public void onRecordConfigChanged() {}
+ }
+
+ /**
+ * @hide
+ * candidate for public API
+ * @param non-null callback
+ */
+ public void registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
+ if (cb == null) {
+ throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
+ }
+ synchronized(mRecordCallbackLock) {
+ // lazy initialization of the list of recording callbacks
+ if (mRecordCallbackList == null) {
+ mRecordCallbackList = new ArrayList<AudioRecordingCallback>();
+ }
+ final int oldCbCount = mRecordCallbackList.size();
+ if (!mRecordCallbackList.contains(cb)) {
+ mRecordCallbackList.add(cb);
+ final int newCbCount = mRecordCallbackList.size();
+ if ((oldCbCount == 0) && (newCbCount > 0)) {
+ // register binder for callbacks
+ final IAudioService service = getService();
+ try {
+ service.registerRecordingCallback(mRecCb);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in registerRecordingCallback", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "attempt to call registerAudioRecordingCallback() on a previously"
+ + "registered callback");
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * candidate for public API
+ * @param non-null callback
+ */
+ public void unregisterAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
+ if (cb == null) {
+ throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
+ }
+ synchronized(mRecordCallbackLock) {
+ if (mRecordCallbackList == null) {
+ return;
+ }
+ final int oldCbCount = mRecordCallbackList.size();
+ if (mRecordCallbackList.remove(cb)) {
+ final int newCbCount = mRecordCallbackList.size();
+ if ((oldCbCount > 0) && (newCbCount == 0)) {
+ // unregister binder for callbacks
+ final IAudioService service = getService();
+ try {
+ service.unregisterRecordingCallback(mRecCb);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in unregisterRecordingCallback", e);
+ }
+ }
+ } else {
+ Log.w(TAG, "attempt to call unregisterAudioRecordingCallback() on a callback"
+ + " already unregistered or never registered");
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * candidate for public API
+ * @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() {
+ final IAudioService service = getService();
+ try {
+ return service.getActiveRecordConfigurations();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to retrieve active record configurations", e);
+ return null;
+ }
+ }
+
+ /**
+ * constants for the recording events, to keep in sync
+ * with frameworks/av/include/media/AudioPolicy.h
+ */
+ /** @hide */
+ public final static int RECORD_CONFIG_EVENT_START = 1;
+ /** @hide */
+ public final static int RECORD_CONFIG_EVENT_STOP = 0;
+
+ /**
+ * All operations on this list are sync'd on mRecordCallbackLock.
+ * List is lazy-initialized in {@link #registerAudioRecordingCallback(AudioRecordingCallback)}.
+ * List can be null.
+ */
+ private List<AudioRecordingCallback> mRecordCallbackList;
+ private final Object mRecordCallbackLock = new Object();
+
+ private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
+
+ public void dispatchRecordingConfigChange() {
+ final Message m = mServiceEventHandlerDelegate.getHandler().obtainMessage(
+ MSSG_RECORDING_CONFIG_CHANGE/*what*/);
+ mServiceEventHandlerDelegate.getHandler().sendMessage(m);
+ }
+
+ };
+
+ //=====================================================================
+
/**
* @hide
* Reload audio settings. This method is called by Settings backup
diff --git a/core/java/android/os/PersistableBundle.aidl b/media/java/android/media/AudioRecordConfiguration.aidl
similarity index 78%
rename from core/java/android/os/PersistableBundle.aidl
rename to media/java/android/media/AudioRecordConfiguration.aidl
index 94e8607..afe912b 100644
--- a/core/java/android/os/PersistableBundle.aidl
+++ b/media/java/android/media/AudioRecordConfiguration.aidl
@@ -1,6 +1,4 @@
-/*
-**
-** Copyright 2014, The Android Open Source Project
+/* Copyright 2016, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -15,6 +13,6 @@
** limitations under the License.
*/
-package android.os;
+package android.media;
-parcelable PersistableBundle cpp_header "binder/PersistableBundle.h";
+parcelable AudioRecordConfiguration;
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordConfiguration.java
new file mode 100644
index 0000000..aefe692
--- /dev/null
+++ b/media/java/android/media/AudioRecordConfiguration.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.media;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ * Candidate for public API, see AudioManager.getActiveRecordConfiguration()
+ *
+ */
+public class AudioRecordConfiguration implements Parcelable {
+
+ private final int mSessionId;
+
+ private final int mClientSource;
+
+ /**
+ * @hide
+ */
+ public AudioRecordConfiguration(int session, int source) {
+ mSessionId = session;
+ mClientSource = source;
+ }
+
+ /**
+ * @return one of AudioSource.MIC, AudioSource.VOICE_UPLINK,
+ * AudioSource.VOICE_DOWNLINK, AudioSource.VOICE_CALL,
+ * AudioSource.CAMCORDER, AudioSource.VOICE_RECOGNITION,
+ * AudioSource.VOICE_COMMUNICATION.
+ */
+ public int getClientAudioSource() { return mClientSource; }
+
+ /**
+ * @return the session number of the recorder.
+ */
+ public int getAudioSessionId() { return mSessionId; }
+
+
+ public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR
+ = new Parcelable.Creator<AudioRecordConfiguration>() {
+ /**
+ * 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
+ */
+ public AudioRecordConfiguration createFromParcel(Parcel p) {
+ return new AudioRecordConfiguration(p);
+ }
+ public AudioRecordConfiguration[] newArray(int size) {
+ return new AudioRecordConfiguration[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mSessionId, mClientSource);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSessionId);
+ dest.writeInt(mClientSource);
+ }
+
+ private AudioRecordConfiguration(Parcel in) {
+ mSessionId = in.readInt();
+ mClientSource = in.readInt();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || !(o instanceof AudioRecordConfiguration)) return false;
+
+ final AudioRecordConfiguration that = (AudioRecordConfiguration) o;
+ return ((mSessionId == that.mSessionId)
+ && (mClientSource == that.mClientSource));
+ }
+}
\ No newline at end of file
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 7bfd7ca..aa0d78d 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -227,7 +227,7 @@
}
/**
- * Handles events for the audio policy manager about dynamic audio policies
+ * Handles events from the audio policy manager about dynamic audio policies
* @see android.media.audiopolicy.AudioPolicy
*/
public interface DynamicPolicyCallback
@@ -267,6 +267,33 @@
}
}
+ /**
+ * Handles events from the audio policy manager about recording events
+ * @see android.media.AudioManager.AudioRecordingCallback
+ */
+ public interface AudioRecordingCallback
+ {
+ void onRecordingConfigurationChanged(int event, int session, int source);
+ }
+
+ private static AudioRecordingCallback sRecordingCallback;
+
+ public static void setRecordingCallback(AudioRecordingCallback cb) {
+ synchronized (AudioSystem.class) {
+ sRecordingCallback = cb;
+ native_register_recording_callback();
+ }
+ }
+
+ private static void recordingCallbackFromNative(int event, int session, int source) {
+ AudioRecordingCallback cb = null;
+ synchronized (AudioSystem.class) {
+ cb = sRecordingCallback;
+ }
+ if (cb != null) {
+ cb.onRecordingConfigurationChanged(event, session, source);
+ }
+ }
/*
* Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...)
@@ -646,6 +673,8 @@
// declare this instance as having a dynamic policy callback handler
private static native final void native_register_dynamic_policy_callback();
+ // declare this instance as having a recording configuration update callback handler
+ private static native final void native_register_recording_callback();
// must be kept in sync with value in include/system/audio.h
public static final int AUDIO_HW_SYNC_INVALID = 0;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index dbb7661..abe92c7 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -20,9 +20,11 @@
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.media.AudioAttributes;
+import android.media.AudioRecordConfiguration;
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
+import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
import android.media.IVolumeController;
import android.media.Rating;
@@ -161,4 +163,10 @@
int setFocusPropertiesForPolicy(int duckingBehavior, in IAudioPolicyCallback pcb);
void setVolumePolicy(in VolumePolicy policy);
+
+ void registerRecordingCallback(in IRecordingConfigDispatcher rcdb);
+
+ oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb);
+
+ AudioRecordConfiguration[] getActiveRecordConfigurations();
}
diff --git a/media/java/android/media/IMediaResourceMonitor.aidl b/media/java/android/media/IMediaResourceMonitor.aidl
new file mode 100644
index 0000000..7b4bc39
--- /dev/null
+++ b/media/java/android/media/IMediaResourceMonitor.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/** {@hide} */
+interface IMediaResourceMonitor
+{
+ oneway void notifyResourceGranted(in int pid, String type, String subType, long value);
+}
+
diff --git a/media/java/android/media/IRecordingConfigDispatcher.aidl b/media/java/android/media/IRecordingConfigDispatcher.aidl
new file mode 100644
index 0000000..a5eb8b9f
--- /dev/null
+++ b/media/java/android/media/IRecordingConfigDispatcher.aidl
@@ -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.media;
+
+/**
+ * AIDL for the RecordingActivity monitor in AudioService to signal audio recording updates.
+ *
+ * {@hide}
+ */
+oneway interface IRecordingConfigDispatcher {
+
+ void dispatchRecordingConfigChange();
+
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 478fd99..f1f8437 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2175,8 +2175,6 @@
int offset, int size, long presentationTimeUs, int flags)
throws CryptoException;
- // The following mode constants MUST stay in sync with their equivalents
- // in media/hardware/CryptoAPI.h !
public static final int CRYPTO_MODE_UNENCRYPTED = 0;
public static final int CRYPTO_MODE_AES_CTR = 1;
public static final int CRYPTO_MODE_AES_CBC = 2;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9bcb5e3..0fba992 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1735,8 +1735,7 @@
CodecProfileLevel[] profileLevels = mParent.profileLevels;
String mime = mParent.getMimeType();
- if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC) ||
- mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_AVC)) {
+ if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC)) {
maxBlocks = 99;
maxBlocksPerSecond = 1485;
maxBps = 64000;
@@ -2090,8 +2089,7 @@
applyMacroBlockLimits(Short.MAX_VALUE, Short.MAX_VALUE,
maxBlocks, maxBlocksPerSecond, blockSize, blockSize,
1 /* widthAlignment */, 1 /* heightAlignment */);
- } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC) ||
- mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_HEVC)) {
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
maxBlocks = 36864;
maxBlocksPerSecond = maxBlocks * 15;
maxBps = 128000;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index a102e51..b2fa0ac 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -92,8 +92,6 @@
public static final String MIMETYPE_VIDEO_H263 = "video/3gpp";
public static final String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
public static final String MIMETYPE_VIDEO_RAW = "video/raw";
- public static final String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
- public static final String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
public static final String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
public static final String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 96c616b..dfe024a 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -17,6 +17,7 @@
package android.media;
import android.content.ContentProviderClient;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -37,6 +38,7 @@
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.sax.Element;
import android.sax.ElementListener;
import android.sax.RootElement;
@@ -328,8 +330,6 @@
// used when scanning the image database so we know whether we have to prune
// old thumbnail files
private int mOriginalCount;
- /** Whether the database had any entries in it before the scan started */
- private boolean mWasEmptyPriorToScan = false;
/** Whether the scanner has set a default sound for the ringer ringtone. */
private boolean mDefaultRingtoneSet;
/** Whether the scanner has set a default sound for the notification ringtone. */
@@ -562,12 +562,29 @@
FileEntry entry = beginFile(path, mimeType, lastModified,
fileSize, isDirectory, noMedia);
+ if (entry == null) {
+ return null;
+ }
+
// if this file was just inserted via mtp, set the rowid to zero
// (even though it already exists in the database), to trigger
// the correct code path for updating its entry
if (mMtpObjectHandle != 0) {
entry.mRowId = 0;
}
+
+ if (entry.mPath != null &&
+ ((!mDefaultNotificationSet &&
+ doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
+ || (!mDefaultRingtoneSet &&
+ doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
+ || (!mDefaultAlarmSet &&
+ doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
+ Log.w(TAG, "forcing rescan of " + entry.mPath +
+ "since ringtone setting didn't finish");
+ scanAlways = true;
+ }
+
// rescan for metadata if file was modified since last scan
if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
if (noMedia) {
@@ -947,6 +964,26 @@
}
Uri result = null;
boolean needToSetSettings = false;
+ // Setting a flag in order not to use bulk insert for the file related with
+ // notifications, ringtones, and alarms, because the rowId of the inserted file is
+ // needed.
+ if (notifications && !mDefaultNotificationSet) {
+ if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
+ needToSetSettings = true;
+ }
+ } else if (ringtones && !mDefaultRingtoneSet) {
+ if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
+ needToSetSettings = true;
+ }
+ } else if (alarms && !mDefaultAlarmSet) {
+ if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
+ needToSetSettings = true;
+ }
+ }
+
if (rowId == 0) {
if (mMtpObjectHandle != 0) {
values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
@@ -958,28 +995,6 @@
}
values.put(Files.FileColumns.FORMAT, format);
}
- // Setting a flag in order not to use bulk insert for the file related with
- // notifications, ringtones, and alarms, because the rowId of the inserted file is
- // needed.
- if (mWasEmptyPriorToScan) {
- if (notifications && !mDefaultNotificationSet) {
- if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
- needToSetSettings = true;
- }
- } else if (ringtones && !mDefaultRingtoneSet) {
- if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
- needToSetSettings = true;
- }
- } else if (alarms && !mDefaultAlarmSet) {
- if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
- needToSetSettings = true;
- }
- }
- }
-
// New file, insert it.
// Directories need to be inserted before the files they contain, so they
// get priority when bulk inserting.
@@ -1049,14 +1064,18 @@
private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
- String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
- settingName);
+ if(wasSettingAlreadySet(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(mContext.getContentResolver(), settingName,
+ Settings.System.putString(cr, settingName,
ContentUris.withAppendedId(uri, rowId).toString());
}
+ Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
}
private int getFileTypeFromDrm(String path) {
@@ -1083,6 +1102,20 @@
}; // end of anonymous MediaScannerClient instance
+ private String settingSetIndicatorName(String base) {
+ return base + "_set";
+ }
+
+ private boolean wasSettingAlreadySet(String name) {
+ ContentResolver cr = mContext.getContentResolver();
+ String indicatorName = settingSetIndicatorName(name);
+ try {
+ return Settings.System.getInt(cr, indicatorName) != 0;
+ } catch (SettingNotFoundException e) {
+ return false;
+ }
+ }
+
private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
Cursor c = null;
String where = null;
@@ -1100,6 +1133,10 @@
selectionArgs = new String[] { "" };
}
+ mDefaultRingtoneSet = wasSettingAlreadySet(Settings.System.RINGTONE);
+ mDefaultNotificationSet = wasSettingAlreadySet(Settings.System.NOTIFICATION_SOUND);
+ mDefaultAlarmSet = wasSettingAlreadySet(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
// accidentally deleting files that are really there (this may happen if the
@@ -1117,7 +1154,6 @@
// with CursorWindow positioning.
long lastId = Long.MIN_VALUE;
Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
- mWasEmptyPriorToScan = true;
while (true) {
selectionArgs[0] = "" + lastId;
@@ -1136,7 +1172,6 @@
if (num == 0) {
break;
}
- mWasEmptyPriorToScan = false;
while (c.moveToNext()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
@@ -1284,7 +1319,7 @@
}
}
- private void postscan(String[] directories) throws RemoteException {
+ private void postscan(final String[] directories) throws RemoteException {
// handle playlists last, after we know what media files are on the storage.
if (mProcessPlaylists) {
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index d24c5e8..4379a99 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -21,6 +21,8 @@
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
+import com.android.internal.util.Preconditions;
+
import java.io.IOException;
/**
@@ -164,12 +166,14 @@
* of the data and speed of the devices.
*
* @param objectHandle handle of the object to read
- * @param offset Start index of reading range.
- * @param size Size of reading range.
+ * @param offset Start index of reading range. It must be a non-negative value at most
+ * 0xffffffff.
+ * @param size Size of reading range. It must be a non-negative value at most 0xffffffff. If
+ * 0xffffffff is specified, the method obtains the full bytes of object.
* @param buffer Array to write data.
* @return Size of bytes that are actually read.
*/
- public int getPartialObject(int objectHandle, int offset, int size, byte[] buffer)
+ public long getPartialObject(int objectHandle, long offset, long size, byte[] buffer)
throws IOException {
return native_get_partial_object(objectHandle, offset, size, buffer);
}
@@ -340,8 +344,8 @@
private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
private native MtpObjectInfo native_get_object_info(int objectHandle);
private native byte[] native_get_object(int objectHandle, int objectSize);
- private native int native_get_partial_object(
- int objectHandle, int offset, int objectSize, byte[] buffer) throws IOException;
+ private native long native_get_partial_object(
+ int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
private native byte[] native_get_thumbnail(int objectHandle);
private native boolean native_delete_object(int objectHandle);
private native long native_get_parent(int objectHandle);
diff --git a/media/java/android/mtp/MtpEvent.java b/media/java/android/mtp/MtpEvent.java
index 6ec16db..dc89a56 100644
--- a/media/java/android/mtp/MtpEvent.java
+++ b/media/java/android/mtp/MtpEvent.java
@@ -18,15 +18,152 @@
/**
* This class encapsulates information about a MTP event.
- * Event constants are defined by the USB-IF MTP specification.
+ * This corresponds to the events described in appendix G of the MTP specification.
*/
public class MtpEvent {
private int mEventCode = MtpConstants.EVENT_UNDEFINED;
+ // Parameters for event. The interpretation of event parameters depends upon mEventCode.
+ private int mParameter1;
+ private int mParameter2;
+ private int mParameter3;
+
/**
* Returns event code of MTP event.
* See the USB-IF MTP specification for the details of event constants.
* @return event code
*/
public int getEventCode() { return mEventCode; }
+
+ /**
+ * Obtains the first event parameter.
+ */
+ public int getParameter1() { return mParameter1; }
+
+ /**
+ * Obtains the second event parameter.
+ */
+ public int getParameter2() { return mParameter2; }
+
+ /**
+ * Obtains the third event parameter.
+ */
+ public int getParameter3() { return mParameter3; }
+
+ /**
+ * Obtains objectHandle event parameter.
+ *
+ * @see MtpConstants#EVENT_OBJECT_ADDED
+ * @see MtpConstants#EVENT_OBJECT_REMOVED
+ * @see MtpConstants#EVENT_OBJECT_INFO_CHANGED
+ * @see MtpConstants#EVENT_REQUEST_OBJECT_TRANSFER
+ * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED
+ * @see MtpConstants#EVENT_OBJECT_REFERENCES_CHANGED
+ */
+ public int getObjectHandle() {
+ switch (mEventCode) {
+ case MtpConstants.EVENT_OBJECT_ADDED:
+ return mParameter1;
+ case MtpConstants.EVENT_OBJECT_REMOVED:
+ return mParameter1;
+ case MtpConstants.EVENT_OBJECT_INFO_CHANGED:
+ return mParameter1;
+ case MtpConstants.EVENT_REQUEST_OBJECT_TRANSFER:
+ return mParameter1;
+ case MtpConstants.EVENT_OBJECT_PROP_CHANGED:
+ return mParameter1;
+ case MtpConstants.EVENT_OBJECT_REFERENCES_CHANGED:
+ return mParameter1;
+ default:
+ throw new IllegalParameterAccess("objectHandle", mEventCode);
+ }
+ }
+
+ /**
+ * Obtains storageID event parameter.
+ *
+ * @see MtpConstants#EVENT_STORE_ADDED
+ * @see MtpConstants#EVENT_STORE_REMOVED
+ * @see MtpConstants#EVENT_STORE_FULL
+ * @see MtpConstants#EVENT_STORAGE_INFO_CHANGED
+ */
+ public int getStorageId() {
+ switch (mEventCode) {
+ case MtpConstants.EVENT_STORE_ADDED:
+ return mParameter1;
+ case MtpConstants.EVENT_STORE_REMOVED:
+ return mParameter1;
+ case MtpConstants.EVENT_STORE_FULL:
+ return mParameter1;
+ case MtpConstants.EVENT_STORAGE_INFO_CHANGED:
+ return mParameter1;
+ default:
+ throw new IllegalParameterAccess("storageID", mEventCode);
+ }
+ }
+
+ /**
+ * Obtains devicePropCode event parameter.
+ *
+ * @see MtpConstants#EVENT_DEVICE_PROP_CHANGED
+ */
+ public int getDevicePropCode() {
+ switch (mEventCode) {
+ case MtpConstants.EVENT_DEVICE_PROP_CHANGED:
+ return mParameter1;
+ default:
+ throw new IllegalParameterAccess("devicePropCode", mEventCode);
+ }
+ }
+
+ /**
+ * Obtains transactionID event parameter.
+ *
+ * @see MtpConstants#EVENT_CAPTURE_COMPLETE
+ */
+ public int getTransactionId() {
+ switch (mEventCode) {
+ case MtpConstants.EVENT_CAPTURE_COMPLETE:
+ return mParameter1;
+ default:
+ throw new IllegalParameterAccess("transactionID", mEventCode);
+ }
+ }
+
+ /**
+ * Obtains objectPropCode event parameter.
+ *
+ * @see MtpConstants#EVENT_OBJECT_PROP_CHANGED
+ * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED
+ */
+ public int getObjectPropCode() {
+ switch (mEventCode) {
+ case MtpConstants.EVENT_OBJECT_PROP_CHANGED:
+ return mParameter2;
+ case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED:
+ return mParameter1;
+ default:
+ throw new IllegalParameterAccess("objectPropCode", mEventCode);
+ }
+ }
+
+ /**
+ * Obtains objectFormatCode event parameter.
+ *
+ * @see MtpConstants#EVENT_OBJECT_PROP_DESC_CHANGED
+ */
+ public int getObjectFormatCode() {
+ switch (mEventCode) {
+ case MtpConstants.EVENT_OBJECT_PROP_DESC_CHANGED:
+ return mParameter2;
+ default:
+ throw new IllegalParameterAccess("objectFormatCode", mEventCode);
+ }
+ }
+
+ private static class IllegalParameterAccess extends UnsupportedOperationException {
+ public IllegalParameterAccess(String propertyName, int eventCode) {
+ super("Cannot obtain " + propertyName + " for the event: " + eventCode + ".");
+ }
+ }
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 49b579c..2004a3a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -85,6 +85,13 @@
jmethodID setNativeObjectLocked;
} gPersistentSurfaceClassInfo;
+static struct {
+ jint Unencrypted;
+ jint AesCtr;
+ jint AesCbc;
+} gCryptoModes;
+
+
struct fields_t {
jfieldID context;
jmethodID postEventFromNativeID;
@@ -94,6 +101,9 @@
jfieldID cryptoInfoKeyID;
jfieldID cryptoInfoIVID;
jfieldID cryptoInfoModeID;
+ jfieldID cryptoInfoPatternID;
+ jfieldID patternEncryptBlocksID;
+ jfieldID patternSkipBlocksID;
};
static fields_t gFields;
@@ -325,11 +335,12 @@
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
+ const CryptoPlugin::Pattern &pattern,
int64_t presentationTimeUs,
uint32_t flags,
AString *errorDetailMsg) {
return mCodec->queueSecureInputBuffer(
- index, offset, subSamples, numSubSamples, key, iv, mode,
+ index, offset, subSamples, numSubSamples, key, iv, mode, pattern,
presentationTimeUs, flags, errorDetailMsg);
}
@@ -1275,7 +1286,26 @@
jbyteArray ivObj =
(jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
- jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+ jint jmode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+ enum CryptoPlugin::Mode mode;
+ if (jmode == gCryptoModes.Unencrypted) {
+ mode = CryptoPlugin::kMode_Unencrypted;
+ } else if (jmode == gCryptoModes.AesCtr) {
+ mode = CryptoPlugin::kMode_AES_CTR;
+ } else if (jmode == gCryptoModes.AesCbc) {
+ mode = CryptoPlugin::kMode_AES_CBC;
+ } else {
+ throwExceptionAsNecessary(env, INVALID_OPERATION);
+ return;
+ }
+
+ jobject patternObj = env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoPatternID);
+
+ CryptoPlugin::Pattern pattern;
+ if (patternObj != NULL) {
+ pattern.mEncryptBlocks = env->GetIntField(patternObj, gFields.patternEncryptBlocksID);
+ pattern.mSkipBlocks = env->GetIntField(patternObj, gFields.patternSkipBlocksID);
+ }
status_t err = OK;
@@ -1360,7 +1390,8 @@
index, offset,
subSamples, numSubSamples,
(const uint8_t *)key, (const uint8_t *)iv,
- (CryptoPlugin::Mode)mode,
+ mode,
+ pattern,
timestampUs,
flags,
&errorDetailMsg);
@@ -1658,6 +1689,22 @@
CHECK(gFields.postEventFromNativeID != NULL);
+ jfieldID field;
+ field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_UNENCRYPTED", "I");
+ CHECK(field != NULL);
+ gCryptoModes.Unencrypted =
+ env->GetStaticIntField(clazz.get(), field);
+
+ field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_AES_CTR", "I");
+ CHECK(field != NULL);
+ gCryptoModes.AesCtr =
+ env->GetStaticIntField(clazz.get(), field);
+
+ field = env->GetStaticFieldID(clazz.get(), "CRYPTO_MODE_AES_CBC", "I");
+ CHECK(field != NULL);
+ gCryptoModes.AesCbc =
+ env->GetStaticIntField(clazz.get(), field);
+
clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo"));
CHECK(clazz.get() != NULL);
@@ -1682,10 +1729,22 @@
gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
CHECK(gFields.cryptoInfoModeID != NULL);
+ gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "pattern",
+ "Landroid/media/MediaCodec$CryptoInfo$Pattern;");
+ CHECK(gFields.cryptoInfoPatternID != NULL);
+
+ clazz.reset(env->FindClass("android/media/MediaCodec$CryptoInfo$Pattern"));
+ CHECK(clazz.get() != NULL);
+
+ gFields.patternEncryptBlocksID = env->GetFieldID(clazz.get(), "mEncryptBlocks", "I");
+ CHECK(gFields.patternEncryptBlocksID != NULL);
+
+ gFields.patternSkipBlocksID = env->GetFieldID(clazz.get(), "mSkipBlocks", "I");
+ CHECK(gFields.patternSkipBlocksID != NULL);
+
clazz.reset(env->FindClass("android/media/MediaCodec$CryptoException"));
CHECK(clazz.get() != NULL);
- jfieldID field;
field = env->GetStaticFieldID(clazz.get(), "ERROR_NO_KEY", "I");
CHECK(field != NULL);
gCryptoErrorCodes.cryptoErrorNoKey =
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 6650cf9..c0c47ef 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -81,6 +81,7 @@
const uint8_t key[16],
const uint8_t iv[16],
CryptoPlugin::Mode mode,
+ const CryptoPlugin::Pattern &pattern,
int64_t presentationTimeUs,
uint32_t flags,
AString *errorDetailMsg);
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 4aa12c2..b1b3b62 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -98,6 +98,9 @@
// MtpEvent fields
static jfieldID field_event_eventCode;
+static jfieldID field_event_parameter1;
+static jfieldID field_event_parameter2;
+static jfieldID field_event_parameter3;
class JavaArrayWriter {
public:
@@ -369,12 +372,12 @@
return nullptr;
}
-static jint
+static jlong
android_mtp_MtpDevice_get_partial_object(JNIEnv *env,
jobject thiz,
jint objectID,
- jint offset,
- jint size,
+ jlong offset,
+ jlong size,
jbyteArray array)
{
if (!array) {
@@ -382,6 +385,22 @@
return -1;
}
+ if (offset < 0 || 0xffffffffL < offset) {
+ jniThrowException(
+ env,
+ "java/lang/IllegalArgumentException",
+ "Offset argument must be a 32-bit unsigned integer.");
+ return -1;
+ }
+
+ if (size < 0 || 0xffffffffL < size) {
+ jniThrowException(
+ env,
+ "java/lang/IllegalArgumentException",
+ "Size argument must be a 32-bit unsigned integer.");
+ return -1;
+ }
+
MtpDevice* const device = get_device_from_object(env, thiz);
if (!device) {
jniThrowException(env, "java/io/IOException", "Failed to obtain MtpDevice.");
@@ -390,16 +409,13 @@
JavaArrayWriter writer(env, array);
uint32_t written_size;
- bool success = device->readPartialObject(
+ const bool success = device->readPartialObject(
objectID, offset, size, &written_size, JavaArrayWriter::writeTo, &writer);
if (!success) {
jniThrowException(env, "java/io/IOException", "Failed to read data.");
return -1;
}
- // Note: assumption here is that a negative value will be treated as unsigned on the Java
- // level.
- // TODO: Make sure that actually holds.
- return static_cast<jint>(written_size);
+ return static_cast<jlong>(written_size);
}
static jbyteArray
@@ -573,13 +589,17 @@
env->ThrowNew(clazz_io_exception, "");
return NULL;
}
- const int eventCode = device->reapEventRequest(seq);
+ uint32_t parameters[3];
+ const int eventCode = device->reapEventRequest(seq, ¶meters);
if (eventCode <= 0) {
env->ThrowNew(clazz_operation_canceled_exception, "");
return NULL;
}
jobject result = env->NewObject(clazz_event, constructor_event);
env->SetIntField(result, field_event_eventCode, eventCode);
+ env->SetIntField(result, field_event_parameter1, static_cast<jint>(parameters[0]));
+ env->SetIntField(result, field_event_parameter2, static_cast<jint>(parameters[1]));
+ env->SetIntField(result, field_event_parameter3, static_cast<jint>(parameters[2]));
return result;
}
@@ -608,7 +628,7 @@
{"native_get_object_info", "(I)Landroid/mtp/MtpObjectInfo;",
(void *)android_mtp_MtpDevice_get_object_info},
{"native_get_object", "(II)[B",(void *)android_mtp_MtpDevice_get_object},
- {"native_get_partial_object", "(III[B)I", (void *)android_mtp_MtpDevice_get_partial_object},
+ {"native_get_partial_object", "(IJJ[B)J", (void *)android_mtp_MtpDevice_get_partial_object},
{"native_get_thumbnail", "(I)[B",(void *)android_mtp_MtpDevice_get_thumbnail},
{"native_delete_object", "(I)Z", (void *)android_mtp_MtpDevice_delete_object},
{"native_get_parent", "(I)J", (void *)android_mtp_MtpDevice_get_parent},
@@ -832,6 +852,21 @@
ALOGE("Can't find MtpObjectInfo.mEventCode");
return -1;
}
+ field_event_parameter1 = env->GetFieldID(clazz, "mParameter1", "I");
+ if (field_event_parameter1 == NULL) {
+ ALOGE("Can't find MtpObjectInfo.mParameter1");
+ return -1;
+ }
+ field_event_parameter2 = env->GetFieldID(clazz, "mParameter2", "I");
+ if (field_event_parameter2 == NULL) {
+ ALOGE("Can't find MtpObjectInfo.mParameter2");
+ return -1;
+ }
+ field_event_parameter3 = env->GetFieldID(clazz, "mParameter3", "I");
+ if (field_event_parameter3 == NULL) {
+ ALOGE("Can't find MtpObjectInfo.mParameter3");
+ return -1;
+ }
clazz_event = (jclass)env->NewGlobalRef(clazz);
clazz = env->FindClass("android/mtp/MtpDevice");
diff --git a/packages/BackupRestoreConfirmation/res/values-de/strings.xml b/packages/BackupRestoreConfirmation/res/values-de/strings.xml
index a2e24e7..e1f797f 100644
--- a/packages/BackupRestoreConfirmation/res/values-de/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-de/strings.xml
@@ -18,19 +18,19 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="backup_confirm_title" msgid="827563724209303345">"Vollständige Sicherung"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Vollständige Wiederherstellung"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"Es wurde eine vollständige Sicherung sämtlicher Daten auf einen verbundenen Desktop-Computer angefordert. Möchten Sie dies zulassen?\n\nWenn Sie die Sicherung nicht selbst angefordert haben, sollten Sie dem Vorgang nicht zustimmen."</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"Es wurde eine vollständige Sicherung sämtlicher Daten auf einen verbundenen Desktop-Computer angefordert. Möchtest du dies zulassen?\n\nWenn du die Sicherung nicht selbst angefordert hast, solltest du dem Vorgang nicht zustimmen."</string>
<string name="allow_backup_button_label" msgid="4217228747769644068">"Meine Daten sichern"</string>
<string name="deny_backup_button_label" msgid="6009119115581097708">"Nicht sichern"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"Es wurde eine vollständige Wiederherstellung aller Daten von einem verbundenen Desktop-Computer angefordert. Möchten Sie dies zulassen?\n\nWenn Sie die Wiederherstellung nicht selbst angefordert haben, sollten Sie dem Vorgang nicht zustimmen. Dadurch werden alle zurzeit auf dem Gerät befindlichen Daten ersetzt!"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"Es wurde eine vollständige Wiederherstellung aller Daten von einem verbundenen Desktop-Computer angefordert. Möchtest du dies zulassen?\n\nWenn du die Wiederherstellung nicht selbst angefordert hast, solltest du dem Vorgang nicht zustimmen. Dadurch werden alle derzeit auf dem Gerät befindlichen Daten ersetzt!"</string>
<string name="allow_restore_button_label" msgid="3081286752277127827">"Meine Daten wiederherstellen"</string>
<string name="deny_restore_button_label" msgid="1724367334453104378">"Nicht wiederherstellen"</string>
- <string name="current_password_text" msgid="8268189555578298067">"Geben Sie Ihr aktuelles Sicherungspasswort unten ein:"</string>
- <string name="device_encryption_restore_text" msgid="1570864916855208992">"Geben Sie Ihr Passwort zur Geräteverschlüsselung unten ein."</string>
- <string name="device_encryption_backup_text" msgid="5866590762672844664">"Geben Sie Ihr Passwort zur Geräteverschlüsselung unten ein. Damit wird auch das Sicherungsarchiv verschlüsselt."</string>
- <string name="backup_enc_password_text" msgid="4981585714795233099">"Geben Sie ein Passwort für die Verschlüsselung der vollständigen Sicherungsdaten ein. Wenn Sie dieses Feld leer lassen, wird Ihr aktuelles Sicherungspasswort verwendet:"</string>
- <string name="backup_enc_password_optional" msgid="1350137345907579306">"Wenn Sie die gesamten Sicherungsdaten verschlüsseln möchten, geben Sie unten ein Passwort ein:"</string>
+ <string name="current_password_text" msgid="8268189555578298067">"Gib dein aktuelles Sicherungspasswort unten ein:"</string>
+ <string name="device_encryption_restore_text" msgid="1570864916855208992">"Gib dein Passwort zur Geräteverschlüsselung unten ein."</string>
+ <string name="device_encryption_backup_text" msgid="5866590762672844664">"Gib dein Passwort zur Geräteverschlüsselung unten ein. Damit wird auch das Sicherungsarchiv verschlüsselt."</string>
+ <string name="backup_enc_password_text" msgid="4981585714795233099">"Gib ein Passwort für die Verschlüsselung der vollständigen Sicherungsdaten ein. Wenn du dieses Feld leer lässt, wird dein aktuelles Sicherungspasswort verwendet:"</string>
+ <string name="backup_enc_password_optional" msgid="1350137345907579306">"Wenn du die gesamten Sicherungsdaten verschlüsseln möchtest, gib unten ein Passwort ein:"</string>
<string name="backup_enc_password_required" msgid="7889652203371654149">"Da Ihr Gerät verschlüsselt ist, muss auch die Sicherung verschlüsselt werden. Geben Sie unten ein Passwort ein:"</string>
- <string name="restore_enc_password_text" msgid="6140898525580710823">"Geben Sie das Passwort unten ein, wenn die Daten für die Wiederherstellung verschlüsselt sind:"</string>
+ <string name="restore_enc_password_text" msgid="6140898525580710823">"Gib das Passwort unten ein, wenn die Daten für die Wiederherstellung verschlüsselt sind:"</string>
<string name="toast_backup_started" msgid="550354281452756121">"Sicherung wird gestartet..."</string>
<string name="toast_backup_ended" msgid="3818080769548726424">"Sicherung abgeschlossen"</string>
<string name="toast_restore_started" msgid="7881679218971277385">"Wiederherstellung wird gestartet..."</string>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index abb464e..6fb8b51 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -56,7 +56,6 @@
public class CaptivePortalLoginActivity extends Activity {
private static final String TAG = "CaptivePortalLogin";
- private static final String DEFAULT_SERVER = "connectivitycheck.gstatic.com";
private static final int SOCKET_TIMEOUT_MS = 10000;
private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
@@ -72,16 +71,14 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
- if (server == null) server = DEFAULT_SERVER;
mCm = ConnectivityManager.from(this);
String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
+ if (url == null) url = mCm.getCaptivePortalServerUrl();
try {
- mURL = url != null ? new URL(url) : new URL("http", server, "/generate_204");
+ mURL = new URL(url);
} catch (MalformedURLException e) {
// System misconfigured, bail out in a way that at least provides network access.
- Log.e(TAG, "Invalid captive portal URL, server=" + server);
+ Log.e(TAG, "Invalid captive portal URL, url=" + url);
done(Result.WANTED_AS_IS);
}
mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
diff --git a/packages/DocumentsUI/res/drawable/ic_root_home.xml b/packages/DocumentsUI/res/drawable/ic_root_home.xml
index 0a258ac..696ee05 100644
--- a/packages/DocumentsUI/res/drawable/ic_root_home.xml
+++ b/packages/DocumentsUI/res/drawable/ic_root_home.xml
@@ -1,15 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+<!--
+Copyright (C) 2015 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
- android:fillColor="#000000"
- android:pathData="M20 6h-8l-2-2H4c-1.1 0-1.99 .9 -1.99 2L2 18c0 1.1 .9 2 2 2h16c1.1 0 2-.9
-2-2V8c0-1.1-.9-2-2-2zm-5 3c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm4
-8h-8v-1c0-1.33 2.67-2 4-2s4 .67 4 2v1z" />
- <path
- android:pathData="M0 0h24v24H0z" />
+ android:fillColor="#FF000000"
+ android:pathData="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
</vector>
diff --git a/packages/DocumentsUI/res/layout/fragment_directory.xml b/packages/DocumentsUI/res/layout/fragment_directory.xml
index f9bbccb..1b5911d 100644
--- a/packages/DocumentsUI/res/layout/fragment_directory.xml
+++ b/packages/DocumentsUI/res/layout/fragment_directory.xml
@@ -23,11 +23,11 @@
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/progress_bar_height"
android:indeterminate="true"
style="@style/TrimmedHorizontalProgressBar"
android:visibility="gone"/>
-
+
<FrameLayout
android:id="@+id/container_message_bar"
android:layout_width="match_parent"
@@ -44,7 +44,7 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone">
-
+
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
@@ -58,9 +58,9 @@
android:layout_height="wrap_content"
android:text="@string/button_retry"
style="?android:attr/buttonBarPositiveButtonStyle" />
-
+
</LinearLayout>
-
+
<!-- This FrameLayout works around b/24189541 -->
<FrameLayout
android:layout_width="match_parent"
@@ -68,6 +68,7 @@
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
+ android:background="@color/window_background"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index cacdf4d..5adb165 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -15,6 +15,9 @@
-->
<resources>
+ <dimen name="grid_container_padding">10dp</dimen>
+ <dimen name="list_container_padding">0dp</dimen>
+
<dimen name="icon_size">40dp</dimen>
<dimen name="root_icon_size">24dp</dimen>
<dimen name="root_icon_margin">0dp</dimen>
@@ -23,6 +26,8 @@
<dimen name="list_item_thumbnail_size">40dp</dimen>
<dimen name="grid_item_icon_size">30dp</dimen>
+ <dimen name="progress_bar_height">4dp</dimen>
+
<dimen name="grid_width">152dp</dimen>
<dimen name="grid_height">176dp</dimen>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 180a48e..be54496 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -176,6 +176,12 @@
return true;
}
+ @Override
+ protected void onDestroy() {
+ mRoots.setOnCacheUpdateListener(null);
+ super.onDestroy();
+ }
+
State buildDefaultState() {
State state = new State();
@@ -518,7 +524,7 @@
@Override
protected void onPostExecute(DocumentInfo result) {
- if (result != null) {
+ if (result != null && !isDestroyed()) {
openContainerDocument(result);
}
}
@@ -630,7 +636,7 @@
@Override
protected void onPostExecute(RootInfo homeRoot) {
- if (homeRoot != null && mHome != null) {
+ if (homeRoot != null && mHome != null && !isDestroyed()) {
// Clear entire backstack and start in new root
mState.onRootChanged(homeRoot);
mSearchManager.update(homeRoot);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 898713f..22e81c6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -601,11 +601,11 @@
throw new IllegalArgumentException("Unsupported layout mode: " + mode);
}
- mRecView.setLayoutManager(layout);
- // TODO: Once b/23691541 is resolved, use a listener within MultiSelectManager instead of
- // imperatively calling this function.
- mSelectionManager.handleLayoutChanged();
+ int pad = getDirectoryPadding(mode);
+ mRecView.setPadding(pad, pad, pad, pad);
// setting layout manager automatically invalidates existing ViewHolders.
+ mRecView.setLayoutManager(layout);
+ mSelectionManager.handleLayoutChanged(); // RecyclerView doesn't do this for us
mIconHelper.setMode(mode);
}
@@ -621,6 +621,20 @@
return columnCount;
}
+ private int getDirectoryPadding(int mode) {
+ switch (mode) {
+ case MODE_GRID:
+ return getResources().getDimensionPixelSize(
+ R.dimen.grid_container_padding);
+ case MODE_LIST:
+ return getResources().getDimensionPixelSize(
+ R.dimen.list_container_padding);
+ case MODE_UNKNOWN:
+ default:
+ throw new IllegalArgumentException("Unsupported layout mode: " + mode);
+ }
+ }
+
@Override
public int getColumnCount() {
return mColumnCount;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java
deleted file mode 100644
index d1f8ff7..0000000
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/EmptyDocumentHolder.java
+++ /dev/null
@@ -1,50 +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.dirlist;
-
-import android.content.Context;
-import android.database.Cursor;
-import android.view.View;
-import android.widget.Space;
-
-import com.android.documentsui.R;
-import com.android.documentsui.State;
-
-final class EmptyDocumentHolder extends DocumentHolder {
- final int mVisibleHeight;
-
- public EmptyDocumentHolder(Context context) {
- super(context, new Space(context));
-
- // Per UX spec, this puts a bigger gap between the folders and documents in the grid.
- mVisibleHeight = context.getResources().getDimensionPixelSize(R.dimen.grid_item_margin) * 2;
- }
-
- public void bind(State state) {
- bind(null, null, state);
- }
-
- @Override
- public void bind(Cursor cursor, String modelId, State state) {
- if (state.derivedMode == State.MODE_GRID) {
- itemView.setMinimumHeight(mVisibleHeight);
- } else {
- itemView.setMinimumHeight(0);
- }
- return;
- }
-}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
index 3ee1d42..2485ad9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/SectionBreakDocumentsAdapterWrapper.java
@@ -18,10 +18,16 @@
import static com.android.internal.util.Preconditions.checkArgument;
+import android.content.Context;
+import android.database.Cursor;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView.AdapterDataObserver;
import android.util.SparseArray;
import android.view.ViewGroup;
+import android.widget.Space;
+
+import com.android.documentsui.R;
+import com.android.documentsui.State;
import java.util.List;
@@ -222,4 +228,33 @@
throw new UnsupportedOperationException();
}
}
+
+ /**
+ * The most elegant transparent blank box that spans N rows ever conceived.
+ */
+ private static final class EmptyDocumentHolder extends DocumentHolder {
+ final int mVisibleHeight;
+
+ public EmptyDocumentHolder(Context context) {
+ super(context, new Space(context));
+
+ // Per UX spec, this puts a bigger gap between the folders and documents in the grid.
+ mVisibleHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.grid_item_margin);
+ }
+
+ public void bind(State state) {
+ bind(null, null, state);
+ }
+
+ @Override
+ public void bind(Cursor cursor, String modelId, State state) {
+ if (state.derivedMode == State.MODE_GRID) {
+ itemView.setMinimumHeight(mVisibleHeight);
+ } else {
+ itemView.setMinimumHeight(0);
+ }
+ return;
+ }
+ }
}
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index dea5fa0..746457f 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -308,31 +308,31 @@
<string name="airplane_mode">Airplane mode</string>
<!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_restart_pattern">Pattern required when you restart device.</string>
+ <string name="kg_prompt_reason_restart_pattern">Pattern required after device restarts</string>
<!-- An explanation text that the pin needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_restart_pin">PIN required when you restart device.</string>
+ <string name="kg_prompt_reason_restart_pin">PIN required after device restarts</string>
<!-- An explanation text that the password needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_restart_password">Password required when you restart device.</string>
+ <string name="kg_prompt_reason_restart_password">Password required after device restarts</string>
<!-- An explanation text that the pattern needs to be solved since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_timeout_pattern">Pattern required for additional security.</string>
+ <string name="kg_prompt_reason_timeout_pattern">Pattern required for additional security</string>
<!-- An explanation text that the pin needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_timeout_pin">PIN required for additional security.</string>
+ <string name="kg_prompt_reason_timeout_pin">PIN required for additional security</string>
<!-- An explanation text that the password needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_timeout_password">Password required for additional security.</string>
+ <string name="kg_prompt_reason_timeout_password">Password required for additional security</string>
<!-- An explanation text that the pattern needs to be solved since profiles have just been switched. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_switch_profiles_pattern">Pattern required when you switch profiles.</string>
+ <string name="kg_prompt_reason_switch_profiles_pattern">Pattern required when you switch profiles</string>
<!-- An explanation text that the pin needs to be entered since profiles have just been switched. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_switch_profiles_pin">PIN required when you switch profiles.</string>
+ <string name="kg_prompt_reason_switch_profiles_pin">PIN required when you switch profiles</string>
<!-- An explanation text that the password needs to be entered since profiles have just been switched. [CHAR LIMIT=80] -->
- <string name="kg_prompt_reason_switch_profiles_password">Password required when you switch profiles.</string>
+ <string name="kg_prompt_reason_switch_profiles_password">Password required when you switch profiles</string>
<!-- An explanation text that the pattern needs to be solved since it hasn't been solved in a while. [CHAR LIMIT=80]-->
<plurals name="kg_prompt_reason_time_pattern">
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index b03871a..64b443b 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -123,39 +123,45 @@
mPendingLockCheck.cancel(false);
}
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
if (entry.length() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
setPasswordEntryInputEnabled(true);
- onPasswordChecked(false /* matched */, 0, false /* not valid - too short */);
+ onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
return;
}
mPendingLockCheck = LockPatternChecker.checkPassword(
mLockPatternUtils,
entry,
- KeyguardUpdateMonitor.getCurrentUser(),
+ userId,
new LockPatternChecker.OnCheckCallback() {
@Override
public void onChecked(boolean matched, int timeoutMs) {
setPasswordEntryInputEnabled(true);
mPendingLockCheck = null;
- onPasswordChecked(matched, timeoutMs, true /* isValidPassword */);
+ onPasswordChecked(userId, matched, timeoutMs,
+ true /* isValidPassword */);
}
});
}
- private void onPasswordChecked(boolean matched, int timeoutMs, boolean isValidPassword) {
+ private void onPasswordChecked(int userId, boolean matched, int timeoutMs,
+ boolean isValidPassword) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
if (matched) {
- mDismissing = true;
- mCallback.reportUnlockAttempt(true, 0);
- mCallback.dismiss(true);
+ mCallback.reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mDismissing = true;
+ mCallback.dismiss(true);
+ }
} else {
if (isValidPassword) {
- mCallback.reportUnlockAttempt(false, timeoutMs);
+ mCallback.reportUnlockAttempt(userId, false, timeoutMs);
if (timeoutMs > 0) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser(), timeoutMs);
+ userId, timeoutMs);
handleAttemptLockout(deadline);
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 29b319f..2a2f5a0 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -227,22 +227,23 @@
mPendingLockCheck.cancel(false);
}
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
mLockPatternView.enableInput();
- onPatternChecked(false, 0, false /* not valid - too short */);
+ onPatternChecked(userId, false, 0, false /* not valid - too short */);
return;
}
mPendingLockCheck = LockPatternChecker.checkPattern(
mLockPatternUtils,
pattern,
- KeyguardUpdateMonitor.getCurrentUser(),
+ userId,
new LockPatternChecker.OnCheckCallback() {
@Override
public void onChecked(boolean matched, int timeoutMs) {
mLockPatternView.enableInput();
mPendingLockCheck = null;
- onPatternChecked(matched, timeoutMs, true);
+ onPatternChecked(userId, matched, timeoutMs, true);
}
});
if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
@@ -250,18 +251,22 @@
}
}
- private void onPatternChecked(boolean matched, int timeoutMs, boolean isValidPattern) {
+ private void onPatternChecked(int userId, boolean matched, int timeoutMs,
+ boolean isValidPattern) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
if (matched) {
- mCallback.reportUnlockAttempt(true, 0);
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
- mCallback.dismiss(true);
+ mCallback.reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
+ mCallback.dismiss(true);
+ }
} else {
mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
if (isValidPattern) {
- mCallback.reportUnlockAttempt(false, timeoutMs);
+ mCallback.reportUnlockAttempt(userId, false, timeoutMs);
if (timeoutMs > 0) {
long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser(), timeoutMs);
+ userId, timeoutMs);
handleAttemptLockout(deadline);
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
index 836c195..232d4d2 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -36,11 +36,12 @@
/**
* Call to report an unlock attempt.
+ * @param userId id of the user whose unlock attempt is recorded.
* @param success set to 'true' if user correctly entered security credentials.
* @param timeoutMs timeout in milliseconds to wait before reattempting an unlock.
* Only nonzero if 'success' is false
*/
- void reportUnlockAttempt(boolean success, int timeoutMs);
+ void reportUnlockAttempt(int userId, boolean success, int timeoutMs);
/**
* Resets the keyguard view.
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index f45b9bd..409f6a7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -205,7 +205,8 @@
if (messageId != 0) {
final String message = mContext.getString(messageId,
- KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
+ KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(
+ KeyguardUpdateMonitor.getCurrentUser()),
timeoutInSeconds);
showDialog(null, message);
}
@@ -249,16 +250,15 @@
showDialog(null, message);
}
- private void reportFailedUnlockAttempt(int timeoutMs) {
+ private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
- final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
+ final int failedAttempts = monitor.getFailedUnlockAttempts(userId) + 1; // +1 for this time
if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
- final int currentUser = KeyguardUpdateMonitor.getCurrentUser();
final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
final int failedAttemptsBeforeWipe =
- dpm.getMaximumFailedPasswordsForWipe(null, currentUser);
+ dpm.getMaximumFailedPasswordsForWipe(null, userId);
final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
(failedAttemptsBeforeWipe - failedAttempts)
@@ -268,9 +268,9 @@
// N attempts. Once we get below the grace period, we post this dialog every time as a
// clear warning until the deletion fires.
// Check which profile has the strictest policy for failed password attempts
- final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(currentUser);
+ final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
int userType = USER_TYPE_PRIMARY;
- if (expiringUser == currentUser) {
+ if (expiringUser == userId) {
// TODO: http://b/23522538
if (expiringUser != UserHandle.USER_SYSTEM) {
userType = USER_TYPE_SECONDARY_USER;
@@ -286,8 +286,8 @@
showWipeDialog(failedAttempts, userType);
}
}
- monitor.reportFailedStrongAuthUnlockAttempt();
- mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser());
+ monitor.reportFailedStrongAuthUnlockAttempt(userId);
+ mLockPatternUtils.reportFailedPasswordAttempt(userId);
if (timeoutMs > 0) {
showTimeoutDialog(timeoutMs);
}
@@ -422,14 +422,13 @@
return mIsVerifyUnlockOnly;
}
- public void reportUnlockAttempt(boolean success, int timeoutMs) {
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
if (success) {
monitor.clearFailedUnlockAttempts();
- mLockPatternUtils.reportSuccessfulPasswordAttempt(
- KeyguardUpdateMonitor.getCurrentUser());
+ mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
} else {
- KeyguardSecurityContainer.this.reportFailedUnlockAttempt(timeoutMs);
+ KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
}
}
@@ -445,7 +444,7 @@
@Override
public void userActivity() { }
@Override
- public void reportUnlockAttempt(boolean success, int timeoutMs) { }
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
@Override
public boolean isVerifyUnlockOnly() { return false; }
@Override
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8102c34..b4f9b9f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1552,12 +1552,12 @@
mFailedAttempts.delete(sCurrentUser);
}
- public int getFailedUnlockAttempts() {
- return mFailedAttempts.get(sCurrentUser, 0);
+ public int getFailedUnlockAttempts(int userId) {
+ return mFailedAttempts.get(userId, 0);
}
- public void reportFailedStrongAuthUnlockAttempt() {
- mFailedAttempts.put(sCurrentUser, getFailedUnlockAttempts() + 1);
+ public void reportFailedStrongAuthUnlockAttempt(int userId) {
+ mFailedAttempts.put(userId, getFailedUnlockAttempts(userId) + 1);
}
public void clearFingerprintRecognized() {
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index f9fb85c..cde28fc 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -34,27 +34,33 @@
namespace {
-// Maximum number of bytes to write in one request.
+// The numbers came from sdcard.c.
+// Maximum number of bytes to write/read in one request/one reply.
constexpr size_t MAX_WRITE = 256 * 1024;
+constexpr size_t MAX_READ = 128 * 1024;
+
constexpr size_t NUM_MAX_HANDLES = 1024;
// Largest possible request.
// The request size is bounded by the maximum size of a FUSE_WRITE request
// because it has the largest possible data payload.
constexpr size_t MAX_REQUEST_SIZE = sizeof(struct fuse_in_header) +
- sizeof(struct fuse_write_in) + MAX_WRITE;
+ sizeof(struct fuse_write_in) + (MAX_WRITE > MAX_READ ? MAX_WRITE : MAX_READ);
static jclass app_fuse_class;
static jmethodID app_fuse_get_file_size;
static jmethodID app_fuse_get_object_bytes;
+// NOTE:
+// FuseRequest and FuseResponse shares the same buffer to save memory usage, so the handlers must
+// not access input buffer after writing data to output buffer.
struct FuseRequest {
char buffer[MAX_REQUEST_SIZE];
FuseRequest() {}
const struct fuse_in_header& header() const {
return *(const struct fuse_in_header*) buffer;
}
- const void* data() const {
+ void* data() {
return (buffer + sizeof(struct fuse_in_header));
}
size_t data_length() const {
@@ -62,6 +68,26 @@
}
};
+template<typename T>
+class FuseResponse {
+ size_t size_;
+ T* const buffer_;
+public:
+ FuseResponse(void* buffer) : size_(0), buffer_(static_cast<T*>(buffer)) {}
+
+ void prepare_buffer(size_t size = sizeof(T)) {
+ memset(buffer_, 0, size);
+ size_ = size;
+ }
+
+ void set_size(size_t size) {
+ size_ = size;
+ }
+
+ size_t size() const { return size_; }
+ T* data() const { return buffer_; }
+};
+
class ScopedFd {
int mFd;
@@ -76,7 +102,7 @@
};
/**
- * The class is used to access AppFuse class in Java from fuse handlers.
+ * Fuse implementation consists of handlers parsing FUSE commands.
*/
class AppFuse {
JNIEnv* env_;
@@ -90,9 +116,9 @@
AppFuse(JNIEnv* env, jobject self) :
env_(env), self_(self), handle_counter_(0) {}
- bool handle_fuse_request(int fd, const FuseRequest& req) {
- ALOGV("Request op=%d", req.header().opcode);
- switch (req.header().opcode) {
+ bool handle_fuse_request(int fd, FuseRequest* req) {
+ ALOGV("Request op=%d", req->header().opcode);
+ switch (req->header().opcode) {
// TODO: Handle more operations that are enough to provide seekable
// FD.
case FUSE_LOOKUP:
@@ -110,20 +136,20 @@
invoke_handler(fd, req, &AppFuse::handle_fuse_open);
return true;
case FUSE_READ:
- invoke_handler(fd, req, &AppFuse::handle_fuse_read, 8192);
+ invoke_handler(fd, req, &AppFuse::handle_fuse_read);
return true;
case FUSE_RELEASE:
- invoke_handler(fd, req, &AppFuse::handle_fuse_release, 0);
+ invoke_handler(fd, req, &AppFuse::handle_fuse_release);
return true;
case FUSE_FLUSH:
- invoke_handler(fd, req, &AppFuse::handle_fuse_flush, 0);
+ invoke_handler(fd, req, &AppFuse::handle_fuse_flush);
return true;
default: {
ALOGV("NOTIMPL op=%d uniq=%" PRIx64 " nid=%" PRIx64 "\n",
- req.header().opcode,
- req.header().unique,
- req.header().nodeid);
- fuse_reply(fd, req.header().unique, -ENOSYS, NULL, 0);
+ req->header().opcode,
+ req->header().unique,
+ req->header().nodeid);
+ fuse_reply(fd, req->header().unique, -ENOSYS, NULL, 0);
return true;
}
}
@@ -132,8 +158,7 @@
private:
int handle_fuse_lookup(const fuse_in_header& header,
const char* name,
- fuse_entry_out* out,
- uint32_t* /*unused*/) {
+ FuseResponse<fuse_entry_out>* out) {
if (header.nodeid != 1) {
return -ENOENT;
}
@@ -148,19 +173,19 @@
return -ENOENT;
}
- out->nodeid = n;
- out->attr_valid = 10;
- out->entry_valid = 10;
- out->attr.ino = n;
- out->attr.mode = S_IFREG | 0777;
- out->attr.size = size;
+ out->prepare_buffer();
+ out->data()->nodeid = n;
+ out->data()->attr_valid = 10;
+ out->data()->entry_valid = 10;
+ out->data()->attr.ino = n;
+ out->data()->attr.mode = S_IFREG | 0777;
+ out->data()->attr.size = size;
return 0;
}
int handle_fuse_init(const fuse_in_header&,
const fuse_init_in* in,
- fuse_init_out* out,
- uint32_t* reply_size) {
+ FuseResponse<fuse_init_out>* out) {
// Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
// defined (fuse version 7.6). The structure is the same from 7.6 through
// 7.22. Beginning with 7.23, the structure increased in size and added
@@ -172,50 +197,58 @@
return -1;
}
+ // Before writing |out|, we need to copy data from |in|.
+ const uint32_t minor = in->minor;
+ const uint32_t max_readahead = in->max_readahead;
+
// We limit ourselves to 15 because we don't handle BATCH_FORGET yet
- out->minor = std::min(in->minor, 15u);
+ size_t response_size = sizeof(fuse_init_out);
#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
// FUSE_KERNEL_VERSION >= 23.
// If the kernel only works on minor revs older than or equal to 22,
// then use the older structure size since this code only uses the 7.22
// version of the structure.
- if (in->minor <= 22) {
- *reply_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
+ if (minor <= 22) {
+ response_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
}
-#else
- // Don't drop this line to prevent an 'unused' compile error.
- *reply_size = sizeof(fuse_init_out);
#endif
-
- out->major = FUSE_KERNEL_VERSION;
- out->max_readahead = in->max_readahead;
- out->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
- out->max_background = 32;
- out->congestion_threshold = 32;
- out->max_write = MAX_WRITE;
+ out->prepare_buffer(response_size);
+ out->data()->major = FUSE_KERNEL_VERSION;
+ out->data()->minor = std::min(minor, 15u);
+ out->data()->max_readahead = max_readahead;
+ out->data()->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
+ out->data()->max_background = 32;
+ out->data()->congestion_threshold = 32;
+ out->data()->max_write = MAX_WRITE;
return 0;
}
int handle_fuse_getattr(const fuse_in_header& header,
const fuse_getattr_in* /* in */,
- fuse_attr_out* out,
- uint32_t* /*unused*/) {
- if (header.nodeid != 1) {
- return -ENOENT;
+ FuseResponse<fuse_attr_out>* out) {
+ out->prepare_buffer();
+ out->data()->attr_valid = 10;
+ out->data()->attr.ino = header.nodeid;
+ if (header.nodeid == 1) {
+ out->data()->attr.mode = S_IFDIR | 0777;
+ out->data()->attr.size = 0;
+ } else {
+ int64_t size = get_file_size(header.nodeid);
+ if (size < 0) {
+ return -ENOENT;
+ }
+ out->data()->attr.mode = S_IFREG | 0777;
+ out->data()->attr.size = size;
}
- out->attr_valid = 1000 * 60 * 10;
- out->attr.ino = header.nodeid;
- out->attr.mode = S_IFDIR | 0777;
- out->attr.size = 0;
+
return 0;
}
int handle_fuse_open(const fuse_in_header& header,
const fuse_open_in* /* in */,
- fuse_open_out* out,
- uint32_t* /*unused*/) {
+ FuseResponse<fuse_open_out>* out) {
if (handles_.size() >= NUM_MAX_HANDLES) {
// Too many open files.
return -EMFILE;
@@ -224,68 +257,66 @@
do {
handle = handle_counter_++;
} while (handles_.count(handle) != 0);
-
handles_.insert(std::make_pair(handle, header.nodeid));
- out->fh = handle;
+
+ out->prepare_buffer();
+ out->data()->fh = handle;
return 0;
}
int handle_fuse_read(const fuse_in_header& /* header */,
const fuse_read_in* in,
- void* out,
- uint32_t* reply_size) {
+ FuseResponse<void>* out) {
+ if (in->size > MAX_READ) {
+ return -EINVAL;
+ }
const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh);
if (it == handles_.end()) {
return -EBADF;
}
- const int64_t result = get_object_bytes(
- it->second,
- in->offset,
- in->size,
- out);
+ uint64_t offset = in->offset;
+ uint32_t size = in->size;
+
+ // Overwrite the size after writing data.
+ out->prepare_buffer(0);
+ const int64_t result = get_object_bytes(it->second, offset, size, out->data());
if (result < 0) {
return -EIO;
}
- *reply_size = static_cast<size_t>(result);
+ out->set_size(result);
return 0;
}
int handle_fuse_release(const fuse_in_header& /* header */,
const fuse_release_in* in,
- void* /* out */,
- uint32_t* /* reply_size */) {
+ FuseResponse<void>* /* out */) {
handles_.erase(in->fh);
return 0;
}
int handle_fuse_flush(const fuse_in_header& /* header */,
const void* /* in */,
- void* /* out */,
- uint32_t* /* reply_size */) {
+ FuseResponse<void>* /* out */) {
return 0;
}
template <typename T, typename S>
void invoke_handler(int fd,
- const FuseRequest& request,
+ FuseRequest* request,
int (AppFuse::*handler)(const fuse_in_header&,
const T*,
- S*,
- uint32_t*),
- uint32_t reply_size = sizeof(S)) {
- char reply_data[reply_size];
- memset(reply_data, 0, reply_size);
+ FuseResponse<S>*)) {
+ FuseResponse<S> response(request->data());
const int reply_code = (this->*handler)(
- request.header(),
- static_cast<const T*>(request.data()),
- reinterpret_cast<S*>(reply_data),
- &reply_size);
+ request->header(),
+ static_cast<const T*>(request->data()),
+ &response);
fuse_reply(
fd,
- request.header().unique,
+ request->header().unique,
reply_code,
- reply_data,
- reply_size);
+ request->data(),
+ response.size());
}
int64_t get_file_size(int inode) {
@@ -378,7 +409,7 @@
continue;
}
- if (!appfuse.handle_fuse_request(fd, request)) {
+ if (!appfuse.handle_fuse_request(fd, &request)) {
return JNI_TRUE;
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index 9c726ba..0d81a30 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -188,6 +188,15 @@
}
}
+ @VisibleForTesting
+ long getPartialObject(int deviceId, int objectHandle, long offset, long size, byte[] buffer)
+ throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ synchronized (device) {
+ return device.getPartialObject(objectHandle, offset, size, buffer);
+ }
+ }
+
byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
final MtpDevice device = getDevice(deviceId);
synchronized (device) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index c216c77..15b8ef3 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -7,9 +7,6 @@
import android.provider.DocumentsContract;
import android.util.Log;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
index 49b48c5..25e9900 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java
@@ -19,8 +19,12 @@
import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
+import android.mtp.MtpConstants;
+import android.mtp.MtpEvent;
+import android.mtp.MtpObjectInfo;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
+import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.test.InstrumentationTestCase;
@@ -32,12 +36,10 @@
@RealDeviceTest
public class MtpManagerTest extends InstrumentationTestCase {
-
private static final int TIMEOUT_MS = 1000;
UsbManager mUsbManager;
MtpManager mManager;
UsbDevice mUsbDevice;
- int mRequest;
@Override
public void setUp() throws Exception {
@@ -85,6 +87,65 @@
getInstrumentation().show(Arrays.toString(records[0].operationsSupported));
}
+ public void testEventObjectAdded() throws Exception {
+ while (true) {
+ getInstrumentation().show("Please take a photo by using connected MTP device.");
+ final CancellationSignal signal = new CancellationSignal();
+ MtpEvent event = mManager.readEvent(mUsbDevice.getDeviceId(), signal);
+ if (event.getEventCode() != MtpConstants.EVENT_OBJECT_ADDED) {
+ continue;
+ }
+ assertTrue(event.getObjectHandle() != 0);
+ break;
+ }
+ }
+
+ public void testCreateDocumentAndGetPartialObject() throws Exception {
+ final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+ final ParcelFileDescriptor.AutoCloseOutputStream stream =
+ new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]);
+ int storageId = 0;
+ for (final MtpDeviceRecord record : mManager.getDevices()) {
+ if (record.deviceId == mUsbDevice.getDeviceId()) {
+ storageId = record.roots[0].mStorageId;
+ break;
+ }
+ }
+ assertTrue("Valid storage not found.", storageId != 0);
+ final String testFileName = "MtpManagerTest_testFile.txt";
+ for (final int handle : mManager.getObjectHandles(
+ mUsbDevice.getDeviceId(), storageId, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN)) {
+ if (mManager.getObjectInfo(mUsbDevice.getDeviceId(), handle)
+ .getName().equals(testFileName)) {
+ mManager.deleteDocument(mUsbDevice.getDeviceId(), handle);
+ break;
+ }
+ }
+ final byte[] expectedBytes = "Hello Android!".getBytes("ascii");
+ final int objectHandle;
+ try {
+ stream.write(expectedBytes);
+ objectHandle = mManager.createDocument(
+ mUsbDevice.getDeviceId(),
+ new MtpObjectInfo.Builder()
+ .setStorageId(storageId)
+ .setName(testFileName)
+ .setCompressedSize(expectedBytes.length)
+ .setFormat(MtpConstants.FORMAT_TEXT)
+ .build(),
+ fds[0]);
+ } finally {
+ stream.close();
+ }
+ final byte[] bytes = new byte[100];
+ assertEquals(5, mManager.getPartialObject(
+ mUsbDevice.getDeviceId(), objectHandle, 0, 5, bytes));
+ assertEquals("Hello", new String(bytes, 0, 5, "ascii"));
+ assertEquals(8, mManager.getPartialObject(
+ mUsbDevice.getDeviceId(), objectHandle, 6, 100, bytes));
+ assertEquals("Android!", new String(bytes, 0, 8, "ascii"));
+ }
+
private Context getContext() {
return getInstrumentation().getContext();
}
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index 6179b4b..57ba2b6 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Soek tans vir drukkers"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Geen drukdienste is geaktiveer nie"</string>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index 2f94e1e..a2182fb 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"አታሚዎችን በመፈለግ ላይ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ምንም የህትመት አገልግሎቶች አልነቁም"</string>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index 29767c3..eab1339 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -65,6 +65,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"البحث عن طابعات"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"لم يتم تمكين أي خدمات طباعة"</string>
diff --git a/packages/PrintSpooler/res/values-az-rAZ/strings.xml b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
index 3316b30..bff477d 100644
--- a/packages/PrintSpooler/res/values-az-rAZ/strings.xml
+++ b/packages/PrintSpooler/res/values-az-rAZ/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
index 86baf10..b28aa29 100644
--- a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
+++ b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
@@ -62,6 +62,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Pretraga štampača"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nijedna usluga štampanja nije omogućena"</string>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index 2c02b74..e8de8ea 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Търсене на принтери"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Няма активирани услуги за отпечатване"</string>
diff --git a/packages/PrintSpooler/res/values-bn-rBD/strings.xml b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
index d48d91d..a1ca494 100644
--- a/packages/PrintSpooler/res/values-bn-rBD/strings.xml
+++ b/packages/PrintSpooler/res/values-bn-rBD/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"মুদ্রকগুলি অনুসন্ধান করা হচ্ছে"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"মুদ্রণ পরিষেবা সক্ষম নেই"</string>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index d0fd75e..aa6f992 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index bec34ac..4bc22d4 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -63,6 +63,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index de29146..b8be624 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Søger efter printere"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ingen udskrivningstjenester er aktiveret"</string>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index 054454e..0b77186 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Suche nach Druckern"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Keine Druckdienste aktiviert"</string>
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index abae961..d9a4aeb 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Αναζήτηση για εκτυπωτές"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Δεν έχουν ενεργοποιηθεί υπηρεσίες εκτύπωσης"</string>
diff --git a/packages/PrintSpooler/res/values-en-rAU/strings.xml b/packages/PrintSpooler/res/values-en-rAU/strings.xml
index b656dcd..d8a9437 100644
--- a/packages/PrintSpooler/res/values-en-rAU/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rAU/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
diff --git a/packages/PrintSpooler/res/values-en-rGB/strings.xml b/packages/PrintSpooler/res/values-en-rGB/strings.xml
index b656dcd..d8a9437 100644
--- a/packages/PrintSpooler/res/values-en-rGB/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rGB/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
diff --git a/packages/PrintSpooler/res/values-en-rIN/strings.xml b/packages/PrintSpooler/res/values-en-rIN/strings.xml
index b656dcd..d8a9437 100644
--- a/packages/PrintSpooler/res/values-en-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rIN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Searching for printers"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No print services enabled"</string>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index 712bb80..19c695b 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Buscando impresoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No hay servicios de impresión habilitados"</string>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index dfd6a62..d13ccda 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Buscando impresoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"No hay servicios de impresión habilitados"</string>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index 7b962af..f03eb37 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Printerite otsimine"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ühtegi printimisteenust pole lubatud"</string>
diff --git a/packages/PrintSpooler/res/values-eu-rES/strings.xml b/packages/PrintSpooler/res/values-eu-rES/strings.xml
index 4551cc2..d4255e2 100644
--- a/packages/PrintSpooler/res/values-eu-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-eu-rES/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Inprimagailuak bilatzen"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ez dago gaituta inprimatzeko zerbitzurik"</string>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index d85978d..907123c 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"درحال جستجوی چاپگرها"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"هیچ خدمات چاپی فعال نیست"</string>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index 4472459..f57b884 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Etsitään tulostimia"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ei käytössä olevia tulostuspalveluita"</string>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index 1d45315..949ba55 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index df7dfef..1fcc040 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-gl-rES/strings.xml b/packages/PrintSpooler/res/values-gl-rES/strings.xml
index eae6450..2e60960 100644
--- a/packages/PrintSpooler/res/values-gl-rES/strings.xml
+++ b/packages/PrintSpooler/res/values-gl-rES/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-gu-rIN/strings.xml b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
index 8028274..4ba969c 100644
--- a/packages/PrintSpooler/res/values-gu-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-gu-rIN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"પ્રિન્ટર્સ માટે શોધી રહ્યું છે"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"કોઈ છાપ સેવાઓ સક્ષમ કરેલ નથી"</string>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index 5bfcc6e..1061346 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"प्रिंटर खोज रहा है"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"कोई भी प्रिंट सेवा सक्षम नहीं है"</string>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index bf244c5..4a7d29f 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -62,6 +62,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index 6c608f1..5aae2e4 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 801410b..179c384 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Տպիչների որոնում"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ակտիվացված տպման ծառայություններ չկան"</string>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index 227a372..7286f7a 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Mencari printer"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Tidak ada layanan cetak yang aktif"</string>
diff --git a/packages/PrintSpooler/res/values-is-rIS/strings.xml b/packages/PrintSpooler/res/values-is-rIS/strings.xml
index f078ff8..9ea49a9 100644
--- a/packages/PrintSpooler/res/values-is-rIS/strings.xml
+++ b/packages/PrintSpooler/res/values-is-rIS/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Leitar að prentara"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Engin prentþjónusta er virk"</string>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index 10b3601..c19d012 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index 8a04c76..00bf27c 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -63,6 +63,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"מחפש מדפסות"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"לא הופעלו שירותי הדפסה"</string>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index a132fb1..e0fc79a 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"プリンタの検索中"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"使用できる印刷サービスがありません"</string>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index 675245b..ad1468a 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"მიმდინარეობს პრინტერების ძიება"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ბეჭდვის სერვისები გააქტიურებული არ არის"</string>
diff --git a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
index 4c7c4f4..d0337a6 100644
--- a/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
+++ b/packages/PrintSpooler/res/values-kk-rKZ/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Принтерлерді іздеу"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Басып шығару қызметтері қосылмаған"</string>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index fac7c0c..c9431e9 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"ស្វែងរកម៉ាស៊ីនបោះពុម្ព"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"គ្មានការបើកដំណើរការសេវាបោះពុម្ពទេ"</string>
diff --git a/packages/PrintSpooler/res/values-kn-rIN/strings.xml b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
index 61279fc..fc5149a 100644
--- a/packages/PrintSpooler/res/values-kn-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-kn-rIN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"ಪ್ರಿಂಟರ್ಗಳಿಗಾಗಿ ಹುಡುಕಲಾಗುತ್ತಿದೆ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ಯಾವುದೇ ಮುದ್ರಣ ಸೇವೆಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿಲ್ಲ"</string>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index fe47e55..2faff1f 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"프린터 검색 중"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"사용 가능한 프린트 서비스 없음"</string>
diff --git a/packages/PrintSpooler/res/values-ky-rKG/strings.xml b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
index 79b38d1..a01e4a8 100644
--- a/packages/PrintSpooler/res/values-ky-rKG/strings.xml
+++ b/packages/PrintSpooler/res/values-ky-rKG/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Принтерлер изделүүдө"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Принтер-кызматтары иштетилген эмес"</string>
diff --git a/packages/PrintSpooler/res/values-lo-rLA/strings.xml b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
index 3140a25..b5d13b5 100644
--- a/packages/PrintSpooler/res/values-lo-rLA/strings.xml
+++ b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"ກຳລັງຊອກຫາເຄື່ອງພິມ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ບໍ່ມີການບໍລິການພິມເປີດໃຊ້ງານໄວ້"</string>
diff --git a/packages/PrintSpooler/res/values-lt/strings.xml b/packages/PrintSpooler/res/values-lt/strings.xml
index 4f0772e..3b8f143 100644
--- a/packages/PrintSpooler/res/values-lt/strings.xml
+++ b/packages/PrintSpooler/res/values-lt/strings.xml
@@ -63,6 +63,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Ieškoma spausdintuvų"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Neįgalinta jokių spausdinimo paslaugų"</string>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index 0efa50f..762d0bd 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -62,6 +62,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Printeru meklēšana"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nav iespējots neviens drukas pakalpojums"</string>
diff --git a/packages/PrintSpooler/res/values-mk-rMK/strings.xml b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
index 2805dee..de6d3e9 100644
--- a/packages/PrintSpooler/res/values-mk-rMK/strings.xml
+++ b/packages/PrintSpooler/res/values-mk-rMK/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Пребарување печатачи"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Нема овозможени услуги за печатење"</string>
diff --git a/packages/PrintSpooler/res/values-ml-rIN/strings.xml b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
index 9617a7a..7a33e14 100644
--- a/packages/PrintSpooler/res/values-ml-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ml-rIN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"പ്രിന്ററുകൾക്കായി തിരയുന്നു"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"പ്രിന്റ് സേവനങ്ങളൊന്നും പ്രവർത്തനക്ഷമാക്കിയിട്ടില്ല"</string>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index 59bb9bf..c94e56d 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Принтер хайж байна"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Хэвлэх үйлчилгээг идэвхжүүлээгүй"</string>
diff --git a/packages/PrintSpooler/res/values-mr-rIN/strings.xml b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
index 2b6661e..ab25010 100644
--- a/packages/PrintSpooler/res/values-mr-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-mr-rIN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"प्रिंटर शोधत आहे"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"कोणत्याही मुद्रण सेवा सक्षम केलेल्या नाहीत"</string>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index c066627..917ae8a 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Mencari pencetak"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Perkhidmatan cetak tidak didayakan"</string>
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index a63e85e..4d4c95b 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"စာထုတ်စက်များကို ရှာနေပါသည်"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ပုံနှိပ်ထုတ်ယူရေး ဝန်ဆောင်မှုများ ဖွင့်မထားပါ"</string>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index 8128011..9efa5d1 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-ne-rNP/strings.xml b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
index 89b2fbb..281a65d 100644
--- a/packages/PrintSpooler/res/values-ne-rNP/strings.xml
+++ b/packages/PrintSpooler/res/values-ne-rNP/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"प्रिन्टरहरू खोज्दै"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"कुनै पनि मुद्रण सेवाहरू सक्रिय छैनन्"</string>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index 70b93e6..eef9880 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Printers zoeken"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Geen afdrukservices ingeschakeld"</string>
diff --git a/packages/PrintSpooler/res/values-pa-rIN/strings.xml b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
index da81919..7d7860c 100644
--- a/packages/PrintSpooler/res/values-pa-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-pa-rIN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"ਪ੍ਰਿੰਟਰ ਖੋਜ ਰਿਹਾ ਹੈ"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ਪ੍ਰਿੰਟ ਸੇਵਾਵਾਂ ਯੋਗ ਨਹੀਂ ਬਣਾਈਆਂ ਗਈਆਂ"</string>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index b7613cf..6837edf 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -63,6 +63,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Szukanie drukarek"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Brak włączonych usług drukowania"</string>
diff --git a/packages/PrintSpooler/res/values-pt-rBR/strings.xml b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
index 199f304..c9713c9 100644
--- a/packages/PrintSpooler/res/values-pt-rBR/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rBR/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Procurando impressoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index ea94c55..9fabc0f 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index 199f304..c9713c9 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Procurando impressoras"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Nenhum serviço de impressão ativado"</string>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 1c74aab..7364eb0 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -62,6 +62,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Se caută imprimante"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Niciun serviciu de printare activat"</string>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index e900ab9..bb03860 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -63,6 +63,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Поиск принтеров…"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Службы печати недоступны"</string>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index 5731849..610442d 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"මුද්රණ යන්ත්ර සොයමින්"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"මුද්රණ සේවා සබල නැත"</string>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 1c7b740..603d1d2 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -63,6 +63,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index 4c794dc..4a08269 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -63,6 +63,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Iskanje tiskalnikov"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ni omogočenih tiskalnih storitev"</string>
diff --git a/packages/PrintSpooler/res/values-sq-rAL/strings.xml b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
index aff55f6..b0902ef 100644
--- a/packages/PrintSpooler/res/values-sq-rAL/strings.xml
+++ b/packages/PrintSpooler/res/values-sq-rAL/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index 043048e..feb2940 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -62,6 +62,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Претрага штампача"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Ниједна услуга штампања није омогућена"</string>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index fd8581f..cf398c7 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Söker efter skrivare"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Inga utskriftstjänster har aktiverats"</string>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index 0950f57..7e00b70 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Inatafuta printa"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Huduma za kuchapisha hazijawashwa"</string>
diff --git a/packages/PrintSpooler/res/values-ta-rIN/strings.xml b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
index bf6feae..ae0b774 100644
--- a/packages/PrintSpooler/res/values-ta-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-ta-rIN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"அச்சுப்பொறிகளைத் தேடுகிறது"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"அச்சுப் பொறிகள் இல்லை"</string>
diff --git a/packages/PrintSpooler/res/values-te-rIN/strings.xml b/packages/PrintSpooler/res/values-te-rIN/strings.xml
index 4d14b8c..5fd8d60 100644
--- a/packages/PrintSpooler/res/values-te-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-te-rIN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"ప్రింటర్ల కోసం శోధిస్తోంది"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ముద్రణ సేవలు ఏవీ ప్రారంభించలేదు"</string>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index e1f82e1..ebd5e2a 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"กำลังค้นหาเครื่องพิมพ์"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"ไม่ได้เปิดใช้บริการพิมพ์"</string>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index 052d8cc..ebe869b 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index 7f9e6f5..9cd42ab 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-uk/strings.xml b/packages/PrintSpooler/res/values-uk/strings.xml
index d34aa2a..1082147 100644
--- a/packages/PrintSpooler/res/values-uk/strings.xml
+++ b/packages/PrintSpooler/res/values-uk/strings.xml
@@ -63,6 +63,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Пошук принтерів"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Немає служб друку"</string>
diff --git a/packages/PrintSpooler/res/values-ur-rPK/strings.xml b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
index ccf6f56..56f1093 100644
--- a/packages/PrintSpooler/res/values-ur-rPK/strings.xml
+++ b/packages/PrintSpooler/res/values-ur-rPK/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"پرنٹرز تلاش کر رہا ہے"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"کوئی پرنٹ سروس فعال نہیں"</string>
diff --git a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
index d1fc47f..30b218e 100644
--- a/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
+++ b/packages/PrintSpooler/res/values-uz-rUZ/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Printerlar qidirilmoqda"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Hech qaysi chop etish xizmati yoqilmagan"</string>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index eb5de95..32aaf63 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -61,6 +61,7 @@
</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_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>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index e39849e..42cf3b1 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"正在搜索打印机"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"未启用任何打印服务"</string>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index 1148226..0a458ad 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"正在搜尋打印機"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"沒有已啟用的列印服務"</string>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index c0dd3de..7a30011 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"正在搜尋印表機"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"未啟用任何列印服務"</string>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index 231b1bf..f57b58c 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -61,6 +61,7 @@
</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_searching_for_printers" msgid="6550424555079932867">"Isesha amaphrinta"</string>
<string name="print_no_print_services" msgid="8561247706423327966">"Amasevisi ephrinta akavuliwe."</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 9c1cf64..811adda 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -1964,7 +1964,7 @@
&& printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE)
|| (mPrinter.getCapabilities() == null
&& printer.getCapabilities() != null);
- mPrinter.copyFrom(printer);
+ mPrinter = printer;
}
if (available) {
@@ -2394,7 +2394,7 @@
mPrinterAvailabilityDetector.updatePrinter(newPrinterState);
- oldPrinterState.copyFrom(newPrinterState);
+ mCurrentPrinter = newPrinterState;
if ((isActive && gotCapab) || (becameActive && hasCapab)) {
if (hasCapab && capabChanged) {
diff --git a/packages/SettingsLib/Android.mk b/packages/SettingsLib/Android.mk
index c860668..2189b55 100644
--- a/packages/SettingsLib/Android.mk
+++ b/packages/SettingsLib/Android.mk
@@ -3,9 +3,22 @@
LOCAL_MODULE := SettingsLib
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-v4 \
+ android-support-v7-recyclerview \
+ android-support-v7-preference \
+ android-support-v7-appcompat \
+ android-support-v14-preference
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
+ frameworks/support/v7/preference/res \
+ frameworks/support/v14/preference/res \
+ frameworks/support/v7/appcompat/res \
+ frameworks/support/v7/recyclerview/res
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_AAPT_FLAGS := --auto-add-overlay \
+ --extra-packages android.support.v7.preference:android.support.v14.preference:android.support.v17.preference:android.support.v7.appcompat:android.support.v7.recyclerview
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml
new file mode 100644
index 0000000..b3d7cf9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_settings_lock_outline.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="21dp"
+ android:height="21dp"
+ android:viewportWidth="21.0"
+ android:viewportHeight="21.0"
+ android:tint="?android:attr/colorAccent">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M8,16c1.1,0,2-0.9,2-2s-0.9-2-2-2s-2,0.9-2,2S6.9,16,8,16zM14,7h-1V5c0-2.8-2.2-5-5-5S3,2.2,3,5v2H2C0.9,7,0,7.9,0,9v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V9C16,7.9,15.1,7,14,7z M4.9,5c0-1.7,1.4-3.1,3.1-3.1s3.1,1.4,3.1,3.1v2H4.9V5z M14,19H2V9h12V19z" />
+</vector>
diff --git a/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml
new file mode 100644
index 0000000..f7a9c9f
--- /dev/null
+++ b/packages/SettingsLib/res/layout/spinner_dropdown_restricted_item.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ style="?android:attr/spinnerDropDownItemStyle"
+ android:singleLine="true"
+ android:layout_width="wrap_content"
+ android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:ellipsize="marquee" />
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index ebe8752..30a3b59 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -247,8 +247,8 @@
<string name="force_allow_on_external_summary" msgid="3191952505860343233">"Laat enige program na ekstern geskryf word, ongeag manifeswaardes"</string>
<string name="force_resizable_activities" msgid="8615764378147824985">"Verplig verstelbare groottes vir aktiwiteite"</string>
<string name="force_resizable_activities_summary" msgid="4508217476997182216">"Maak grootte van alle aktiwiteite verstelbaar vir veelvuldige vensters, ongeag manifeswaardes."</string>
- <string name="enable_freeform_support" msgid="1461893351278940416">"Aktiveer vormvrye vensters"</string>
- <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktiveer steun vir eksperimentele vormvrye vensters."</string>
+ <string name="enable_freeform_support" msgid="1461893351278940416">"Aktiveer vormvrye-Windows"</string>
+ <string name="enable_freeform_support_summary" msgid="2252563497485436534">"Aktiveer steun vir eksperimentele vormvrye-Windows."</string>
<string name="local_backup_password_title" msgid="3860471654439418822">"Werkskerm-rugsteunwagwoord"</string>
<string name="local_backup_password_summary_none" msgid="6951095485537767956">"Volle rekenaarrugsteune word nie tans beskerm nie"</string>
<string name="local_backup_password_summary_change" msgid="2731163425081172638">"Raak om die wagwoord vir volledige rekenaarrugsteune te verander of te verwyder"</string>
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Outomaties"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Stel WebView-implementering"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Skakel om na lêerenkripsie"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Skakel om …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Lêerenkripsie is reeds uitgevoer"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Kleurregstelling"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierdie kenmerk is eksperimenteel en kan werkverrigting beïnvloed."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – sowat <xliff:g id="TIME">%2$s</xliff:g> oor"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol op WS"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol oor USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol vanaf draadloos"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Laai"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laai tans op WS"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laai tans oor USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Laai tans draadloos"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Laai nie"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laai nie"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Vol"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index e33d0fa..beac0a1 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"ራስ-ሰር"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"የWebView ትግበራ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"የWebView ትግበራን ያዘጋጁ"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"የተመረጠው WebView ትግበራ ተሰናክሏል፣ እና ጥቅም ላይ እንዲውል መንቃት አለበት፣ ሊያነቁት ይፈልጋሉ?"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"የቀለም ማስተካከያ"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ይህ ባህሪ የሙከራ ነውና አፈጻጸም ላይ ተጽዕኖ ሊኖረው ይችላል።"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ገደማ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> እስከሚሞላ ድረስ"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በኤሲ ላይ እስከሚሞላ ድረስ"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በዩኤስቢ ላይ እስከሚሞላ ድረስ"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በገመድ አልባ ላይ እስከሚሞላ ድረስ"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"ያልታወቀ"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"ኃይል በመሙላት ላይ"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"በኤሲ ሃይል በመሙላት ላይ"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"በዩኤስቢ ሃይል በመሙላት ላይ"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"በገመድ አልባ ሃይል በመሙላት ላይ"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"ባትሪ እየሞላ አይደለም"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ኃይል እየሞላ አይደለም"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"ሙሉነው"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 71761b9..2386cb6 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"تلقائيًا"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"تطبيق WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"تعيين تطبيق WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"إن تنفيذ ميزة WebView التي تم اختيارها معطَّل، ويجب تمكين هذه الميزة ليتسنى استخدامها، فهل تريد تمكينها؟"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"تصحيح الألوان"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"هذه الميزة تجريبية وقد تؤثر في الأداء."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تبقى <xliff:g id="TIME">%2$s</xliff:g> تقريبًا"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال باستخدام التيار المتردد"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال عبر USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال بالشحن اللاسلكي"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"غير معروف"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"شحن"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"جارٍ الشحن بتيار متردد"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"جارٍ الشحن عبر USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"جارٍ الشحن لاسلكيًا"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"لا يتم الشحن"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"لا يتم الشحن"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"ممتلئة"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index b5a7181..273ea30 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Avtomatik"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView icrası"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView icrasını ayarlayın"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Seçilmiş WebView icrası deaktiv edildi, istifadəsi üçün aktiv edilməlidir, aktivləşdirmək istəyirsiniz?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Fayl şifrələnməsinə çevirin"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Çevirin..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fayl artıq şifrələnib"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Rəng düzəlişi"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya eksperimentaldır və performansa təsir edə bilər."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tərəfindən qəbul edilmir"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - təxminən <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> dolana qədər"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC üzərindən dolana qədər"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB üzərindən dolana qədər"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> naqilsiz üzərindən dolana qədər"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Naməlum"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Enerji doldurma"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Dəyişən cərəyanda qidalanır"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB üzərindən qidalanır"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Naqilsiz qidalanır"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Doldurulmur"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Enerji doldurulmur"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Tam"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3fd4f7f..5d8b934 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatski"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Primena WebView-a"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Podesite primenu WebView-a"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertuj u šifrovanje datoteka"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertuj..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Već se koristi šifrovanje datoteka"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korekcija boja"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna i može da utiče na performanse."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo oko <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni punjačem"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni preko USB-a"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni bežično"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Punjenje preko punjača"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje preko USB-a"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index cd4529f..e5dbf50 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматично"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Внедряване на WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Задаване на внедряването на WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Корекция на цветове"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Тази функция е експериментална и може да се отрази на ефективността."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Заменено от „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – приблизително оставащо време: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане при променлив ток"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане през USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно безжично зареждане"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарежда се"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зареждане при AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зареждане през USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Безжично зареждане"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Не се зарежда"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се зарежда"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Пълна"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index 7c598229..9e42f71 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"স্বয়ংক্রিয়"</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_confirmation_text" msgid="6671472080671066972">"নির্বাচিত ওয়েবভিউ প্রয়োগটি অক্ষম করা আছে এবং ব্যবহার করার জন্য অবশ্যই সক্ষম করতে হবে, আপনি কি এটিকে সক্ষম করতে চান?"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"রঙ সংশোধন"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"এই বৈশিষ্ট্যটি পরীক্ষামূলক এবং এটি কার্য-সম্পাদনা প্রভাবিত করতে পারে।"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - আনুমানিক <xliff:g id="TIME">%2$s</xliff:g> বাকি আছে"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ACতে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB এর মাধ্যমে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - বেতার যোগে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"অজানা"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"চার্জ হচ্ছে"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC তে চার্জ হচ্ছে"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB এর মাধ্যমে চার্জ হচ্ছে"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"তারবিহীনভাবে চার্জ হচ্ছে"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"চার্জ হচ্ছে না"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"চার্জ হচ্ছে না"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"পূর্ণ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 1349a7a..2f81098 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automàtic"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementació de WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configura la implementació de WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"La implementació de WebView que has triat està desactivada i s\'ha d\'activar per utilitzar-la. Vols activar-la?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converteix en l\'encriptació de fitxers"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converteix…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"El fitxer ja està encriptat"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correcció del color"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Aquesta funció és experimental i pot afectar el rendiment."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g>: falten aproximadament <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega per CA"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega per USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega sense fil"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconegut"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"S\'està carregant"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Càrrega: corr. alt."</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Càrrega per USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Càrrega sense fils"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"No s\'està carregant"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No s\'està carregant"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Plena"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index beb8f94..f1c9e27 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatický"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementace WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Nastavte implementaci WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Vybraná implementace WebView je zakázána a nelze ji použít. Chcete ji povolit a použít?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Převést na šifrování souborů"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Převést…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Obsah je již na úrovni souborů zašifrován"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korekce barev"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkce je experimentální a může mít vliv na výkon."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zbývá přibližně <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití ze zásuvky"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití přes USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití bezdrátově"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznámé"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjí se"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nabíjení z adaptéru"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nabíjení přes USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezdrátové nabíjení"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Nenabíjí se"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíjí se"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 3923dbd..90ea69a 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatisk"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Konfigurer WebView-implementering"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Den valgte WebView-implementering er deaktiveret og skal aktiveres, før den kan bruges. Vil du aktivere den?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertér til filkryptering"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertér…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Allerede filkrypteret"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korriger farver"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Denne funktion er eksperimentel og kan påvirke ydeevnen."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca. <xliff:g id="TIME">%2$s</xliff:g> tilbage"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med adapter"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med trådløs"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Ukendt"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Oplader"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Opladning med AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Opladning via USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Trådløs opladning"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Oplader ikke"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Oplader ikke"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Fuld"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 4d9d4fb..8d95ddf 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatisch"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-Implementierung"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-Implementierung festlegen"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Zu Dateiverschlüsselung wechseln"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Wechseln…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Dateiverschlüsselung wird bereits verwendet."</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Farbkorrektur"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierbei handelt es sich um eine experimentelle Funktion. Dies kann sich auf die Leistung auswirken."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – noch etwa <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – bei Stromanschluss voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – über USB voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – bei kabellosem Laden voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Unbekannt"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Wird aufgeladen"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laden über Netzteil"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laden über USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Kabelloses Laden"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Wird nicht geladen"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wird nicht geladen"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Voll"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index c3acca7..303f0e5 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Αυτόματο"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Υλοποίηση WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ορισμός υλοποίησης WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Διόρθωση χρωμάτων"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Αυτή η λειτουργία είναι πειραματική και ενδεχομένως να επηρεάσει τις επιδόσεις."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Αντικαταστάθηκε από <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - απομένουν περίπου <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση με φορτιστή AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση μέσω USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη ασύρματη φόρτιση"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Άγνωστο"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Φόρτιση"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Φόρτιση με AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Φόρτιση μέσω USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Ασύρματη φόρτιση"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Δεν φορτίζει"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Δεν φορτίζει"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Πλήρης"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 833fb62..b938ace 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatic"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"The chosen WebView implementation is disabled and must be enabled to be used, do you wish to enable it?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convert to file encryption"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convert…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Already file encrypted"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Colour correction"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 833fb62..b938ace 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatic"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"The chosen WebView implementation is disabled and must be enabled to be used, do you wish to enable it?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convert to file encryption"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convert…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Already file encrypted"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Colour correction"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 833fb62..b938ace 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatic"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"The chosen WebView implementation is disabled and must be enabled to be used, do you wish to enable it?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convert to file encryption"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convert…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Already file encrypted"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Colour correction"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 4d4ab24..9ddc03f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementación de WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configurar la implementación de WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir a encriptación de archivo"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Ya está encriptado"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corrección de color"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar el rendimiento."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g>: alrededor de <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga por CA"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga por USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga inalámbrica"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carga en CA"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carga con USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carga inalámbrica"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"No se está cargando."</string>
+ <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>
</resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 858c1b6..f0682c2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementación de WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Establecer implementación de WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir a cifrado de archivo"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Ya está cifrado"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corrección del color"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar al rendimiento."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quedan <xliff:g id="TIME">%2$s</xliff:g> aproximadamente"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería con CA"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería por USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería con Wi-Fi"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Cargando en CA"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Cargando por USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Cargando de forma inalámbrica"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"No se está cargando"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se está cargando"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index 7f1056c..ff7d7ee 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automaatne"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView\' rakendamine"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView\' rakendamise seadistamine"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Teisendamine failikrüpteeringuga andmeteks"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Teisenda …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Juba failikrüpteeringuga"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Värviparandus"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"See funktsioon on katseline ja võib mõjutada toimivust."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – jäänud on umbes <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (vahelduvvool)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (USB)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (juhtmeta laad.)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Tundmatu"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Laadimine"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laad. vahelduvv.-v."</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laadimine USB kaudu"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Juhtmevaba laadimine"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Ei lae"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei lae"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Täis"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index 6405ca8..145e8f8 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatikoa"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Desgaituta dago aukeratu den Web-ikustailearen inplementazioa. Erabili nahi izanez gero, gaitu egin behar duzu. Gaitu nahi al duzu?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Eman fitxategietan oinarritutako enkriptatzea"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Enkriptatu…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fitxategietan oinarritutako enkriptatzea dauka dagoeneko"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Kolore-zuzenketa"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Eginbidea esperimentala da eta eragina izan dezake funtzionamenduan."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> inguru. <xliff:g id="TIME">%2$s</xliff:g> geratzen d(ir)a"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> korrontearen bidez guztiz kargatu arte"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB bidez guztiz kargatu arte"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> haririk gabe guztiz kargatu arte"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Ezezaguna"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Kargatzea"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"KA bidez kargatzen"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB bidez kargatzen"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Hari gabe kargatzen"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Ez da kargatzen ari"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ez da kargatzen ari"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Beteta"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index d591426..5380651 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"خودکار"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"اجرای WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"تنظیم اجرای WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"پیادهسازی WebView انتخابشده غیرفعال شده است و برای استفاده شدن باید فعال شود؛ میخواهید آن را فعال کنید؟"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"تصحیح رنگ"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"این قابلیت آزمایشی است و ممکن است عملکرد را تحت تأثیر قرار دهد."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تقریباً <xliff:g id="TIME">%2$s</xliff:g> باقی مانده است"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل با جریان متناوب"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل از طریق USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل بهطور بیسیم"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"ناشناس"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"در حال شارژ شدن"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"شارژ با جریان متناوب"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"شارژ از طریق USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"شارژ به صورت بیسیم"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"شارژ نمیشود"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"شارژ نمیشود"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"پر"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index ce36de3..f9fdb63 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automaattinen"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-käyttöönotto"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Määritä WebView-käyttöönotto"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Muunna tiedostojen salaukseksi"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Muunna…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Tiedostot on jo salattu."</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Värikorjaus"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tämä ominaisuus on kokeellinen ja voi vaikuttaa suorituskykyyn."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – noin <xliff:g id="TIME">%2$s</xliff:g> jäljellä"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (laturilataus)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (USB-lataus)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (WiFi-lataus)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Tuntematon"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Ladataan"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laturilataus"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-lataus"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Langaton lataus"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Ei laturissa"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei laturissa"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Täynnä"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 3ee0775..9f802ab 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatique"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Mise en œuvre WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Définir la mise en œuvre WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir en chiffrement basé sur un fichier"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Déjà chiffré par un fichier"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correction des couleurs"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut toucher les performances."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> %% – Temps restant : environ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (charge complète sur c.a. dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% par USB dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% avec chargeur sans fil dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"En charge (c.a.)"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"En charge par USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"En charge sans fil"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"N\'est pas en charge"</string>
+ <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>
</resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index c0098d2..6dabba8 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatique"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Mise en œuvre WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Définir la mise en œuvre WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"La mise en œuvre WebView sélectionnée est désactivée. Vous devez l\'activer pour l\'utiliser. Souhaitez-vous l\'activer ?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir en chiffrement basé sur un fichier"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Déjà chiffré via un fichier"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correction couleur"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut affecter les performances."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – Temps restant : <xliff:g id="TIME">%2$s</xliff:g> environ"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% sur secteur dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% via USB dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% sans fil dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"En charge sur secteur"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"En charge via USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"En charge sans fil"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Pas en charge"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Débranchée"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"pleine"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index 0efe805..0f0d13d 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementación de WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Definir implementación de WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter no encriptado baseado en ficheiros"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Xa se encriptou o ficheiro"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corrección da cor"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función é experimental e pode afectar ao rendemento."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - faltan aproximadamente <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga con CA"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga con USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga co modo sen fíos"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Descoñecido"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Cargando con CA"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Cargando por USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Cargando sen fíos"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Non se está cargando"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non está cargando"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index 2a0ff45..9e2ec7e 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"સ્વચલિત"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView અમલીકરણ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView અમલીકરણ સેટ કરો"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"પસંદ કરેલ WebView અમલીકરણ અક્ષમ કરેલ છે અને ઉપયોગ કરવા માટે સક્ષમ કરવું આવશ્યક છે, શું તમે તેને સક્ષમ કરવા માગો છો?"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"રંગ સુધારણા"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"આ સુવિધા પ્રાયોગિક છે અને કામગીરી પર અસર કરી શકે છે."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - આશરે <xliff:g id="TIME">%2$s</xliff:g> બાકી"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"સંપૂર્ણ થવામાં <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>, AC પર પૂર્ણ ચાર્જ થયાંને <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>, USB પર પૂર્ણ ચાર્જ થયાંને <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> વાયરલેસ દ્વારા પૂર્ણ થાય ત્યાં સુધી"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"અજાણ્યું"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"ચાર્જ થઈ રહ્યું છે"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC પર ચાર્જિંગ"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB થી ચાર્જિંગ"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"વાયરલેસથી ચાર્જિંગ"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"ચાર્જ થઈ રહ્યું નથી"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ચાર્જ થઈ રહ્યું નથી"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"પૂર્ણ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 5224c38..b6b8eac 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"स्वचालित"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView कार्यान्वयन"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView कार्यान्वयन सेट करें"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"चुना गया WebView कार्यान्वयन अक्षम है और उसे उपयोग करने के लिए सक्षम किया जाना आवश्यक है, क्या आप उसे सक्षम करना चाहते हैं?"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"रंग सुधार"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यह सुविधा प्रायोगिक है और निष्पादन को प्रभावित कर सकती है."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - लगभग <xliff:g id="TIME">%2$s</xliff:g> शेष"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूरी होने तक"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC पर पूरी होने तक"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB पर पूरी होने तक"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेस से पूरी होने तक"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हो रही है"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC से चार्ज हो रही"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB पर चार्ज हो रही"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"वायरलेस रूप से चार्ज हो रही"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज नहीं हो रही है"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज नहीं हो रही है"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"पूरी"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 0a46b27..0ab13f6 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatska"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementacija WebViewa"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Postavi implementaciju WebViewa"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Pretvori u enkripciju datoteka"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Pretvori…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Enkripcija datoteka već je izvršena"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korekcija boje"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova je značajka eksperimentalna i može utjecati na performanse."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – još približno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti strujnim napajanjem"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti putem USB-a"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti bežičnim putem"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Punjenje punjačem"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje putem USB-a"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 3559f1e..26d65fb 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatikus"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-megvalósítás"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-megvalósítás beállítása"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"A kiválasztott WebView-megvalósítás le van tiltva, a használathoz viszont engedélyezni kell. Szeretné engedélyezni?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertálás fájlalapú titkosításra"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertálás…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Már fájlalapú titkosítást használ"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Színkorrekció"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ez egy kísérleti funkció, és hatással lehet a teljesítményre."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – kb. <xliff:g id="TIME">%2$s</xliff:g> van hátra"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttség eléréséig"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig hálózatról"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig USB-ről"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig vezeték nélkül"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Ismeretlen"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Töltés"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Hálózati töltés"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-s töltés"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Nem vezetékes töltés"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Nem tölt"</string>
+ <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>
</resources>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index fb401f0..7a9b16f 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Ավտոմատ"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-ի իրականացում"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ընտրեք WebView-ի իրականացումը"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Գունային կարգաբերում"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Սա փորձնական գործառույթ է և կարող է ազդել աշխատանքի վրա:"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - մնաց մոտավորապես <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը հոսանքից"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը USB-ով"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը անլար ցանցից"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Անհայտ"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Լիցքավորում"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Լիցքավորում AC-ով"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Լիցքավորում USB-ով"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Անլար լիցքավորում"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Չի լիցքավորվում"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Չի լիցքավորվում"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Լիցքավորված"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 1262512..f2eaccf 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Otomatis"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Penerapan WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Setel penerapan WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konversi ke enkripsi file"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konversi..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Sudah dienkripsi berbasis file"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Koreksi warna"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Fitur ini bersifat eksperimental dan dapat memengaruhi kinerja."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira tersisa. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh pada AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh melalui USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh dari nirkabel"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Mengisi daya"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Mengisi daya pada AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Isi daya lewat USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Isi daya nirkabel"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Tidak mengisi daya"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengisi daya"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index 3e53e94..dc105eb 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Sjálfvirkt"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Innleiðing WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Stilla innleiðingu WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Umbreyta í dulkóðun skráa"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Umbreyta…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Þegar dulkóðað á grundvelli skráa"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Litaleiðrétting"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Þessi eiginleiki er á tilraunastigi og getur haft áhrif á frammistöðu."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – u.þ.b. <xliff:g id="TIME">%2$s</xliff:g> eftir"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu með hleðslutæki"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu í gegnum USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu þráðlaust"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Óþekkt"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Í hleðslu"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Hleðslutæki tengt"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Hleður um USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Hleður þráðlaust"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Ekki í hleðslu"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ekki í hleðslu"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Fullhlaðin"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 2d98a74..eedfa33 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatico"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementazione di WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Imposta l\'implementazione di WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"L\'implementazione di WebView selezionata non è attiva e deve essere attivata per poterla utilizzare. Vuoi attivarla?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converti in crittografia basata su file"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converti..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Crittografia su base file già eseguita"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correzione del colore"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Questa funzione è sperimentale e potrebbe influire sulle prestazioni."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tempo rimanente: <xliff:g id="TIME">%2$s</xliff:g> circa"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa tramite CA"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa tramite USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lla carica completa con wireless"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Sconosciuta"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"In carica"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"In carica tramite CA"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"In carica tramite USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"In carica, wireless"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Non in carica"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non in carica"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Carica"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 54099c7..ec8f1a5 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -247,8 +247,8 @@
<string name="force_allow_on_external_summary" msgid="3191952505860343233">"מאפשר כתיבה של כל אפליקציה באחסון חיצוני, ללא התחשבות בערכי המניפסט"</string>
<string name="force_resizable_activities" msgid="8615764378147824985">"אלץ יכולת קביעת גודל של הפעילויות"</string>
<string name="force_resizable_activities_summary" msgid="4508217476997182216">"מאפשר יכולת קביעת גודל של כל הפעילויות לריבוי חלונות, ללא קשר לערך המניפסט."</string>
- <string name="enable_freeform_support" msgid="1461893351278940416">"הפעל חלונות בצורה חופשית"</string>
- <string name="enable_freeform_support_summary" msgid="2252563497485436534">"מפעיל תמיכה בתכונה הניסיונית של חלונות בצורה חופשית."</string>
+ <string name="enable_freeform_support" msgid="1461893351278940416">"הפעל את האפשרות לשנות את הגודל והמיקום של החלונות"</string>
+ <string name="enable_freeform_support_summary" msgid="2252563497485436534">"מפעיל תמיכה בתכונה הניסיונית של שינוי הגודל והמיקום של החלונות."</string>
<string name="local_backup_password_title" msgid="3860471654439418822">"סיסמת גיבוי מקומי"</string>
<string name="local_backup_password_summary_none" msgid="6951095485537767956">"גיבויים מלאים בשולחן העבודה אינם מוגנים כעת"</string>
<string name="local_backup_password_summary_change" msgid="2731163425081172638">"גע כדי לשנות או להסיר את הסיסמה עבור גיבויים מלאים בשולחן העבודה"</string>
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"באופן אוטומטי"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"יישום WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"הגדרת יישום WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"תיקון צבע"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"תכונה זו היא ניסיונית ועשויה להשפיע על הביצועים."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> בקירוב עד לסיום"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד למילוי"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד למילוי בזרם חילופין"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד למילוי ב-USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד למילוי בטעינה אלחוטית"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"לא ידוע"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"טוען"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"טוען בזרם חילופין"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"טוען ב-USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"טוען באופן אלחוטי"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"לא בטעינה"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"לא טוען"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"מלא"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 7e45fb2..9a32d44 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"自動"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView の実装"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView の実装の設定"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,20 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色補正"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"この機能は試験運用機能であり、パフォーマンスに影響することがあります。"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g>によって上書き済み"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 残り約<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(AC)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(USB)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(ワイヤレス)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ACで充電しています"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USBで充電しています"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"無線で充電しています"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"充電していません"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"充電していません"</string>
+ <!-- String.format failed for translation -->
+ <!-- no translation found for battery_info_status_full (2824614753861462808) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index a8f25c6..0cfb6ec 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"ავტომატური"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView რეალიზაცია"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView რეალიზაციის დაყენება"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ფერის კორექცია"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ეს ფუნქცია საცდელია და შეიძლება გავლენა იქონიოს შესრულებაზე."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"დაახლ. <xliff:g id="LEVEL">%1$s</xliff:g> დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> სრულ დატენვამდე"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ელკვებით სრულ დატენვამდე"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB-თი სრულ დატენვამდე"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> უსადენოდან სრულ დატენვამდე"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"უცნობი"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"იტენება"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"დატენვა ელკვებაზე"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"დატენვა USB-ზე"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"დატენვა უსადენოდ"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"არ იტენება"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"არ იტენება"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"ბატარეა დატენილია"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index 300aaa3..b629522 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Aвтоматты"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ендіру"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ендіруін орнату"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Түсті түзету"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бұл мүмкіндік эксперименттік болып табылады және өнімділікке әсер етуі мүмкін."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - айнымалы токпен толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB арқылы толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - сымсыз толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Белгісіз"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарядталуда"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Айнымалы токпен зар."</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB арқылы зарядтау"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Сымсыз зарядтау"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Зарядталу орындалып жатқан жоқ"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Зарядталып тұрған жоқ"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Толық"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index a414668..1155c07 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"ស្វ័យប្រវត្តិ"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"ការប្រតិបត្តិ WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"កំណត់ការប្រតិបត្តិ WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ការកែពណ៌"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"លក្ខណៈនេះគឺជាការពិសោធន៍ ហើយអាចប៉ះពាល់ការអនុវត្ត។"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"បដិសេធដោយ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅសល់ប្រហែល <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូតដល់ពេញ"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូតដល់ពេញរចន្តឆ្លាស់"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូតដល់ពេញតាមយូអេសប៊ី"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូតដល់ពេញពីឥតខ្សែ"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"មិនស្គាល់"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"កំពុងបញ្ចូលថ្ម"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"បញ្ចូលថ្មតាម AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"បញ្ចូលថ្មតាមយូអេសប៊ី"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"បញ្ចូលថ្មដោយឥតខ្សែ"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"មិនកំពុងបញ្ចូលថ្ម"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"មិនបញ្ចូលថ្ម"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"ពេញ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 50e5955..948c665315 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"ಸ್ವಯಂಚಾಲಿತ"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿಸಿ"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"ಆಯ್ಕೆಮಾಡಲಾದ WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ ಮತ್ತು ಬಳಸಲು ಸಕ್ರಿಯಗೊಳಿಸಬೇಕಾಗಿದೆ, ಇದನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ನೀವು ಬಯಸುತ್ತೀರಾ?"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ಬಣ್ಣದ ತಿದ್ದುಪಡಿ"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ಇದು ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯವಾಗಿದೆ. ಕಾರ್ಯಕ್ಷಮತೆ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರಬಹುದು."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"ಸುಮಾರು <xliff:g id="LEVEL">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> ಉಳಿದಿದೆ"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC ನಲ್ಲಿ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ಮೂಲಕ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ವೈರ್ಲೆಸ್ನಿಂದ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"ಅಜ್ಞಾತ"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ನಲ್ಲಿ ಚಾರ್ಜ್"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ಮೂಲಕ ಚಾರ್ಜ್"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ನಿಸ್ತಂತುವಾಗಿ ಚಾರ್ಜ್"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"ಭರ್ತಿ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index c23a8a7..7f5cee2 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"자동"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 구현"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView 구현 설정"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"색보정"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"실험실 기능이며 성능에 영향을 줄 수 있습니다."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> 우선 적용됨"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 대략 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(AC 전원)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(USB)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(무선)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"알 수 없음"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"충전 중"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"충전 중(AC 전원)"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"충전 중(USB)"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"충전 중(무선)"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"충전 안함"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"충전 안함"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"충전 완료"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index 84bfe94f..c00e827 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматтык"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView аткарылышы"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView аткарылышын коюу"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Түсүн тууралоо"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бул сынамык мүмкүнчүлүк болгондуктан, иштин майнаптуулугуна таасир этиши мүмкүн."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - болжол менен <xliff:g id="TIME">%2$s</xliff:g> саат калды"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> толгончо"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC аркылуу толгончо"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB аркылуу толгончо"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> зымсыз кубаттоо аркылуу толгончо"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Белгисиз"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Кубатталууда"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ӨА кубатталууда"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB\'ден кубатталууда"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Зымсыз кубатталууда"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Кубат алган жок"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Кубатталган жок"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Толук"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index 4b5359a..6dd3097 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"ອັດຕະໂນມັດ"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"ຕັ້ງການຈັດຕັ້ງປະຕິບັດ WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ການປັບແຕ່ງສີ"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ຄຸນສົມບັດນີ້ກຳລັງຢູ່ໃນການທົດລອງແລະອາດມີຜົນຕໍ່ປະສິດທິພາບ."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ເຫຼືອປະມານ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະເຕັມ"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະເຕັມໂດຍສາກດ້ວຍໄຟ AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະເຕັມໂດຍສາກດ້ວຍ USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະເຕັມໂດຍສາກແບບໄຮ້ສາຍ"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"ບໍ່ຮູ້ຈັກ"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"ກຳລັງສາກໄຟ"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ກຳລັງສາກຜ່ານໝໍ້ໄຟ"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"ກຳລັງສາກຜ່ານ USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ກຳລັງສາກໄຮ້ສາຍ"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"ບໍ່ໄດ້ສາກໄຟ"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ບໍ່ໄດ້ສາກໄຟ"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"ເຕັມ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 6b21041..6010c7a 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatinė"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"„WebView“ diegimas"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"„WebView“ diegimo nustatymas"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertuoti į failų šifruotę"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertuoti…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Jau konvertuota į failų šifruotę"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Spalvų taisymas"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ši funkcija yra eksperimentinė ir ji gali turėti įtakos našumui."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko maždaug <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo naud. kint. sr."</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo naudojant USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo belaid. ryš."</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Nežinomas"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Kraunasi..."</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Įkr. naud. kint. sr."</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Įkraunama naud. USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Įkraunama be laidų"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Nekraunama"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nekraunama"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Visiškai įkrautas"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 0652b05..d50e1ea 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automātiski"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ieviešana"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Iestatīt WebView ieviešanu"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Pārvērst par failu šifrējumu"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Pārvērst…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Jau šifrēts failu līmenī"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Krāsu korekcija"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Šī funkcija ir eksperimentāla un var ietekmēt veiktspēju."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> — aptuvenais atlikušais laiks: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai maiņstrāvas uzlādei"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai USB uzlādei"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai bezvadu uzlādei"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Nezināms"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Uzlāde"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Maiņstrāvas uzlāde"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB uzlāde"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezvadu uzlāde"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Nenotiek uzlāde"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenotiek uzlāde"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Pilns"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index b588c2c..03feddd 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматски"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Воведување WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Поставете воведување WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Корекција на боја"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Функцијата е експериментална и може да влијае на изведбата."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостанува приближно <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна на AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна преку USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна, безжично"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Се полни"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Полнење на струја"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Полнење преку УСБ"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Безжично полнење"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Не се полни"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се полни"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Целосна"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index 2457847..e47afc4 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"ഓട്ടോമാറ്റിക്"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView നടപ്പാക്കൽ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView നടപ്പാക്കൽ സജ്ജമാക്കുക"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"വർണ്ണം ക്രമീകരിക്കൽ"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ഈ ഫീച്ചർ പരീക്ഷണാത്മകമായതിനാൽ പ്രകടനത്തെ ബാധിച്ചേക്കാം."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഏകദേശം <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - AC-യിൽ പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB വഴി പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - വയർലെസ് വഴി പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"അജ്ഞാതം"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"ചാർജ്ജുചെയ്യുന്നു"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC-യിൽ ചാർജ്ജുചെയ്യുന്നു"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-യിലൂടെ ചാർജ്ജുചെയ്യുന്നു"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"വയർലെസ്സ് കണക്ഷനിലൂടെ ചാർജ്ജുചെയ്യുന്നു"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"നിറഞ്ഞു"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index 622c13f..01925f7 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматаар"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView хэрэгжилт"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView хэрэгжилтийг тохируулах"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Өнгө тохируулах"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Энэ функц туршилтынх бөгөөд ажиллагаанд нөлөөлж болзошгүй."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ойролцоогоор <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"АС-р дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"USB-р дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"утасгүй цэнэглэгчээр дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Тодорхойгүй"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Цэнэглэж байна"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC-р цэнэглэж байна"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-р цэнэглэж байна"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Кабльгүйгээр цэнэглэж байна"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Цэнэглэхгүй байна"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Цэнэглэхгүй байна"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Дүүрэн"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index ed57fb7..804fab0 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"स्वयंचलित"</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_confirmation_text" msgid="6671472080671066972">"निवडलेली WebView अंमलबजावणी अक्षम आहे आणि वापरण्यास सक्षम असणे आवश्यक आहे, आपण ती सक्षम करू इच्छिता?"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"रंग सुधारणा"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"हे वैशिष्ट्य प्रायोगिक आहे आणि कदाचित कार्यप्रदर्शन प्रभावित करू शकते."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - अंदाजे. <xliff:g id="TIME">%2$s</xliff:g> शिल्लक"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूर्ण होण्यात"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC वरून पूर्ण होण्यात"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB वरून पूर्ण होण्यात"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेसवरून पूर्ण होण्यात"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज होत आहे"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC वर चार्ज करीत आहे"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB वरून चार्ज करीत आहे"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"वायरलेस वरून चार्ज करीत आहे"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज होत नाही"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज होत नाही"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index 5f27232..ed4d071 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatik"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Pelaksanaan WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Tetapkan pelaksanaan WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Tukar kepada penyulitan fail"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Tukar..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Sudah disulitkan fail"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Pembetulan warna"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ciri ini adalah percubaan dan boleh menjejaskan prestasi."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Diatasi oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira. <xliff:g id="TIME">%2$s</xliff:g> yang tinggal"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh di AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh melalui USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh dari wayarles"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Mengecas"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Mengecas pada AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Mengecas melalui USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Mengecas tanpa wayar"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Tidak mengecas"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengecas"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index 0a86cdb..ab7a8d0 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"အလိုအလျောက်"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView အကောင်အထည်ဖော်မှု"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView အကောင်အထည်ဖော်မှု သတ်မှတ်ပါ"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"အရောင်ပြင်ဆင်မှု"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ဒီအင်္ဂါရပ်မှာ စမ်းသပ်မှု ဖြစ်၍ လုပ်ကိုင်မှုကို အကျိုးသက်ရောက်နိုင်သည်။"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ခန့်မှန်းခြေ။ <xliff:g id="TIME">%2$s</xliff:g> ကျန်ရှိနေ"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> အပြည့်အထိ"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> လျှပ်စစ်ဖြင့် အပြည့်အထိ"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ဖြင့် အပြည့်အထိ"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ကြိုးမဲ့ဖြင့် အပြည့်အထိ"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"အကြောင်းအရာ မသိရှိ"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"အားသွင်းနေပါသည်"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"လျှပ်စစ်ဖြင့် အားသွင်းနေ"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USBဖြင့် အားသွင်းနေ"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ကြိုးမဲ့ အားသွင်းနေ"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"အားသွင်းမနေပါ"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"အားသွင်းမနေပါ"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"အပြည့်"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 99ee44c..e39cf30 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatisk"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Angi WebView-implementering"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Den valgte implementeringen av nettvisningen er slått av – den må slås på for å brukes. Vil du slå den på?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertér til kryptert fil"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertér …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Allerede kryptert og lagret som fil"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Fargekorrigering"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Denne funksjonen er eksperimentell og kan påvirke ytelsen."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca. <xliff:g id="TIME">%2$s</xliff:g> igjen"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> med vekselstrøm"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> via USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> via trådløs lading"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Ukjent"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Lader"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Lader via strømuttak"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Lader via USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Lader trådløst"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Lader ikke"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Lader ikke"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index a109197..d9cfbcc 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"स्वचालित"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView कार्यान्वयन"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView कार्यान्वयन सेट गर्नुहोस्"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"रङ्ग सुधार"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यो सुविधा प्रयोगात्मक छ र प्रदर्शनमा असर गर्न सक्छ।"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - लगभग। <xliff:g id="TIME">%2$s</xliff:g> बायाँ"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूर्ण नभए सम्म"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC मा पूर्ण नभए सम्म"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB मा पूर्ण नभए सम्म"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेसबाट पूर्ण नभए सम्म"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हुँदै"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC मा चार्ज गर्दै"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB मा चार्ज गर्दै"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"बिना तार चार्ज गर्दै"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज भइरहेको छैन"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज हुँदै छैन"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 1dd477d..4873ac3 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatisch"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementatie"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-implementatie instellen"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"De gekozen WebView-implementatie is uitgeschakeld en moet worden ingeschakeld voor gebruik. Wil je deze inschakelen?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converteren naar versleuteling op basis van bestanden"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converteren…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Al versleuteld op basis van bestanden"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Kleurcorrectie"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Deze functie is experimenteel en kan invloed hebben op de prestaties."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overschreven door <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ca. <xliff:g id="TIME">%2$s</xliff:g> resterend"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via wisselstroom"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via draadloos"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Opladen"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Opladen via netvoeding"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Opladen via USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Draadloos opladen"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Wordt niet opgeladen"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wordt niet opgeladen"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Volledig"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index 85f9ffb..61f187d 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"ਆਟੋਮੈਟਿਕ"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ਅਮਲ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ਅਮਲ ਸੈੱਟ ਕਰੋ"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"ਚੁਣਿਆ ਗਿਆ WebView ਅਮਲ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ, ਅਤੇ ਵਰਤੋਂ ਕਰਨ ਲਈ ਇਸ ਨੂੰ ਯੋਗ ਬਣਾਇਆ ਜਾਣਾ ਜ਼ਰੂਰੀ ਹੈ, ਕੀ ਤੁਸੀਂ ਇਸ ਨੂੰ ਯੋਗ ਬਣਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"ਰੰਗ ਸੰਸ਼ੋਧਨ"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਾਤਮਿਕ ਹੈ ਅਤੇ ਪ੍ਰਦਰਸ਼ਨ ਤੇ ਅਸਰ ਪਾ ਸਕਦੀ ਹੈ।"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਲਗਭਗ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਪੂਰੀ ਹੋਣ ਤੱਕ"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC ਤੇ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ਤੇ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਵਾਇਰਲੈਸ ਤੋਂ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"ਅਗਿਆਤ"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"ਚਾਰਜਿੰਗ"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ਤੇ ਚਾਰਜਿੰਗ"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ਤੇ ਚਾਰਜਿੰਗ"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ਵਾਇਰਲੈਸ ਤੌਰ ਤੇ ਚਾਰਜਿੰਗ"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"ਪੂਰੀ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 9f7c022..0189fab 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatycznie"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementacja WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ustaw implementację WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Przekształć na szyfrowanie plików"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Przekształć…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Pliki są już zaszyfrowane"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korekcja kolorów"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To jest funkcja eksperymentalna i może wpływać na działanie urządzenia."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostało ok. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania z gniazdka"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania przez USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania bezprzewodowo"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Nieznane"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Ładowanie"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Ładowanie zasilaczem"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Ładowanie przez USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Ład. bezprzewodowe"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Nie podłączony"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nie podłączony"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Naładowana"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 98a3121..ee701a6 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementação do WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configurar implementação do WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter para criptografia de arquivos"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Já criptografado com base em arquivos"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correção de cor"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restantes"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir em CA"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir via USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir sem fio"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carregamento CA"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carregamento via USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carregamento sem fio"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Não está carregando"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 0cbe5e4..2f25ae2 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementação WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Definir implementação WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"A implementação WebView escolhida foi desativada e tem de ser ativada para poder ser utilizada. Pretende ativá-la?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter para a encriptação de ficheiros"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Os ficheiros já estão encriptados"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correção da cor"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta funcionalidade é experimental e pode afetar o desempenho."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – resta(m) aprox. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa através de CA"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa através de USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até ficar compl. por rede s/ fios"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"A carregar"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"A carregar por CA"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"A carregar por USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"A carregar sem fios"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Não está a carregar"</string>
+ <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>
</resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 98a3121..ee701a6 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementação do WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configurar implementação do WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter para criptografia de arquivos"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Já criptografado com base em arquivos"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Correção de cor"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restantes"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir em CA"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir via USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir sem fio"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carregamento CA"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carregamento via USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carregamento sem fio"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Não está carregando"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 00ff92b..29d878f 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automat"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementare WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Setați implementarea WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Faceți conversia la criptarea bazată pe sistemul de fișiere"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertiți…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Criptarea bazată pe sistemul de fișiere este finalizată"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Corecția culorii"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Această funcție este experimentală și poate afecta performanțele."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – timp rămas: aproximativ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă la c.a."</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă prin USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă wireless"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Necunoscut"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Încarcă"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Se încarcă la C.A."</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Se încarcă prin USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Se încarcă fără fir"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Nu se încarcă"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nu încarcă"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Complet"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 9a3a7e6..ba21d3e 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматическое переключение"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Сервис WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Настройки сервиса WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Коррекция цвета"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Это экспериментальная функция, она может снизить производительность устройства."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – осталось около <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (от сети)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (через USB)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (беспроводная)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Идет зарядка"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зарядка от сети"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зарядка через USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Беспроводная зарядка"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Не заряжается"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряжается"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Батарея заряжена"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index 083dd05..0f32a0c 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"ස්වයංක්රීය"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ක්රියාත්මක කිරීම"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ක්රියාත්මක කිරීම සකසන්න"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"වර්ණ නිවැරදි කිරීම"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"මෙම විශේෂාංගය පරීක්ෂණාත්මක සහ ඇතැම් විට ක්රියාකාරිත්වයට බලපෑ හැක."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> මගින් ඉක්මවන ලදී"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආසන්න <xliff:g id="TIME">%2$s</xliff:g> වම"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"AC හි <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"USB හරහ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"රේඩියෝව වෙතින් <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පූර්ණ වන තෙක්"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"නොදනී"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"ආරෝපණය වෙමින්"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC හි ආරෝපණය වෙමින්"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB හරහා ආරෝපණය වෙමින්"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"රැහැන් රහිතව ආරෝපණය වෙමින්"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"ආරෝපණය නොවේ"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ආරෝපණය නොවෙමින්"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"පූර්ණ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index e12f435..8df02ea 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatický"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Implementácia komponenta WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Nastavenie implementácie komponenta WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Zvolená implementácia technológie WebView je zakázaná. Ak ju chcete použiť, musíte ju najprv povoliť. Chcete ju povoliť?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertovať na šifrovanie súborov"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertovať…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Súbory sú už šifrované"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Úprava farieb"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkcia je experimentálna a môže mať vplyv na výkonnosť."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostáva približne <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia zo zásuvky"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia cez USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia bezdrôtovo"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznáme"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjanie"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nabíjanie zo zásuvky"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nabíjanie cez USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezdrôtové nabíjanie"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Nenabíja sa"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíja sa"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 1f66583..5d47af9 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Samodejno"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Izvedba spletnega pogleda"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Nastavitev izvedbe spletnega pogleda"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Preklop na šifriranje podatkov"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Preklop …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Šifriranje podatkov je že uveljavljeno"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Popravljanje barv"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To je preskusna funkcija in lahko vpliva na učinkovitost delovanja."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – še približno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek napajalnika"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek USB-ja"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek brezž. pol."</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznano"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Polnjenje"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Polnj. prek iz. toka"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Polnj. prek USB-ja"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Brezžično polnjenje"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Se ne polni"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Se ne polni"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Poln"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index 7f4d6a8..8e8112f 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatike"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Zbatimi i WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Cakto zbatimin e WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konverto në enkriptimin e skedarit"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konverto..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Enkriptimi i skedarit është kryer tashmë"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korrigjimi i ngjyrës"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ky funksion është eksperimental dhe mund të ndikojë në veprimtari."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - afërsisht <xliff:g id="TIME">%2$s</xliff:g> të mbetura"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të jetë e plotë"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet në AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet me USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet nga lidhja pa tel"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"I panjohur"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Po ngarkohet"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Po ngarkohet në AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Po ngarkohet me USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Po ngarkohet me valë"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Nuk po ngarkohet"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nuk po ngarkohet"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"E mbushur"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index ab96afd..935f44d 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Аутоматски"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Примена WebView-а"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Подесите примену WebView-а"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Корекција боја"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ова функција је експериментална и може да утиче на перформансе."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замењује га <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостало око <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни пуњачем"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни преко USB-а"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни бежично"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Пуњење"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Пуњење преко пуњача"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Пуњење преко USB-а"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Бежично пуњење"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Не пуни се"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не пуни се"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Пуно"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index f8901c9..1bdf6b2 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Automatiskt"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ange WebView-implementering"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Den valda WebView-implementeringen har inaktiverats och måste aktiveras om du ska kunna använda den. Vill du aktivera den?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertera till filkryptering"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertera …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Filkryptering används redan"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Färgkorrigering"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Den här funktionen är experimentell och kan påverka prestandan."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca <xliff:g id="TIME">%2$s</xliff:g> kvar"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via laddare"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via trådlös laddning"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Okänd"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Laddar"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laddas via adapter"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laddas via USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Laddas trådlöst"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Laddar inte"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laddar inte"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 8a33946..8a5ddd0 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Otomatiki"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Utekelezaji wa WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Weka utekelezaji wa WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Badilisha kuwa usimbaji fiche wa faili"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Badilisha..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Tayari faili imesimbwa kwa njia fiche"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Usahihishaji wa rangi"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Kipengele hiki ni cha majaribio na huenda kikaathiri utendaji."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Imetanguliwa na <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia takriban <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa kutumia AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>%% - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa kutumia USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa isiyotumia waya"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Haijulikani"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Inachaji"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Inachaji kupitia AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Inachaji kupitia USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Inachaji bila kutumia waya"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Haichaji"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Haichaji"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Imejaa"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index d386039..42a5534 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"தானியங்கு"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView செயல்படுத்தல்"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView செயல்படுத்தலை அமை"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"வண்ணத்திருத்தம்"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"இது சோதனை முறையிலான அம்சம், இது செயல்திறனைப் பாதிக்கலாம்."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"தோராயம்: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> உள்ளது"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"முழு சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"முழு AC சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"முழு USB சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"முழு வயர்லெஸ் சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"அறியப்படாத"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"சார்ஜ் ஏற்றப்படுகிறது"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC மூலம் சார்ஜாகிறது"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB மூலம் சார்ஜாகிறது"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"வயர்லெஸில் சார்ஜாகிறது"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"சார்ஜ் செய்யப்படவில்லை"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"சார்ஜ் ஏறவில்லை"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"முழுமை"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index 80b84f0..04f0ca0 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"స్వయంచాలకం"</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_confirmation_text" msgid="6671472080671066972">"ఎంచుకున్న వెబ్ వీక్షణ అమలు నిలిపివేయబడింది, కానీ ఉపయోగించడానికి తప్పనిసరిగా ప్రారంభించాల్సి ఉంటుంది, మీరు దీన్ని ప్రారంభించాలనుకుంటున్నారా?"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"రంగు సవరణ"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ఈ లక్షణం ప్రయోగాత్మకమైనది మరియు పనితీరుపై ప్రభావం చూపవచ్చు."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - సుమారు <xliff:g id="TIME">%2$s</xliff:g> మిగిలి ఉంది"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ACలో పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB ద్వారా పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - వైర్లెస్ నుండి పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"తెలియదు"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"ఛార్జ్ అవుతోంది"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ACలో ఛార్జ్ అవుతోంది"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ద్వారా ఛార్జ్ అవుతోంది"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"వైర్లెస్ ద్వారా ఛార్జ్ అవుతోంది"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"ఛార్జ్ కావడం లేదు"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ఛార్జ్ కావడం లేదు"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"నిండింది"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index b17b516..241fd06 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"อัตโนมัติ"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"การใช้งาน WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"ตั้งค่าการใช้งาน WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"การแก้สี"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"คุณลักษณะนี้เป็นแบบทดลองและอาจส่งผลต่อประสิทธิภาพการทำงาน"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"แทนที่โดย <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - เหลือประมาณ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็ม"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่าน AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่าน USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่านระบบไร้สาย"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"ไม่ทราบ"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"กำลังชาร์จ"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"กำลังชาร์จไฟ AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"กำลังชาร์จผ่าน USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"กำลังชาร์จแบบไร้สาย"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"ไม่ได้ชาร์จ"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ไม่ได้ชาร์จ"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"เต็ม"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index d0e7f87..d1a525b 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Awtomatiko"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Pagpapatupad sa WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Itakda ang pagpapatupad sa WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"I-convert at gawing pag-encrypt ng file"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"I-convert..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Na-encrypt na ang file"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Pagtatama ng kulay"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ang feature na ito ay pinag-eeksperimentuhan at maaaring makaapekto sa pagganap."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Na-override ng <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - humigit kumulang <xliff:g id="TIME">%2$s</xliff:g> ang natitira"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno sa AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno sa USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno mula sa wireless"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Hindi Kilala"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Nagcha-charge"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nagcha-charge sa AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nagcha-charge sa USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Wireless nag-charge"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Hindi nagcha-charge"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hindi nagkakarga"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index e6ddcdd..d4c211c 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Otomatik"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView kullanımı"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView kullanımını ayarla"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Dosya şifrelemeye dönüştür"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Dönüştür…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Dosya şifreleme zaten uygulandı"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Renk düzeltme"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu özellik deneyseldir ve performansı etkileyebilir."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - yaklaşık <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - prize takılı, tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB üzerinden şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - kablosuzdan tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Bilinmiyor"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Şarj oluyor"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ile şarj oluyor"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ile şarj oluyor"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Kablosuz şarj oluyor"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Şarj olmuyor"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Şarj etmiyor"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Dolu"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 2448b23..3e89cce 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматично"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Застосування WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Налаштувати застосування WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Вибране застосування WebView вимкнено. Увімкнути його?"</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>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Корекція кольору"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Це експериментальна функція. Вона може вплинути на продуктивність."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – залишилось близько <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження з розетки"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження через USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного з бездротового зарядження"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Невідомо"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарядж-ся"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Заряджання з розетки"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Заряджання через USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Заряджання без дроту"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Не заряджається"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряджається"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Акумулятор заряджено"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index e9fe7e4..0a2adf7 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"خودکار"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView کا نفاذ"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView کا نفاذ سیٹ کریں"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"رنگ کی اصلاح"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"یہ خصوصیت تجرباتی ہے اور اس کی وجہ سے کاکردگی متاثر ہو سکتی ہے۔"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تقریبا <xliff:g id="TIME">%2$s</xliff:g> باقی"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> پورا ہونے تک"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC پر پورا ہونے تک"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB پر پورا ہونے تک"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> وائرلیس سے پورا ہونے تک"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"نامعلوم"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"چارج ہو رہا ہے"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC پر چارج ہو رہی ہے"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB پر چارج ہورہی ہے"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"وائرلیس چارجنگ"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"چارج نہیں ہو رہا ہے"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"چارج نہیں ہو رہا ہے"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"مکمل"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index e53336b..13494a5 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Avtomatik"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ta’minotchisi"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ta’minotchisini sozlash"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Faylli shifrga o‘girish"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"O‘girish…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fayl allaqachon shifrlangan"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Rangni tuzatish"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya tajribaviy bo‘lib, u qurilma unumdorligiga ta’sir qilishi mumkin."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> bilan almashtirildi"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – taxminan <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, to‘lguncha"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, o‘zgaruvchan tok orqali to‘lguncha"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, USB orqali to‘lguncha"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, simsiz quvvatlash orqali to‘lguncha"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Noma’lum"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Quvvat olmoqda"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Quvvat olmoqda (AC)"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Quvvat olmoqda (USB)"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Simsiz quvvat olmoqda"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Quvvatlantirilmayapti"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Quvvatlanmayapti"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"To‘la"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 193f066..81b87da 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Tự động"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Triển khai WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Đặt triển khai WebView"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Chuyển đổi sang mã hóa tệp"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Chuyển đổi..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Đã mã hóa tệp"</string>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Sửa màu"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tính năng này là tính năng thử nghiệm và có thể ảnh hưởng đến hoạt động."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Bị ghi đè bởi <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - còn khoảng <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy khi cắm vào nguồn AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy qua USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy từ không dây"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Không xác định"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Đang sạc"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Sạc trên AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Sạc qua USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Sạc không dây"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Hiện không sạc"</string>
+ <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>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index de8996e..5ffcc0c 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"自动"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 实现"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"设置 WebView 实现"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色彩校正"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"此功能为实验性功能,可能会影响性能。"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还可用大约<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(交流电充电)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(USB充电)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(无线充电)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"正在充电"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在通过交流电源充电"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在通过USB充电"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在无线充电"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"未在充电"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"未在充电"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"电量充足"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 9a86f3a..8eedf6d 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"自動"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 設置"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"設定 WebView 設置"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色彩校正"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會影響效能。"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 尚餘大約 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (透過插頭充電)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (透過 USB 充電)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (無線充電)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在透過 AC 充電"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在透過 USB 充電"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在透過無線方式充電"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"非充電中"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"未開始充電"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"電量已滿"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 3ab60f5..10d2e01f 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -277,6 +277,8 @@
<string name="night_mode_auto" msgid="7508348175804304327">"自動"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 實作"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"設定 WebView 實作"</string>
+ <!-- no translation found for select_webview_provider_confirmation_text (6671472080671066972) -->
+ <skip />
<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>
@@ -293,4 +295,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"色彩校正"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會對效能造成影響。"</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"已改為<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 大約還剩 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (AC)"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (USB)"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (無線充電)"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在透過 AC 變壓器充電"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在透過 USB 充電"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在透過無線方式充電"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"非充電中"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"非充電中"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"電力充足"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index bf3addf..f42abbc 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -277,6 +277,7 @@
<string name="night_mode_auto" msgid="7508348175804304327">"Okuzenzakalelayo"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"Ukufakwa ke-WebView"</string>
<string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Sesba ukufakwa kwe-WebView"</string>
+ <string name="select_webview_provider_confirmation_text" msgid="6671472080671066972">"Ukusetshenziswa kwe-WebView okukhethiwe kukhutshaziwe, futhi kuzomele kunikwe amandla ukuze kusetshenziswe, ingabe ufisa ukukunika amandla?"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Phendulisela ekubethelweni kwefayela"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Iyaphendulela..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Sekuvele kubethelwe ngefayela"</string>
@@ -293,4 +294,18 @@
<string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Ukulungiswa kombala"</string>
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Lesi sici esesilingo futhi singathinta ukusebenza."</string>
<string name="daltonizer_type_overridden" msgid="3116947244410245916">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - isilinganiso esingu-<xliff:g id="TIME">%2$s</xliff:g> esisele"</string>
+ <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
+ <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale"</string>
+ <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale ku-AC"</string>
+ <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale ngaphezulu kwe-USB"</string>
+ <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale kusukela kokungenantambo"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"Akwaziwa"</string>
+ <string name="battery_info_status_charging" msgid="1705179948350365604">"Iyashaja"</string>
+ <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Iyashaja ku-AC"</string>
+ <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Iyashaja ngaphezulu kwe-USB"</string>
+ <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Iyashaja ngaphandle kwentambo"</string>
+ <string name="battery_info_status_discharging" msgid="310932812698268588">"Ayishaji"</string>
+ <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ayishaji"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Kugcwele"</string>
</resources>
diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml
new file mode 100644
index 0000000..46267a2
--- /dev/null
+++ b/packages/SettingsLib/res/values/attrs.xml
@@ -0,0 +1,21 @@
+<?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>
+ <declare-styleable name="RestrictedPreference">
+ <attr name="userRestriction" format="string"/>
+ </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
new file mode 100644
index 0000000..c090468
--- /dev/null
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <color name="disabled_text_color">#66000000</color> <!-- 38% black -->
+</resources>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index d7c78f6..9a1d6a4 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -31,4 +31,8 @@
<dimen name="user_spinner_padding">4dp</dimen>
<dimen name="user_spinner_padding_sides">20dp</dimen>
<dimen name="user_spinner_item_height">56dp</dimen>
+
+ <!-- Lock icon for preferences locked by admin -->
+ <dimen name="restricted_lock_icon_size">16dp</dimen>
+ <dimen name="restricted_lock_icon_padding">4dp</dimen>
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index ac19cf5..6dfa9ad 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -684,6 +684,8 @@
<string name="select_webview_provider_title">WebView implementation</string>
<!-- Developer settings: select WebView provider dialog title -->
<string name="select_webview_provider_dialog_title">Set WebView implementation</string>
+ <!-- Developer settings: confirmation dialog text for the WebView provider selection dialog -->
+ <string name="select_webview_provider_confirmation_text">The chosen WebView implementation is disabled, and must be enabled to be used, do you wish to enable it?</string>
<!-- Developer settings screen, convert userdata to file encryption option name -->
<string name="convert_to_file_encryption">Convert to file encryption</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
new file mode 100644
index 0000000..c2f885d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.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.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_lock_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/RestrictedLockImageSpan.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java
new file mode 100644
index 0000000..e63130d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockImageSpan.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.text.style.ImageSpan;
+
+/**
+ * An extension of ImageSpan which adds a padding before the image.
+ */
+public class RestrictedLockImageSpan extends ImageSpan {
+ private Context mContext;
+ private final float mExtraPadding;
+ private final Drawable mRestrictedPadlock;
+
+ public RestrictedLockImageSpan(Context context) {
+ // we are overriding getDrawable, so passing null to super class here.
+ super((Drawable) null);
+
+ mContext = context;
+ mExtraPadding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.restricted_lock_icon_padding);
+ mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext);
+ }
+
+ @Override
+ public Drawable getDrawable() {
+ return mRestrictedPadlock;
+ }
+
+ @Override
+ public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
+ int bottom, Paint paint) {
+ Drawable drawable = getDrawable();
+ canvas.save();
+
+ // Add extra padding before the padlock.
+ float transX = x + mExtraPadding;
+ float transY = bottom - drawable.getBounds().bottom - paint.getFontMetricsInt().descent;
+
+ canvas.translate(transX, transY);
+ drawable.draw(canvas);
+ canvas.restore();
+ }
+
+ @Override
+ public int getSize(Paint paint, CharSequence text, int start, int end,
+ Paint.FontMetricsInt fontMetrics) {
+ int size = super.getSize(paint, text, start, end, fontMetrics);
+ size += 2 * mExtraPadding;
+ return size;
+ }
+}
\ 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
new file mode 100644
index 0000000..2706e25
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.Spanned;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.ImageSpan;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Utility class to host methods usable in adding a restricted padlock icon and showing admin
+ * support message dialog.
+ */
+public class RestrictedLockUtils {
+ /**
+ * @return drawables for displaying with settings that are locked by a device admin.
+ */
+ public static Drawable getRestrictedPadlock(Context context) {
+ Drawable restrictedPadlock = context.getDrawable(R.drawable.ic_settings_lock_outline);
+ final int iconSize = context.getResources().getDimensionPixelSize(
+ R.dimen.restricted_lock_icon_size);
+ restrictedPadlock.setBounds(0, 0, iconSize, iconSize);
+ return restrictedPadlock;
+ }
+
+ /**
+ * Checks if a restriction is enforced on a user and returns the enforced admin and
+ * admin userId.
+ *
+ * @param userRestriction Restriction to check
+ * @param userId User which we need to check if restriction is enforced on.
+ * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
+ * {@code null} If the restriction is not set. If the restriction is set by both device owner
+ * and profile owner, then the admin will be set to {@code null} and userId to
+ * {@link UserHandle#USER_NULL}.
+ */
+ public static EnforcedAdmin checkIfRestrictionEnforced(Context context,
+ String userRestriction, int userId) {
+ DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
+ int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
+ boolean enforcedByDeviceOwner = false;
+ if (deviceOwner != null && deviceOwnerUserId != UserHandle.USER_NULL) {
+ Bundle enforcedRestrictions = dpm.getUserRestrictions(deviceOwner, deviceOwnerUserId);
+ if (enforcedRestrictions != null
+ && enforcedRestrictions.getBoolean(userRestriction, false)) {
+ enforcedByDeviceOwner = true;
+ }
+ }
+
+ ComponentName profileOwner = null;
+ boolean enforcedByProfileOwner = false;
+ if (userId != UserHandle.USER_NULL) {
+ profileOwner = dpm.getProfileOwnerAsUser(userId);
+ if (profileOwner != null) {
+ Bundle enforcedRestrictions = dpm.getUserRestrictions(profileOwner, userId);
+ if (enforcedRestrictions != null
+ && enforcedRestrictions.getBoolean(userRestriction, false)) {
+ enforcedByProfileOwner = true;
+ }
+ }
+ }
+
+ if (!enforcedByDeviceOwner && !enforcedByProfileOwner) {
+ return null;
+ }
+
+ EnforcedAdmin admin = null;
+ if (enforcedByDeviceOwner && enforcedByProfileOwner) {
+ admin = new EnforcedAdmin();
+ } else if (enforcedByDeviceOwner) {
+ admin = new EnforcedAdmin(deviceOwner, deviceOwnerUserId);
+ } else {
+ admin = new EnforcedAdmin(profileOwner, userId);
+ }
+ return admin;
+ }
+
+ /**
+ * Checks if lock screen notification features are disabled by policy. This should be
+ * only used for keyguard notification features but not the keyguard features
+ * (e.g. KEYGUARD_DISABLE_FINGERPRINT) where a profile owner can set them on the parent user
+ * as it won't work for that case.
+ *
+ * @param keyguardNotificationFeatures Could be any of notification features that can be
+ * disabled by {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}.
+ * @return EnforcedAdmin Object containing the enforce admin and admin user details, or
+ * {@code null} If the notification features are not disabled. If the restriction is set by
+ * multiple admins, then the admin will be set to {@code null} and userId to
+ * {@link UserHandle#USER_NULL}.
+ */
+ public static EnforcedAdmin checkIfKeyguardNotificationFeaturesDisabled(Context context,
+ int keyguardNotificationFeatures) {
+ final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ boolean isDisabledByMultipleAdmins = false;
+ ComponentName adminComponent = null;
+ List<ComponentName> admins = dpm.getActiveAdmins();
+ int disabledKeyguardFeatures;
+ for (ComponentName admin : admins) {
+ disabledKeyguardFeatures = dpm.getKeyguardDisabledFeatures(admin);
+ if ((disabledKeyguardFeatures & keyguardNotificationFeatures) != 0) {
+ if (adminComponent == null) {
+ adminComponent = admin;
+ } else {
+ isDisabledByMultipleAdmins = true;
+ break;
+ }
+ }
+ }
+ EnforcedAdmin enforcedAdmin = null;
+ if (adminComponent != null) {
+ if (!isDisabledByMultipleAdmins) {
+ enforcedAdmin = new EnforcedAdmin(adminComponent, UserHandle.myUserId());
+ } else {
+ enforcedAdmin = new EnforcedAdmin();
+ }
+ }
+ return enforcedAdmin;
+ }
+
+ /**
+ * Set the menu item as disabled by admin by adding a restricted padlock at the end of the
+ * text and set the click listener which will send an intent to show the admin support details
+ * dialog.
+ */
+ public static void setMenuItemAsDisabledByAdmin(final Context context,
+ final MenuItem item, final EnforcedAdmin admin) {
+ SpannableStringBuilder sb = new SpannableStringBuilder(item.getTitle());
+ removeExistingRestrictedSpans(sb);
+
+ final int disabledColor = context.getColor(R.color.disabled_text_color);
+ sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ImageSpan image = new RestrictedLockImageSpan(context);
+ sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ item.setTitle(sb);
+
+ item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ sendShowAdminSupportDetailsIntent(context, admin);
+ return true;
+ }
+ });
+ }
+
+ private static void removeExistingRestrictedSpans(SpannableStringBuilder sb) {
+ final int length = sb.length();
+ RestrictedLockImageSpan[] imageSpans = sb.getSpans(length - 1, length,
+ RestrictedLockImageSpan.class);
+ for (ImageSpan span : imageSpans) {
+ sb.removeSpan(span);
+ }
+ ForegroundColorSpan[] colorSpans = sb.getSpans(0, length, ForegroundColorSpan.class);
+ for (ForegroundColorSpan span : colorSpans) {
+ sb.removeSpan(span);
+ }
+ }
+
+ /**
+ * Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}.
+ */
+ public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
+ final Intent intent = getShowAdminSupportDetailsIntent(context, admin);
+ int adminUserId = UserHandle.myUserId();
+ if (admin.userId != UserHandle.USER_NULL) {
+ adminUserId = admin.userId;
+ }
+ context.startActivityAsUser(intent, new UserHandle(adminUserId));
+ }
+
+ public static Intent getShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
+ final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
+ if (admin != null) {
+ if (admin.component != null) {
+ intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin.component);
+ }
+ int adminUserId = UserHandle.myUserId();
+ if (admin.userId != UserHandle.USER_NULL) {
+ adminUserId = admin.userId;
+ }
+ intent.putExtra(Intent.EXTRA_USER_ID, adminUserId);
+ }
+ return intent;
+ }
+
+ public static void setTextViewPadlock(Context context,
+ TextView textView, boolean showPadlock) {
+ final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText());
+ removeExistingRestrictedSpans(sb);
+ if (showPadlock) {
+ final ImageSpan image = new RestrictedLockImageSpan(context);
+ sb.append(" ", image, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ textView.setText(sb);
+ }
+
+ public static class EnforcedAdmin {
+ public ComponentName component = null;
+ public int userId = UserHandle.USER_NULL;
+
+ public EnforcedAdmin(ComponentName component, int userId) {
+ this.component = component;
+ this.userId = userId;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) return true;
+ if (!(object instanceof EnforcedAdmin)) return false;
+ EnforcedAdmin other = (EnforcedAdmin) object;
+ if (userId != other.userId) {
+ return false;
+ }
+ if ((component == null && other == null) ||
+ (component != null && component.equals(other))) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "EnforcedAdmin{component=" + component + ",userId=" + userId + "}";
+ }
+
+ public void copyTo(EnforcedAdmin other) {
+ if (other == null) {
+ other = new EnforcedAdmin();
+ }
+ other.component = component;
+ other.userId = userId;
+ }
+
+ public EnforcedAdmin() {}
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
new file mode 100644
index 0000000..13a46d0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os.UserHandle;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Preference class that supports being disabled by a user restriction
+ * set by a device admin.
+ */
+public class RestrictedPreference extends Preference {
+ RestrictedPreferenceHelper mHelper;
+
+ public RestrictedPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mHelper = new RestrictedPreferenceHelper(context, this, attrs);
+ }
+
+ public RestrictedPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public RestrictedPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
+ android.R.attr.preferenceStyle));
+ }
+
+ public RestrictedPreference(Context context) {
+ this(context, null);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ mHelper.onBindViewHolder(holder);
+ }
+
+ @Override
+ public void performClick() {
+ if (!mHelper.performClick()) {
+ super.performClick();
+ }
+ }
+
+ @Override
+ protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
+ mHelper.onAttachedToHierarchy();
+ super.onAttachedToHierarchy(preferenceManager);
+ }
+
+ public void checkRestrictionAndSetDisabled(String userRestriction) {
+ mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
+ }
+
+ public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+ mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled && isDisabledByAdmin()) {
+ mHelper.setDisabledByAdmin(null);
+ return;
+ }
+ super.setEnabled(enabled);
+ }
+
+ public void setDisabledByAdmin(EnforcedAdmin admin) {
+ if (mHelper.setDisabledByAdmin(admin)) {
+ notifyChanged();
+ }
+ }
+
+ public boolean isDisabledByAdmin() {
+ return mHelper.isDisabledByAdmin();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
new file mode 100644
index 0000000..06aba96
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+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.widget.TextView;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Helper class for managing settings preferences that can be disabled
+ * by device admins via user restrictions.
+ */
+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;
+ private String mAttrUserRestriction = null;
+
+ public RestrictedPreferenceHelper(Context context, Preference preference,
+ AttributeSet attrs) {
+ mContext = context;
+ mPreference = preference;
+
+ mRestrictedPadlock = RestrictedLockUtils.getRestrictedPadlock(mContext);
+ mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.restricted_lock_icon_padding);
+
+ if (attrs != null) {
+ final TypedArray attributes = context.obtainStyledAttributes(attrs,
+ R.styleable.RestrictedPreference);
+ final TypedValue userRestriction =
+ attributes.peekValue(R.styleable.RestrictedPreference_userRestriction);
+ CharSequence data = null;
+ if (userRestriction != null && userRestriction.type == TypedValue.TYPE_STRING) {
+ if (userRestriction.resourceId != 0) {
+ data = context.getText(userRestriction.resourceId);
+ } else {
+ data = userRestriction.string;
+ }
+ }
+ mAttrUserRestriction = data == null ? null : data.toString();
+ }
+ }
+
+ /**
+ * Modify PreferenceViewHolder to add padlock if restriction is disabled.
+ */
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ final TextView titleView = (TextView) holder.findViewById(android.R.id.title);
+ if (titleView != null) {
+ RestrictedLockUtils.setTextViewPadlock(mContext, titleView, mDisabledByAdmin);
+ if (mDisabledByAdmin) {
+ holder.itemView.setEnabled(true);
+ }
+ }
+ }
+
+ /**
+ * Check if the preference is disabled if so handle the click by informing the user.
+ *
+ * @return true if the method handled the click.
+ */
+ public boolean performClick() {
+ if (mDisabledByAdmin) {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Disable / enable if we have been passed the restriction in the xml.
+ */
+ public void onAttachedToHierarchy() {
+ if (mAttrUserRestriction != null) {
+ checkRestrictionAndSetDisabled(mAttrUserRestriction, UserHandle.myUserId());
+ }
+ }
+
+ /**
+ * Set the user restriction that is used to disable this preference.
+ *
+ * @param userRestriction constant from {@link android.os.UserManager}
+ * @param userId user to check the restriction for.
+ */
+ public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+ EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+ userRestriction, userId);
+ setDisabledByAdmin(admin);
+ }
+
+ /**
+ * Disable this preference based on the enforce admin.
+ *
+ * @param EnforcedAdmin Details of the admin who enforced the restriction. If it
+ * is {@code null}, then this preference will be enabled. Otherwise, it will be disabled.
+ * @return true if the disabled state was changed.
+ */
+ public boolean setDisabledByAdmin(EnforcedAdmin admin) {
+ final boolean disabled = (admin != null ? true : false);
+ mEnforcedAdmin = (disabled ? admin : null);
+ if (mDisabledByAdmin != disabled) {
+ mDisabledByAdmin = disabled;
+ mPreference.setEnabled(!disabled);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isDisabledByAdmin() {
+ return mDisabledByAdmin;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
new file mode 100644
index 0000000..84e2bff
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.os.UserHandle;
+import android.support.v4.content.res.TypedArrayUtils;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.support.v14.preference.SwitchPreference;
+import android.util.AttributeSet;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Version of SwitchPreference that can be disabled by a device admin
+ * using a user restriction.
+ */
+public class RestrictedSwitchPreference extends SwitchPreference {
+ RestrictedPreferenceHelper mHelper;
+
+ public RestrictedSwitchPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mHelper = new RestrictedPreferenceHelper(context, this, attrs);
+ }
+
+ public RestrictedSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public RestrictedSwitchPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.switchPreferenceStyle,
+ android.R.attr.switchPreferenceStyle));
+ }
+
+ public RestrictedSwitchPreference(Context context) {
+ this(context, null);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ mHelper.onBindViewHolder(holder);
+ }
+
+ @Override
+ public void performClick() {
+ if (!mHelper.performClick()) {
+ super.performClick();
+ }
+ }
+
+ @Override
+ protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
+ mHelper.onAttachedToHierarchy();
+ super.onAttachedToHierarchy(preferenceManager);
+ }
+
+ public void checkRestrictionAndSetDisabled(String userRestriction) {
+ mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId());
+ }
+
+ public void checkRestrictionAndSetDisabled(String userRestriction, int userId) {
+ mHelper.checkRestrictionAndSetDisabled(userRestriction, userId);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled && isDisabledByAdmin()) {
+ mHelper.setDisabledByAdmin(null);
+ return;
+ }
+ super.setEnabled(enabled);
+ }
+
+ public void setDisabledByAdmin(EnforcedAdmin admin) {
+ if (mHelper.setDisabledByAdmin(admin)) {
+ notifyChanged();
+ }
+ }
+
+ public boolean isDisabledByAdmin() {
+ return mHelper.isDisabledByAdmin();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
new file mode 100755
index 0000000..77f2e19
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -0,0 +1,223 @@
+/*
+ * 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.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothA2dpSink;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+final class A2dpSinkProfile implements LocalBluetoothProfile {
+ private static final String TAG = "A2dpSinkProfile";
+ private static boolean V = true;
+
+ private BluetoothA2dpSink mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+
+ static final ParcelUuid[] SRC_UUIDS = {
+ BluetoothUuid.AudioSource,
+ BluetoothUuid.AdvAudioDist,
+ };
+
+ static final String NAME = "A2DPSink";
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 5;
+
+ // These callbacks run on the main thread.
+ private final class A2dpSinkServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothA2dpSink) proxy;
+ // We just bound to the service, so refresh the UI for any connected A2DP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "A2dpSinkProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(A2dpSinkProfile.this, BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ A2dpSinkProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new A2dpSinkServiceListener(),
+ BluetoothProfile.A2DP_SINK);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> srcs = getConnectedDevices();
+ if (srcs != null) {
+ for (BluetoothDevice src : srcs) {
+ if (src.equals(device)) {
+ // Connect to same device, Ignore it
+ Log.d(TAG,"Ignoring Connect");
+ return true;
+ }
+ }
+ for (BluetoothDevice src : srcs) {
+ mService.disconnect(src);
+ }
+ }
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ // Downgrade priority as user is disconnecting the headset.
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ boolean isA2dpPlaying() {
+ if (mService == null) return false;
+ List<BluetoothDevice> srcs = mService.getConnectedDevices();
+ if (!srcs.isEmpty()) {
+ if (mService.isA2dpPlaying(srcs.get(0))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ // we need to have same string in UI for even SINK Media Audio.
+ return R.string.bluetooth_profile_a2dp;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_a2dp_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_a2dp_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_headphones_a2dp;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.A2DP_SINK,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up A2DP proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index d994841..7ee53a2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -851,7 +851,8 @@
case BluetoothProfile.STATE_DISCONNECTED:
if (profile.isProfileReady()) {
- if (profile instanceof A2dpProfile) {
+ if ((profile instanceof A2dpProfile)||
+ (profile instanceof A2dpSinkProfile)){
a2dpNotConnected = true;
} else if (profile instanceof HeadsetProfile) {
headsetNotConnected = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
old mode 100644
new mode 100755
index f935f31..9c5abf3
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -162,6 +162,10 @@
if (a2dp != null && a2dp.isA2dpPlaying()) {
return;
}
+ A2dpSinkProfile a2dpSink = mProfileManager.getA2dpSinkProfile();
+ if ((a2dpSink != null) && (a2dpSink.isA2dpPlaying())){
+ return;
+ }
}
if (mAdapter.startDiscovery()) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
old mode 100644
new mode 100755
index 8f5e1f1..b05e34c
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -17,6 +17,7 @@
package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothMap;
@@ -73,6 +74,7 @@
private final BluetoothEventManager mEventManager;
private A2dpProfile mA2dpProfile;
+ private A2dpSinkProfile mA2dpSinkProfile;
private HeadsetProfile mHeadsetProfile;
private MapProfile mMapProfile;
private final HidProfile mHidProfile;
@@ -136,10 +138,10 @@
* @param uuids
*/
void updateLocalProfiles(ParcelUuid[] uuids) {
- // A2DP
+ // A2DP SRC
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
if (mA2dpProfile == null) {
- if(DEBUG) Log.d(TAG, "Adding local A2DP profile");
+ if(DEBUG) Log.d(TAG, "Adding local A2DP SRC profile");
mA2dpProfile = new A2dpProfile(mContext, mLocalAdapter, mDeviceManager, this);
addProfile(mA2dpProfile, A2dpProfile.NAME,
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
@@ -148,6 +150,17 @@
Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
}
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)) {
+ if (mA2dpSinkProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local A2DP Sink profile");
+ mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME,
+ BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mA2dpSinkProfile != null) {
+ Log.w(TAG, "Warning: A2DP Sink profile was previously added but the UUID is now missing.");
+ }
+
// Headset / Handsfree
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
@@ -288,6 +301,10 @@
if (profile != null) {
return profile.isProfileReady();
}
+ profile = mA2dpSinkProfile;
+ if (profile != null) {
+ return profile.isProfileReady();
+ }
return false;
}
@@ -295,6 +312,13 @@
return mA2dpProfile;
}
+ A2dpSinkProfile getA2dpSinkProfile() {
+ if ((mA2dpSinkProfile != null)&&(mA2dpSinkProfile.isProfileReady()))
+ return mA2dpSinkProfile;
+ else
+ return null;
+ }
+
public HeadsetProfile getHeadsetProfile() {
return mHeadsetProfile;
}
@@ -345,6 +369,12 @@
removedProfiles.remove(mA2dpProfile);
}
+ if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS) &&
+ mA2dpSinkProfile != null) {
+ profiles.add(mA2dpSinkProfile);
+ removedProfiles.remove(mA2dpSinkProfile);
+ }
+
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
mOppProfile != null) {
profiles.add(mOppProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
new file mode 100644
index 0000000..f1beb10
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.users;
+
+import android.app.AppGlobals;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class AppRestrictionsHelper {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "AppRestrictionsHelper";
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private final IPackageManager mIPm;
+ private final UserManager mUserManager;
+ private final UserHandle mUser;
+ private final boolean mRestrictedProfile;
+
+ HashMap<String,Boolean> mSelectedPackages = new HashMap<>();
+ private List<SelectableAppInfo> mVisibleApps;
+
+ public AppRestrictionsHelper(Context context, UserHandle user) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mIPm = AppGlobals.getPackageManager();
+ mUser = user;
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mRestrictedProfile = mUserManager.getUserInfo(mUser.getIdentifier()).isRestricted();
+ }
+
+ public void setPackageSelected(String packageName, boolean selected) {
+ mSelectedPackages.put(packageName, selected);
+ }
+
+ public boolean isPackageSelected(String packageName) {
+ return mSelectedPackages.get(packageName);
+ }
+
+ public List<SelectableAppInfo> getVisibleApps() {
+ return mVisibleApps;
+ }
+
+ public void applyUserAppsStates(OnDisableUiForPackageListener listener) {
+ final int userId = mUser.getIdentifier();
+ if (!mUserManager.getUserInfo(userId).isRestricted() && userId != UserHandle.myUserId()) {
+ Log.e(TAG, "Cannot apply application restrictions on another user!");
+ return;
+ }
+ for (Map.Entry<String,Boolean> entry : mSelectedPackages.entrySet()) {
+ String packageName = entry.getKey();
+ boolean enabled = entry.getValue();
+ applyUserAppState(packageName, enabled, listener);
+ }
+ }
+
+ public void applyUserAppState(String packageName, boolean enabled,
+ OnDisableUiForPackageListener listener) {
+ final int userId = mUser.getIdentifier();
+ if (enabled) {
+ // Enable selected apps
+ try {
+ ApplicationInfo info = mIPm.getApplicationInfo(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ if (info == null || !info.enabled
+ || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+ mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier());
+ if (DEBUG) {
+ Log.d(TAG, "Installing " + packageName);
+ }
+ }
+ if (info != null && (info.privateFlags&ApplicationInfo.PRIVATE_FLAG_HIDDEN) != 0
+ && (info.flags&ApplicationInfo.FLAG_INSTALLED) != 0) {
+ listener.onDisableUiForPackage(packageName);
+ mIPm.setApplicationHiddenSettingAsUser(packageName, false, userId);
+ if (DEBUG) {
+ Log.d(TAG, "Unhiding " + packageName);
+ }
+ }
+ } catch (RemoteException re) {
+ // Ignore
+ }
+ } else {
+ // Blacklist all other apps, system or downloaded
+ try {
+ ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId);
+ if (info != null) {
+ if (mRestrictedProfile) {
+ mIPm.deletePackageAsUser(packageName, null, mUser.getIdentifier(),
+ PackageManager.DELETE_SYSTEM_APP);
+ if (DEBUG) {
+ Log.d(TAG, "Uninstalling " + packageName);
+ }
+ } else {
+ listener.onDisableUiForPackage(packageName);
+ mIPm.setApplicationHiddenSettingAsUser(packageName, true, userId);
+ if (DEBUG) {
+ Log.d(TAG, "Hiding " + packageName);
+ }
+ }
+ }
+ } catch (RemoteException re) {
+ // Ignore
+ }
+ }
+ }
+
+ public void fetchAndMergeApps() {
+ mVisibleApps = new ArrayList<>();
+ final PackageManager pm = mPackageManager;
+ final IPackageManager ipm = mIPm;
+
+ final HashSet<String> excludePackages = new HashSet<>();
+ addSystemImes(excludePackages);
+
+ // Add launchers
+ Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
+ launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ addSystemApps(mVisibleApps, launcherIntent, excludePackages);
+
+ // Add widgets
+ Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ addSystemApps(mVisibleApps, widgetIntent, excludePackages);
+
+ List<ApplicationInfo> installedApps = pm.getInstalledApplications(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ for (ApplicationInfo app : installedApps) {
+ // If it's not installed, skip
+ if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
+
+ if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+ && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
+ // Downloaded app
+ SelectableAppInfo info = new SelectableAppInfo();
+ info.packageName = app.packageName;
+ info.appName = app.loadLabel(pm);
+ info.activityName = info.appName;
+ info.icon = app.loadIcon(pm);
+ mVisibleApps.add(info);
+ } else {
+ try {
+ PackageInfo pi = pm.getPackageInfo(app.packageName, 0);
+ // If it's a system app that requires an account and doesn't see restricted
+ // accounts, mark for removal. It might get shown in the UI if it has an icon
+ // but will still be marked as false and immutable.
+ if (mRestrictedProfile
+ && pi.requiredAccountType != null && pi.restrictedAccountType == null) {
+ mSelectedPackages.put(app.packageName, false);
+ }
+ } catch (PackageManager.NameNotFoundException re) {
+ // Skip
+ }
+ }
+ }
+
+ // Get the list of apps already installed for the user
+ List<ApplicationInfo> userApps = null;
+ try {
+ ParceledListSlice<ApplicationInfo> listSlice = ipm.getInstalledApplications(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, mUser.getIdentifier());
+ if (listSlice != null) {
+ userApps = listSlice.getList();
+ }
+ } catch (RemoteException re) {
+ // Ignore
+ }
+
+ if (userApps != null) {
+ for (ApplicationInfo app : userApps) {
+ if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
+
+ if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+ && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
+ // Downloaded app
+ SelectableAppInfo info = new SelectableAppInfo();
+ info.packageName = app.packageName;
+ info.appName = app.loadLabel(pm);
+ info.activityName = info.appName;
+ info.icon = app.loadIcon(pm);
+ mVisibleApps.add(info);
+ }
+ }
+ }
+
+ // Sort the list of visible apps
+ Collections.sort(mVisibleApps, new AppLabelComparator());
+
+ // Remove dupes
+ Set<String> dedupPackageSet = new HashSet<String>();
+ for (int i = mVisibleApps.size() - 1; i >= 0; i--) {
+ SelectableAppInfo info = mVisibleApps.get(i);
+ if (DEBUG) Log.i(TAG, info.toString());
+ String both = info.packageName + "+" + info.activityName;
+ if (!TextUtils.isEmpty(info.packageName)
+ && !TextUtils.isEmpty(info.activityName)
+ && dedupPackageSet.contains(both)) {
+ mVisibleApps.remove(i);
+ } else {
+ dedupPackageSet.add(both);
+ }
+ }
+
+ // Establish master/slave relationship for entries that share a package name
+ HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>();
+ for (SelectableAppInfo info : mVisibleApps) {
+ if (packageMap.containsKey(info.packageName)) {
+ info.masterEntry = packageMap.get(info.packageName);
+ } else {
+ packageMap.put(info.packageName, info);
+ }
+ }
+ }
+
+ /**
+ * Find all pre-installed input methods that are marked as default
+ * and add them to an exclusion list so that they aren't
+ * presented to the user for toggling.
+ * Don't add non-default ones, as they may include other stuff that we
+ * don't need to auto-include.
+ * @param excludePackages the set of package names to append to
+ */
+ private void addSystemImes(Set<String> excludePackages) {
+ InputMethodManager imm = (InputMethodManager)
+ mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ List<InputMethodInfo> imis = imm.getInputMethodList();
+ for (InputMethodInfo imi : imis) {
+ try {
+ if (imi.isDefault(mContext) && isSystemPackage(imi.getPackageName())) {
+ excludePackages.add(imi.getPackageName());
+ }
+ } catch (Resources.NotFoundException rnfe) {
+ // Not default
+ }
+ }
+ }
+
+ /**
+ * Add system apps that match an intent to the list, excluding any packages in the exclude list.
+ * @param visibleApps list of apps to append the new list to
+ * @param intent the intent to match
+ * @param excludePackages the set of package names to be excluded, since they're required
+ */
+ private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent,
+ Set<String> excludePackages) {
+ final PackageManager pm = mPackageManager;
+ List<ResolveInfo> launchableApps = pm.queryIntentActivities(intent,
+ PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ for (ResolveInfo app : launchableApps) {
+ if (app.activityInfo != null && app.activityInfo.applicationInfo != null) {
+ final String packageName = app.activityInfo.packageName;
+ int flags = app.activityInfo.applicationInfo.flags;
+ if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ // System app
+ // Skip excluded packages
+ if (excludePackages.contains(packageName)) continue;
+ int enabled = pm.getApplicationEnabledSetting(packageName);
+ if (enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+ || enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+ // Check if the app is already enabled for the target user
+ ApplicationInfo targetUserAppInfo = getAppInfoForUser(packageName,
+ 0, mUser);
+ if (targetUserAppInfo == null
+ || (targetUserAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+ continue;
+ }
+ }
+ SelectableAppInfo info = new SelectableAppInfo();
+ info.packageName = app.activityInfo.packageName;
+ info.appName = app.activityInfo.applicationInfo.loadLabel(pm);
+ info.icon = app.activityInfo.loadIcon(pm);
+ info.activityName = app.activityInfo.loadLabel(pm);
+ if (info.activityName == null) info.activityName = info.appName;
+
+ visibleApps.add(info);
+ }
+ }
+ }
+ }
+
+ private boolean isSystemPackage(String packageName) {
+ try {
+ final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
+ if (pi.applicationInfo == null) return false;
+ final int flags = pi.applicationInfo.flags;
+ if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException nnfe) {
+ // Missing package?
+ }
+ return false;
+ }
+
+ private ApplicationInfo getAppInfoForUser(String packageName, int flags, UserHandle user) {
+ try {
+ return mIPm.getApplicationInfo(packageName, flags, user.getIdentifier());
+ } catch (RemoteException re) {
+ return null;
+ }
+ }
+
+ public interface OnDisableUiForPackageListener {
+ void onDisableUiForPackage(String packageName);
+ }
+
+ public static class SelectableAppInfo {
+ public String packageName;
+ public CharSequence appName;
+ public CharSequence activityName;
+ public Drawable icon;
+ public SelectableAppInfo masterEntry;
+
+ @Override
+ public String toString() {
+ return packageName + ": appName=" + appName + "; activityName=" + activityName
+ + "; icon=" + icon + "; masterEntry=" + masterEntry;
+ }
+ }
+
+ private static class AppLabelComparator implements Comparator<SelectableAppInfo> {
+
+ @Override
+ public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) {
+ String lhsLabel = lhs.activityName.toString();
+ String rhsLabel = rhs.activityName.toString();
+ return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase());
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index cff8c23..2ee4b12 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -234,10 +234,13 @@
}
public boolean matches(WifiConfiguration config) {
- if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint())
+ if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
return config.FQDN.equals(mConfig.providerFriendlyName);
- else
- return ssid.equals(removeDoubleQuotes(config.SSID)) && security == getSecurity(config);
+ } else {
+ return ssid.equals(removeDoubleQuotes(config.SSID))
+ && security == getSecurity(config)
+ && (mConfig == null || mConfig.shared == config.shared);
+ }
}
public WifiConfiguration getConfig() {
@@ -394,33 +397,20 @@
summary.append(String.format(format, mConfig.providerFriendlyName));
} else if (mConfig != null && mConfig.hasNoInternetAccess()) {
summary.append(mContext.getString(R.string.wifi_no_internet));
- } else if (mConfig != null && ((mConfig.status == WifiConfiguration.Status.DISABLED &&
- mConfig.disableReason != WifiConfiguration.DISABLED_UNKNOWN_REASON)
- || mConfig.autoJoinStatus
- >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)) {
- if (mConfig.autoJoinStatus
- >= WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
- if (mConfig.disableReason == WifiConfiguration.DISABLED_DHCP_FAILURE) {
- summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
- } else if (mConfig.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE) {
+ } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
+ WifiConfiguration.NetworkSelectionStatus networkStatus =
+ mConfig.getNetworkSelectionStatus();
+ switch (networkStatus.getNetworkSelectionDisableReason()) {
+ case WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE:
summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
- } else {
- summary.append(mContext.getString(R.string.wifi_disabled_wifi_failure));
- }
- } else {
- switch (mConfig.disableReason) {
- case WifiConfiguration.DISABLED_AUTH_FAILURE:
- summary.append(mContext.getString(R.string.wifi_disabled_password_failure));
- break;
- case WifiConfiguration.DISABLED_DHCP_FAILURE:
- case WifiConfiguration.DISABLED_DNS_FAILURE:
- summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
- break;
- case WifiConfiguration.DISABLED_UNKNOWN_REASON:
- case WifiConfiguration.DISABLED_ASSOCIATION_REJECT:
- summary.append(mContext.getString(R.string.wifi_disabled_generic));
- break;
- }
+ break;
+ case WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE:
+ case WifiConfiguration.NetworkSelectionStatus.DISABLED_DNS_FAILURE:
+ summary.append(mContext.getString(R.string.wifi_disabled_network_failure));
+ break;
+ case WifiConfiguration.NetworkSelectionStatus.DISABLED_ASSOCIATION_REJECTION:
+ summary.append(mContext.getString(R.string.wifi_disabled_generic));
+ break;
}
} else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range
summary.append(mContext.getString(R.string.wifi_not_in_range));
@@ -437,11 +427,11 @@
summary.append(" f=" + Integer.toString(mInfo.getFrequency()));
}
summary.append(" " + getVisibilityStatus());
- if (mConfig != null && mConfig.autoJoinStatus > 0) {
- summary.append(" (" + mConfig.autoJoinStatus);
- if (mConfig.blackListTimestamp > 0) {
+ if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) {
+ summary.append(" (" + mConfig.getNetworkSelectionStatus().getNetworkStatusString());
+ if (mConfig.getNetworkSelectionStatus().getDisableTime() > 0) {
long now = System.currentTimeMillis();
- long diff = (now - mConfig.blackListTimestamp)/1000;
+ long diff = (now - mConfig.getNetworkSelectionStatus().getDisableTime()) / 1000;
long sec = diff%60; //seconds
long min = (diff/60)%60; //minutes
long hour = (min/60)%60; //hours
@@ -452,17 +442,19 @@
}
summary.append(")");
}
- if (mConfig != null && mConfig.numIpConfigFailures > 0) {
- summary.append(" ipf=").append(mConfig.numIpConfigFailures);
- }
- if (mConfig != null && mConfig.numConnectionFailures > 0) {
- summary.append(" cf=").append(mConfig.numConnectionFailures);
- }
- if (mConfig != null && mConfig.numAuthFailures > 0) {
- summary.append(" authf=").append(mConfig.numAuthFailures);
- }
- if (mConfig != null && mConfig.numNoInternetAccessReports > 0) {
- summary.append(" noInt=").append(mConfig.numNoInternetAccessReports);
+
+ if (mConfig != null) {
+ WifiConfiguration.NetworkSelectionStatus networkStatus =
+ mConfig.getNetworkSelectionStatus();
+ for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+ index < WifiConfiguration.NetworkSelectionStatus
+ .NETWORK_SELECTION_DISABLED_MAX; index++) {
+ if (networkStatus.getDisableReasonCounter(index) != 0) {
+ summary.append(" " + WifiConfiguration.NetworkSelectionStatus
+ .getNetworkDisableReasonString(index) + "="
+ + networkStatus.getDisableReasonCounter(index));
+ }
+ }
}
}
return summary.toString();
@@ -508,10 +500,6 @@
Map<String, ScanResult> list = mScanResultCache.snapshot();
// TODO: sort list by RSSI or age
for (ScanResult result : list.values()) {
- if (result.seen == 0)
- continue;
-
- if (result.autoJoinStatus != ScanResult.ENABLED) numBlackListed++;
if (result.frequency >= LOWER_FREQ_5GHZ
&& result.frequency <= HIGHER_FREQ_5GHZ) {
@@ -525,8 +513,6 @@
num24 = num24 + 1;
}
- // Ignore results seen, older than 20 seconds
- if (now - result.seen > VISIBILITY_OUTDATED_AGE_IN_MILLI) continue;
if (result.frequency >= LOWER_FREQ_5GHZ
&& result.frequency <= HIGHER_FREQ_5GHZ) {
@@ -539,12 +525,6 @@
if (bssid != null && result.BSSID.equals(bssid)) scans5GHz.append("*");
scans5GHz.append("=").append(result.frequency);
scans5GHz.append(",").append(result.level);
- if (result.autoJoinStatus != 0) {
- scans5GHz.append(",st=").append(result.autoJoinStatus);
- }
- if (result.numIpConfigFailures != 0) {
- scans5GHz.append(",ipf=").append(result.numIpConfigFailures);
- }
scans5GHz.append("}");
n5++;
}
@@ -559,12 +539,6 @@
if (bssid != null && result.BSSID.equals(bssid)) scans24GHz.append("*");
scans24GHz.append("=").append(result.frequency);
scans24GHz.append(",").append(result.level);
- if (result.autoJoinStatus != 0) {
- scans24GHz.append(",st=").append(result.autoJoinStatus);
- }
- if (result.numIpConfigFailures != 0) {
- scans24GHz.append(",ipf=").append(result.numIpConfigFailures);
- }
scans24GHz.append("}");
n24++;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java b/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
index f9f1d3f..2317dbc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
@@ -48,6 +48,23 @@
*/
private static int STATE_VERSION = 1;
+ /**
+ * write the Network selecton status to Byte Array
+ */
+ private static void writeNetworkSelectionStatus(WifiConfiguration config, DataOutputStream dest)
+ throws IOException {
+ WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+
+ dest.writeInt(status.getNetworkSelectionStatus());
+ dest.writeInt(status.getNetworkSelectionDisableReason());
+ for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+ index < WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX;
+ index++) {
+ dest.writeInt(status.getDisableReasonCounter(index));
+ }
+ dest.writeLong(status.getDisableTime());
+ writeString(dest, status.getNetworkSelectionBSSID());
+ }
/**
* Marshals a WifiConfig object into a byte-array.
@@ -64,12 +81,11 @@
out.writeInt(STATE_VERSION);
out.writeInt(wifiConfig.networkId);
out.writeInt(wifiConfig.status);
- out.writeInt(wifiConfig.disableReason);
+ writeNetworkSelectionStatus(wifiConfig, out);
writeString(out, wifiConfig.SSID);
writeString(out, wifiConfig.BSSID);
out.writeInt(wifiConfig.apBand);
out.writeInt(wifiConfig.apChannel);
- writeString(out, wifiConfig.autoJoinBSSID);
writeString(out, wifiConfig.FQDN);
writeString(out, wifiConfig.providerFriendlyName);
out.writeInt(wifiConfig.roamingConsortiumIds.length);
@@ -98,7 +114,6 @@
writeString(out, wifiConfig.dhcpServer);
writeString(out, wifiConfig.defaultGwMacAddress);
- out.writeInt(wifiConfig.autoJoinStatus);
out.writeInt(wifiConfig.selfAdded ? 1 : 0);
out.writeInt(wifiConfig.didSelfAdd ? 1 : 0);
out.writeInt(wifiConfig.validatedInternetAccess ? 1 : 0);
@@ -108,14 +123,9 @@
out.writeInt(wifiConfig.lastUpdateUid);
writeString(out, wifiConfig.creatorName);
writeString(out, wifiConfig.lastUpdateName);
- out.writeLong(wifiConfig.blackListTimestamp);
out.writeLong(wifiConfig.lastConnectionFailure);
out.writeLong(wifiConfig.lastRoamingFailure);
out.writeInt(wifiConfig.lastRoamingFailureReason);
- out.writeLong(wifiConfig.roamingFailureBlackListTimeMilli);
- out.writeLong(wifiConfig.numConnectionFailures);
- out.writeLong(wifiConfig.numIpConfigFailures);
- out.writeInt(wifiConfig.numAuthFailures);
out.writeInt(wifiConfig.numScorerOverride);
out.writeInt(wifiConfig.numScorerOverrideAndSwitchedNetwork);
out.writeInt(wifiConfig.numAssociation);
@@ -126,8 +136,6 @@
out.writeInt(wifiConfig.numTicksAtBadRSSI);
out.writeInt(wifiConfig.numTicksAtNotHighRSSI);
out.writeInt(wifiConfig.numUserTriggeredJoinAttempts);
- out.writeInt(wifiConfig.autoJoinUseAggressiveJoinAttemptThreshold);
- out.writeInt(wifiConfig.autoJoinBailedDueToLowRssi ? 1 : 0);
out.writeInt(wifiConfig.userApproved);
out.writeInt(wifiConfig.numNoInternetAccessReports);
out.writeInt(wifiConfig.noInternetAccessExpected ? 1 : 0);
@@ -140,6 +148,23 @@
}
/**
+ *
+ */
+ private static void readNetworkSelectionStatusFromByteArray(DataInputStream in,
+ WifiConfiguration config, int version) throws IOException {
+ WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+ status.setNetworkSelectionStatus(in.readInt());
+ status.setNetworkSelectionDisableReason(in.readInt());
+ for (int index = WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+ index < WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX;
+ index++) {
+ status.setDisableReasonCounter(index, in.readInt());
+ }
+ status.setDisableTime(in.readLong());
+ status.setNetworkSelectionBSSID(readString(in, version));
+ }
+
+ /**
* Unmarshals a byte array into a WifiConfig Object
*
* @param data - marshalled WifiConfig Object
@@ -157,12 +182,11 @@
config.networkId = in.readInt();
config.status = in.readInt();
- config.disableReason = in.readInt();
+ readNetworkSelectionStatusFromByteArray(in, config, version);
config.SSID = readString(in, version);
config.BSSID = readString(in, version);
config.apBand = in.readInt();
config.apChannel = in.readInt();
- config.autoJoinBSSID = readString(in, version);
config.FQDN = readString(in, version);
config.providerFriendlyName = readString(in, version);
int numRoamingConsortiumIds = in.readInt();
@@ -195,7 +219,6 @@
config.dhcpServer = readString(in, version);
config.defaultGwMacAddress = readString(in, version);
- config.autoJoinStatus = in.readInt();
config.selfAdded = in.readInt() != 0;
config.didSelfAdd = in.readInt() != 0;
config.validatedInternetAccess = in.readInt() != 0;
@@ -205,14 +228,10 @@
config.lastUpdateUid = in.readInt();
config.creatorName = readString(in, version);
config.lastUpdateName = readString(in, version);
- config.blackListTimestamp = in.readLong();
config.lastConnectionFailure = in.readLong();
config.lastRoamingFailure = in.readLong();
config.lastRoamingFailureReason = in.readInt();
config.roamingFailureBlackListTimeMilli = in.readLong();
- config.numConnectionFailures = in.readInt();
- config.numIpConfigFailures = in.readInt();
- config.numAuthFailures = in.readInt();
config.numScorerOverride = in.readInt();
config.numScorerOverrideAndSwitchedNetwork = in.readInt();
config.numAssociation = in.readInt();
@@ -223,8 +242,6 @@
config.numTicksAtBadRSSI = in.readInt();
config.numTicksAtNotHighRSSI = in.readInt();
config.numUserTriggeredJoinAttempts = in.readInt();
- config.autoJoinUseAggressiveJoinAttemptThreshold = in.readInt();
- config.autoJoinBailedDueToLowRssi = in.readInt() != 0;
config.userApproved = in.readInt();
config.numNoInternetAccessReports = in.readInt();
config.noInternetAccessExpected = in.readInt() != 0;
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 5c807e1..df8fad4 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -65,6 +65,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemProperties;
import android.os.Vibrator;
@@ -108,7 +109,7 @@
* </ol>
*/
public class BugreportProgressService extends Service {
- static final String TAG = "Shell";
+ private static final String TAG = "BugreportProgressService";
private static final boolean DEBUG = false;
private static final String AUTHORITY = "com.android.shell";
@@ -137,6 +138,7 @@
static final String EXTRA_TITLE = "android.intent.extra.TITLE";
static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
static final String EXTRA_ORIGINAL_INTENT = "android.intent.extra.ORIGINAL_INTENT";
+ static final String EXTRA_INFO = "android.intent.extra.INFO";
private static final int MSG_SERVICE_COMMAND = 1;
private static final int MSG_POLL = 2;
@@ -325,7 +327,7 @@
takeScreenshot(pid, true);
break;
case INTENT_BUGREPORT_SHARE:
- shareBugreport(pid);
+ shareBugreport(pid, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO));
break;
case INTENT_BUGREPORT_CANCEL:
cancel(pid);
@@ -483,6 +485,7 @@
if (mProcesses.indexOfKey(pid) < 0) {
Log.w(TAG, "PID not watched: " + pid);
} else {
+ Log.d(TAG, "Removing PID " + pid);
mProcesses.remove(pid);
}
stopSelfWhenDone();
@@ -675,6 +678,11 @@
final int msgId;
if (taken) {
info.addScreenshot(screenshotFile);
+ if (info.finished) {
+ Log.d(TAG, "Screenshot finished after bugreport; updating share notification");
+ info.renameScreenshots(mScreenshotsDir);
+ sendBugreportNotification(mContext, info);
+ }
msgId = R.string.bugreport_screenshot_taken;
} else {
// TODO: try again using Framework APIs instead of relying on screencap.
@@ -814,12 +822,13 @@
* Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
* intent, but issuing a warning dialog the first time.
*/
- private void shareBugreport(int pid) {
- final BugreportInfo info = getInfo(pid);
+ private void shareBugreport(int pid, BugreportInfo sharedInfo) {
+ BugreportInfo info = getInfo(pid);
if (info == null) {
- // Should not happen, so log if it does...
- Log.e(TAG, "INTERNAL ERROR: no info for PID " + pid + ": " + mProcesses);
- return;
+ // Service was terminated but notification persisted
+ info = sharedInfo;
+ Log.d(TAG, "shareBugreport(): no info for PID " + pid + " on managed processes ("
+ + mProcesses + "), using info from intent instead (" + info + ")");
}
addDetailsToZipFile(info);
@@ -850,6 +859,7 @@
shareIntent.setClass(context, BugreportProgressService.class);
shareIntent.setAction(INTENT_BUGREPORT_SHARE);
shareIntent.putExtra(EXTRA_PID, info.pid);
+ shareIntent.putExtra(EXTRA_INFO, info);
final String title = context.getString(R.string.bugreport_finished_title);
final Notification.Builder builder = new Notification.Builder(context)
@@ -919,6 +929,11 @@
* description will be saved on {@code description.txt}.
*/
private void addDetailsToZipFile(BugreportInfo info) {
+ if (info.bugreportFile == null) {
+ // One possible reason is a bug in the Parcelization code.
+ Log.e(TAG, "INTERNAL ERROR: no bugreportFile on " + info);
+ return;
+ }
// It's not possible to add a new entry into an existing file, so we need to create a new
// zip, copy all entries, then rename it.
final File dir = info.bugreportFile.getParentFile();
@@ -1281,7 +1296,7 @@
/**
* Information about a bugreport process while its in progress.
*/
- private static final class BugreportInfo {
+ private static final class BugreportInfo implements Parcelable {
private final Context context;
/**
@@ -1325,6 +1340,11 @@
long lastUpdate = System.currentTimeMillis();
/**
+ * Time of the last progress update when Parcel was created.
+ */
+ String formattedLastUpdate;
+
+ /**
* Path of the main bugreport file.
*/
File bugreportFile;
@@ -1403,6 +1423,11 @@
}
String getFormattedLastUpdate() {
+ if (context == null) {
+ // Restored from Parcel
+ return formattedLastUpdate == null ?
+ Long.toString(lastUpdate) : formattedLastUpdate;
+ }
return DateUtils.formatDateTime(context, lastUpdate,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
}
@@ -1416,5 +1441,74 @@
+ "\n\tprogress: " + progress + "/" + max + "(" + percent + ")"
+ "\n\tlast_update: " + getFormattedLastUpdate();
}
+
+ // Parcelable contract
+ protected BugreportInfo(Parcel in) {
+ context = null;
+ pid = in.readInt();
+ name = in.readString();
+ title = in.readString();
+ description = in.readString();
+ max = in.readInt();
+ progress = in.readInt();
+ lastUpdate = in.readLong();
+ formattedLastUpdate = in.readString();
+ bugreportFile = readFile(in);
+
+ int screenshotSize = in.readInt();
+ for (int i = 1; i <= screenshotSize; i++) {
+ screenshotFiles.add(readFile(in));
+ }
+
+ finished = in.readInt() == 1;
+ screenshotCounter = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(pid);
+ dest.writeString(name);
+ dest.writeString(title);
+ dest.writeString(description);
+ dest.writeInt(max);
+ dest.writeInt(progress);
+ dest.writeLong(lastUpdate);
+ dest.writeString(getFormattedLastUpdate());
+ writeFile(dest, bugreportFile);
+
+ dest.writeInt(screenshotFiles.size());
+ for (File screenshotFile : screenshotFiles) {
+ writeFile(dest, screenshotFile);
+ }
+
+ dest.writeInt(finished ? 1 : 0);
+ dest.writeInt(screenshotCounter);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private void writeFile(Parcel dest, File file) {
+ dest.writeString(file == null ? null : file.getPath());
+ }
+
+ private File readFile(Parcel in) {
+ final String path = in.readString();
+ return path == null ? null : new File(path);
+ }
+
+ public static final Parcelable.Creator<BugreportInfo> CREATOR =
+ new Parcelable.Creator<BugreportInfo>() {
+ public BugreportInfo createFromParcel(Parcel source) {
+ return new BugreportInfo(source);
+ }
+
+ public BugreportInfo[] newArray(int size) {
+ return new BugreportInfo[size];
+ }
+ };
+
}
}
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
index c8898b9..9afa900 100644
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java
@@ -19,7 +19,6 @@
import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT;
import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
-import static com.android.shell.BugreportProgressService.TAG;
import static com.android.shell.BugreportProgressService.getFileExtra;
import java.io.File;
@@ -37,6 +36,7 @@
* {@link Intent#ACTION_SEND_MULTIPLE}.
*/
public class BugreportReceiver extends BroadcastReceiver {
+ private static final String TAG = "BugreportReceiver";
/**
* Always keep the newest 8 bugreport files; 4 reports and 4 screenshots are
@@ -74,7 +74,11 @@
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- FileUtils.deleteOlderFiles(bugreportFile.getParentFile(), minCount, minAge);
+ try {
+ FileUtils.deleteOlderFiles(bugreportFile.getParentFile(), minCount, minAge);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "RuntimeException deleting old files", e);
+ }
result.finish();
return null;
}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index d1a07ea..52e1b56 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -65,6 +65,7 @@
import android.util.Log;
import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
+import com.android.shell.BugreportProgressService;
/**
* Integration tests for {@link BugreportReceiver}.
@@ -89,6 +90,9 @@
// Timeout for UI operations, in milliseconds.
private static final int TIMEOUT = (int) BugreportProgressService.POLLING_FREQUENCY * 4;
+ // Timeout for when waiting for a screenshot to finish.
+ private static final int SAFE_SCREENSHOT_DELAY = SCREENSHOT_DELAY_SECONDS + 10;
+
private static final String BUGREPORTS_DIR = "bugreports";
private static final String BUGREPORT_FILE = "test_bugreport.txt";
private static final String ZIP_FILE = "test_bugreport.zip";
@@ -109,6 +113,7 @@
private static final String NO_NAME = null;
private static final String NO_SCREENSHOT = null;
private static final String NO_TITLE = null;
+ private static final Integer NO_PID = null;
private String mDescription;
@@ -122,6 +127,7 @@
@Override
protected void setUp() throws Exception {
+ Log.i(TAG, "#### setup() on " + getName());
Instrumentation instrumentation = getInstrumentation();
mContext = instrumentation.getTargetContext();
mUiBot = new UiBot(UiDevice.getInstance(instrumentation), TIMEOUT);
@@ -164,13 +170,21 @@
Bundle extras =
sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, ZIP_FILE,
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
NAME, NO_TITLE, NO_DESCRIPTION, 1, true);
assertServiceNotRunning();
}
public void testProgress_takeExtraScreenshot() throws Exception {
+ takeExtraScreenshotTest(false);
+ }
+
+ public void testProgress_takeExtraScreenshotServiceDiesAfterScreenshotTaken() throws Exception {
+ takeExtraScreenshotTest(true);
+ }
+
+ private void takeExtraScreenshotTest(boolean serviceDies) throws Exception {
resetProperties();
sendBugreportStarted(1000);
@@ -179,14 +193,49 @@
assertScreenshotButtonEnabled(false);
waitForScreenshotButtonEnabled(true);
- Bundle extras =
- sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath, mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, ZIP_FILE,
+ sendBugreportFinished(PID, mPlainTextPath, mScreenshotPath);
+
+ if (serviceDies) {
+ waitShareNotification();
+ killService();
+ }
+
+ Bundle extras = acceptBugreportAndGetSharedIntent();
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, ZIP_FILE,
NAME, NO_TITLE, NO_DESCRIPTION, 2, true);
assertServiceNotRunning();
}
+ public void testScreenshotFinishesAfterBugreport() throws Exception {
+ screenshotFinishesAfterBugreportTest(false);
+ }
+
+ public void testScreenshotFinishesAfterBugreportAndServiceDiesBeforeSharing() throws Exception {
+ screenshotFinishesAfterBugreportTest(true);
+ }
+
+ private void screenshotFinishesAfterBugreportTest(boolean serviceDies) throws Exception {
+ resetProperties();
+
+ sendBugreportStarted(1000);
+ sendBugreportFinished(PID, mPlainTextPath, NO_SCREENSHOT);
+ waitShareNotification();
+
+ // There's no indication in the UI about the screenshot finish, so just sleep like a baby...
+ Thread.sleep(SAFE_SCREENSHOT_DELAY * DateUtils.SECOND_IN_MILLIS);
+
+ if (serviceDies) {
+ killService();
+ }
+
+ Bundle extras = acceptBugreportAndGetSharedIntent();
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT, PID, ZIP_FILE,
+ NAME, NO_TITLE, NO_DESCRIPTION, 1, true);
+
+ assertServiceNotRunning();
+ }
+
public void testProgress_changeDetailsInvalidInput() throws Exception {
resetProperties();
@@ -227,7 +276,7 @@
Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID, mPlainTextPath,
mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, TITLE,
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
NEW_NAME, TITLE, mDescription, 1, true);
assertServiceNotRunning();
@@ -266,7 +315,7 @@
Bundle extras = sendBugreportFinishedAndGetSharedIntent(PID,
plainText? mPlainTextPath : mZipPath, mScreenshotPath);
- assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, TITLE,
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
NEW_NAME, TITLE, mDescription, 1, true);
assertServiceNotRunning();
@@ -317,8 +366,8 @@
// Finally, share bugreport.
Bundle extras = acceptBugreportAndGetSharedIntent();
- assertActionSendMultiple(extras, BUGREPORT_FILE, BUGREPORT_CONTENT, PID, TITLE,
- NAME, TITLE, mDescription, 1, waitScreenshot);
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT, PID, TITLE,
+ NAME, TITLE, mDescription, 1, true);
assertServiceNotRunning();
}
@@ -328,7 +377,7 @@
BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_SHOW);
// Send notification and click on share.
- sendBugreportFinished(null, mPlainTextPath, null);
+ sendBugreportFinished(NO_PID, mPlainTextPath, null);
acceptBugreport();
// Handle the warning
@@ -350,6 +399,13 @@
assertEquals("Didn't change state", BugreportPrefs.STATE_HIDE, newState);
}
+ public void testShareBugreportAfterServiceDies() throws Exception {
+ sendBugreportFinished(NO_PID, mPlainTextPath, NO_SCREENSHOT);
+ killService();
+ Bundle extras = acceptBugreportAndGetSharedIntent();
+ assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
+ }
+
public void testBugreportFinished_plainBugreportAndScreenshot() throws Exception {
Bundle extras = sendBugreportFinishedAndGetSharedIntent(mPlainTextPath, mScreenshotPath);
assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT);
@@ -444,6 +500,13 @@
}
/**
+ * Waits for the notification to share the finished bugreport.
+ */
+ private void waitShareNotification() {
+ mUiBot.getNotification(mContext.getString(R.string.bugreport_finished_title));
+ }
+
+ /**
* Accepts the notification to share the finished bugreport.
*/
private void acceptBugreport() {
@@ -512,7 +575,8 @@
expectedNumberScreenshots ++; // Add screenshot received by dumpstate
}
int expectedSize = expectedNumberScreenshots + 1; // All screenshots plus the bugreport file
- assertEquals("wrong number of attachments", expectedSize, attachments.size());
+ assertEquals("wrong number of attachments (" + attachments + ")",
+ expectedSize, attachments.size());
// Need to interact through all attachments, since order is not guaranteed.
Uri zipUri = null;
@@ -607,6 +671,15 @@
assertFalse("Service '" + service + "' is still running", isServiceRunning(service));
}
+ private void killService() {
+ waitForService(true);
+ Log.v(TAG, "Stopping service");
+ boolean stopped = mContext.stopService(new Intent(mContext, BugreportProgressService.class));
+ Log.d(TAG, "stopService returned " + stopped);
+ waitForService(false);
+ assertServiceNotRunning(); // Sanity check.
+ }
+
private boolean isServiceRunning(String name) {
ActivityManager manager = (ActivityManager) mContext
.getSystemService(Context.ACTIVITY_SERVICE);
@@ -618,6 +691,33 @@
return false;
}
+ private void waitForService(boolean expectRunning) {
+ String service = BugreportProgressService.class.getName();
+ boolean actualRunning;
+ for (int i = 1; i <= 5; i++) {
+ actualRunning = isServiceRunning(service);
+ Log.d(TAG, "Attempt " + i + " to check status of service '"
+ + service + "': expected=" + expectRunning + ", actual= " + actualRunning);
+ if (actualRunning == expectRunning) {
+ return;
+ }
+ try {
+ Thread.sleep(DateUtils.SECOND_IN_MILLIS);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "thread interrupted");
+ Thread.currentThread().interrupt();
+ }
+ }
+ if (!expectRunning) {
+ // Typically happens when service is waiting for a screenshot to finish.
+ Log.w(TAG, "Service didn't stop; try to kill it again");
+ killService();
+ return;
+ }
+
+ fail("Service status didn't change to " + expectRunning);
+ }
+
private static void createTextFile(String path, String content) throws IOException {
Log.v(TAG, "createFile(" + path + ")");
try (Writer writer = new BufferedWriter(new OutputStreamWriter(
@@ -669,7 +769,7 @@
private UiObject waitForScreenshotButtonEnabled(boolean expectedEnabled) throws Exception {
UiObject screenshotButton = getScreenshotButton();
- int maxAttempts = SCREENSHOT_DELAY_SECONDS + 5;
+ int maxAttempts = SAFE_SCREENSHOT_DELAY;
int i = 0;
do {
boolean enabled = screenshotButton.isEnabled();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6201fd6..3dc339d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -146,6 +146,9 @@
<!-- Access battery information -->
<uses-permission android:name="android.permission.BATTERY_STATS" />
+ <!-- DevicePolicyManager get user restrictions -->
+ <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+
<application
android:name=".SystemUIApplication"
android:persistent="true"
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index bc18221..2ea8c9c 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -13,6 +13,7 @@
-keep class com.android.systemui.statusbar.car.CarStatusBar
-keep class com.android.systemui.statusbar.phone.PhoneStatusBar
-keep class com.android.systemui.statusbar.tv.TvStatusBar
+-keep class com.android.systemui.SystemUIFactory
-keepclassmembers class ** {
public void onBusEvent(**);
diff --git a/packages/SystemUI/res/color/qs_tile_text.xml b/packages/SystemUI/res/color/qs_tile_text.xml
new file mode 100644
index 0000000..90e0bce
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_tile_text.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
+ <item android:color="#B3FFFFFF" /> <!-- 70% white -->
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_user_detail_name.xml b/packages/SystemUI/res/color/qs_user_detail_name.xml
index 57f7e65..35c7a4f 100644
--- a/packages/SystemUI/res/color/qs_user_detail_name.xml
+++ b/packages/SystemUI/res/color/qs_user_detail_name.xml
@@ -18,5 +18,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:color="@color/current_user_border_color" />
+ <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
<item android:color="#66ffffff" /> <!-- 40% white -->
</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 071b7c8..03451b4 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -25,15 +25,15 @@
android:gravity="top|start"
android:orientation="vertical"
android:paddingStart="@*android:dimen/notification_content_margin_start"
- android:paddingEnd="@*android:dimen/notification_content_margin_end"
- android:background="@color/notification_guts_text_color" >
+ android:paddingEnd="8dp"
+ android:background="@color/notification_guts_bg_color" >
<!-- header -->
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="8dp"
- android:paddingTop="8dp"
+ android:layout_height="30dp"
+ android:paddingTop="9dp"
+ android:paddingEnd="8dp"
android:id="@+id/notification_guts_header"
android:orientation="horizontal"
android:layout_gravity="center_vertical|start">
@@ -42,25 +42,21 @@
android:id="@android:id/icon"
android:layout_width="18dp"
android:layout_height="18dp"
- android:layout_marginEnd="3dp"
+ android:layout_marginEnd="6dp"
android:src="@android:drawable/arrow_down_float" />
<TextView
- android:id="@+id/pkgname"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@android:style/TextAppearance.Material.Notification.Info"
- android:layout_marginStart="3dp"
- android:layout_marginEnd="4dp"
- android:textColor="@color/notification_guts_title_color" />
+ android:id="@+id/pkgname"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.NotificationGuts.Header" />
<TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/debug_info"
- android:layout_weight="0"
- android:textAppearance="@android:style/TextAppearance.Material.Notification.Time"
- android:layout_gravity="bottom|start"
- android:visibility="gone"
- android:textColor="#ffffff" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/debug_info"
+ android:layout_weight="0"
+ style="@style/TextAppearance.NotificationGuts.Header"
+ android:layout_gravity="bottom|start"
+ android:visibility="gone" />
</LinearLayout>
<!-- Importance slider -->
<LinearLayout
@@ -70,16 +66,17 @@
android:orientation="vertical"
android:clickable="false"
android:focusable="false"
- android:paddingBottom="8dip">
+ android:paddingBottom="8dip"
+ android:paddingEnd="8dp" >
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="@color/notification_guts_text_color"
+ style="@style/TextAppearance.NotificationGuts.Primary"
android:ellipsize="marquee"
- android:fadingEdge="horizontal"/>
+ android:fadingEdge="horizontal"
+ android:paddingBottom="2dp"/>
<TextView
android:id="@+id/summary"
@@ -87,38 +84,41 @@
android:layout_height="wrap_content"
android:layout_alignStart="@android:id/title"
android:textAlignment="viewStart"
- android:textAppearance="@android:style/TextAppearance.Material.Body1"
- android:textColor="@color/notification_guts_title_color"
+ style="@style/TextAppearance.NotificationGuts.Secondary"
android:maxLines="3"
- android:minLines="2" />
+ android:minLines="2"
+ android:paddingBottom="4dp" />
<FrameLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="48dp"
android:paddingTop="8dp" >
<ImageView
android:id="@+id/low_importance"
- android:src="@android:drawable/ic_menu_close_clear_cancel"
+ android:src="@*android:drawable/ic_notification_block"
android:layout_gravity="center_vertical|start"
android:layout_width="24dp"
android:layout_height="24dp" />
<SeekBar
android:id="@+id/seekbar"
- android:layout_marginStart="24dp"
- android:layout_marginEnd="24dp"
+ android:layout_marginStart="56dp"
+ android:layout_marginEnd="56dp"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="48dp"
android:focusable="true"
android:background="#00ffffff"
- android:thumbTint="@android:color/white"
- android:progressTint="@android:color/white" />
+ android:progressBackgroundTint="@color/notification_guts_secondary_slider_color"
+ android:thumbTint="@color/notification_guts_slider_color"
+ android:progressTint="@color/notification_guts_slider_color"
+ style="@android:style/Widget.Material.SeekBar.Discrete"
+ android:tickMarkTint="@android:color/black" />
<ImageView
android:id="@+id/max_importance"
- android:src="@android:drawable/ic_popup_reminder"
+ android:src="@*android:drawable/ic_notification_alert"
android:layout_gravity="center_vertical|end"
android:layout_width="24dp"
android:layout_height="24dp"/>
@@ -128,19 +128,22 @@
<RadioGroup
android:id="@+id/apply_to"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="8dp">
+ android:layout_height="wrap_content" >
<RadioButton android:id="@+id/apply_to_topic"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/notification_guts_text_color"
- android:visibility="gone"/>
+ android:layout_height="48dp"
+ style="@style/TextAppearance.NotificationGuts.Primary"
+ android:visibility="gone"
+ android:buttonTint="#858383"
+ />
<RadioButton android:id="@+id/apply_to_app"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="48dp"
android:text="@string/apply_to_app"
- android:textColor="@color/notification_guts_text_color"
- android:visibility="gone"/>
+ style="@style/TextAppearance.NotificationGuts.Primary"
+ android:visibility="gone"
+ android:buttonTint="#858383"
+ />
</RadioGroup>
</LinearLayout>
<!-- buttons -->
@@ -148,29 +151,30 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
- android:paddingTop="8dp"
- android:paddingBottom="16dp" >
+ android:paddingBottom="8dp" >
<TextView
android:id="@+id/more_settings"
android:text="@string/notification_more_settings"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.NotificationGuts"
+ android:layout_height="48dp"
+ style="@style/TextAppearance.NotificationGuts.Button"
android:background="@drawable/btn_borderless_rect"
android:gravity="center"
- android:paddingEnd="24dp"
- android:paddingStart="12dp"
+ android:paddingEnd="8dp"
+ android:paddingStart="8dp"
android:focusable="true" />
<TextView
android:id="@+id/done"
android:text="@string/notification_done"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.NotificationGuts"
+ android:layout_height="48dp"
+ style="@style/TextAppearance.NotificationGuts.Button"
android:background="@drawable/btn_borderless_rect"
android:gravity="center"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
android:focusable="true"/>
</LinearLayout>
</com.android.systemui.statusbar.NotificationGuts>
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
new file mode 100644
index 0000000..603ebbf
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView android:id="@+id/tile_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/qs_tile_text"
+ android:gravity="center_horizontal"
+ android:minLines="2"
+ android:padding="0dp"
+ android:fontFamily="sans-serif-condensed"
+ android:textStyle="normal"
+ android:textSize="@dimen/qs_tile_text_size"
+ android:clickable="false" />
+ <ImageView android:id="@+id/restricted_padlock"
+ android:layout_width="@dimen/qs_tile_text_size"
+ android:layout_height="@dimen/qs_tile_text_size"
+ android:src="@drawable/ic_settings_lock_outline"
+ android:layout_marginLeft="@dimen/restricted_padlock_pading"
+ android:baselineAlignBottom="true"
+ android:scaleType="centerInside"
+ android:visibility="gone" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index af22f03..a22c360 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -40,12 +40,25 @@
systemui:framePadding="6dp"
systemui:activeFrameColor="@color/current_user_border_color"/>
- <TextView
- android:id="@+id/user_name"
+ <LinearLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="@dimen/qs_detail_item_secondary_text_size"
- android:textColor="@color/qs_user_detail_name"
- android:gravity="center_horizontal" />
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/qs_detail_item_secondary_text_size"
+ android:textColor="@color/qs_user_detail_name"
+ android:gravity="center_horizontal" />
+ <ImageView
+ android:id="@+id/restricted_padlock"
+ android:layout_width="@dimen/qs_detail_item_secondary_text_size"
+ android:layout_height="@dimen/qs_detail_item_secondary_text_size"
+ android:src="@drawable/ic_settings_lock_outline"
+ android:layout_marginLeft="@dimen/restricted_padlock_pading"
+ android:baselineAlignBottom="true"
+ android:scaleType="centerInside"
+ android:visibility="gone" />
+ </LinearLayout>
</com.android.systemui.qs.tiles.UserDetailItemView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 5b44c17..c813818 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -37,7 +37,8 @@
android:translationZ="4dp"
android:contentDescription="@string/recents_lock_to_app_button_label"
android:background="@drawable/recents_lock_to_task_button_bg"
- android:visibility="invisible">
+ android:visibility="invisible"
+ android:alpha="0">
<ImageView
android:layout_width="@dimen/recents_lock_to_app_icon_size"
android:layout_height="@dimen/recents_lock_to_app_icon_size"
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 04f18c5..07ac39a 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -63,4 +64,12 @@
android:background="@drawable/recents_button_bg"
android:visibility="invisible"
android:src="@drawable/recents_dismiss_light" />
+ <ProgressBar
+ android:id="@+id/focus_timer_indicator"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="5dp"
+ android:layout_gravity="bottom"
+ android:indeterminateOnly="false"
+ android:visibility="invisible" />
</com.android.systemui.recents.views.TaskViewHeader>
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 83cfc76..cce07bd 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -23,16 +23,16 @@
android:id="@+id/remote_input"
android:layout_height="match_parent"
android:layout_width="match_parent"
- android:paddingStart="16dp"
- android:paddingEnd="12dp"
- android:paddingBottom="4dp"
- android:paddingTop="2dp">
+ android:paddingEnd="12dp">
<view class="com.android.systemui.statusbar.policy.RemoteInputView$RemoteEditText"
android:id="@+id/remote_input_text"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1"
+ android:paddingTop="2dp"
+ android:paddingBottom="4dp"
+ android:paddingStart="16dp"
android:paddingEnd="12dp"
android:gravity="start|center_vertical"
android:textAppearance="?android:attr/textAppearance"
@@ -47,7 +47,9 @@
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical">
+ android:layout_gravity="center_vertical"
+ android:paddingTop="2dp"
+ android:paddingBottom="4dp">
<ImageButton
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 0187993..19fb28e 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Pas toe"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Bevestig instellings"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Sommige kleurinstellings kan hierdie toestel onbruikbaar maak. Klik OK om hierdie kleurinstellings te bevestig; andersins sal hierdie instellings ná 10 sekondes teruggestel word."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Battery (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterybespaarder is nie beskikbaar wanneer gelaai word nie"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterybespaarder"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Verminder werkverrigting en agtergronddata"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 503a767..208aaff 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"ተግብር"</string>
<string name="color_revert_title" msgid="4746666545480534663">"ቅንብሮችን ያረጋግጡ"</string>
<string name="color_revert_message" msgid="9116001069397996691">"አንዳንድ የቀለም ቅንብሮች ይህን መሣሪያ የማይጠቅም ሊያደርጉት ይችላሉ። እነዚህን የቀለም ቅንብሮች ለማረጋገጥ እሺ የሚለውን ጠቅ ያድርጉ፣ አለበለዚያ እነዚህ ቅንብሮች ከ10 ሰከንዶች በኋላ ዳግም ይጀምራሉ።"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ባትሪ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ኃይል በሚሞላበት ጊዜ ባትሪ ቆጣቢ አይገኝም"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"ባትሪ ቆጣቢ"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"አፈጻጸምን እና የጀርባ ውሂብን ይቀንሳል"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index d89d036..35e78c2 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -476,4 +476,8 @@
<string name="color_apply" msgid="9212602012641034283">"تطبيق"</string>
<string name="color_revert_title" msgid="4746666545480534663">"تأكيد الإعدادات"</string>
<string name="color_revert_message" msgid="9116001069397996691">"يمكن أن تتسبب بعض إعدادات الألوان في تعطيل إمكانية استخدام الجهاز. يمكنك النقر على \"موافق\" لتأكيد إعدادات الألوان هذه، وإلا فستتم إعادة تعيين هذه الإعدادات بعد 10 ثوانٍ."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"البطارية (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"وضع توفير شحن البطارية غير متاح أثناء الشحن."</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"توفير شحن البطارية"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"لخفض مستوى الأداء وبيانات الخلفية"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index 55f0841..864fd32 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Tətbiq edin"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Ayarları təsdiq edin"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Bəzi renk ayarları bu cihazı yararsız edə bilər. Bu rənk ayarlarını təsdiq etmək üçün OK basın, əks halda bu ayarlar 10 saniyə sonra sıfırlanacaq."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Enerji (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Enerji Qənaəti doldurulma zamanı əlçatan deyil"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Enerji Qənaəti"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Performansı azaldır və arxa fon datasını məhdudlaşdırır"</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 4056963..6879d75 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -473,4 +473,8 @@
<string name="color_apply" msgid="9212602012641034283">"Primeni"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Potvrdite podešavanja"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Neka podešavanja boja mogu da učine uređaj neupotrebljivim. Kliknite na Potvrdi da biste potvrdili ova podešavanja boja, pošto će se u suprotnom ova podešavanja resetovati nakon 10 sekundi."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Baterija (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ušteda baterije nije dostupna tokom punjenja"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Ušteda baterije"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Smanjuje performanse i pozadinske podatke"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 93c26a4..f5713c2 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Прилагане"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Потвърждаване на настройките"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Някои настройки за цветовете могат да направят това устройство неизползваемо. За да ги потвърдите, кликнете върху „OK“. В противен случай те ще бъдат нулирани след 10 секунди."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Батерия (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режимът за запазване на батерията не е налице при зареждане"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим за запазване на батерията"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Намалява ефективността и данните на заден план"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 939465b6..bfcb258 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"প্রয়োগ করুন"</string>
<string name="color_revert_title" msgid="4746666545480534663">"সেটিংস নিশ্চিত করুন"</string>
<string name="color_revert_message" msgid="9116001069397996691">"কিছু রঙের সেটিংস এই ডিভাইসকে ব্যবহারের অযোগ্য করে দিতে পারে৷ এই রঙের সেটিংস নিশ্চিত করতে ওকে এ ক্লিক করুন, অন্যথায় ১০ সেকেন্ড পরে এই সেটিংস পুনরায় সেট হবে৷"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ব্যাটারি (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"চার্জ করার সময় ব্যাটারি সেভার উপলব্ধ নয়"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"ব্যাটারি সেভার"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"কার্য-সম্পাদনা ও পশ্চাদপট ডেটাকে কমিয়ে দেয়"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index e19aa0e..c7a30f0 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Aplica"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirma la configuració"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Algunes opcions de configuració de color poden deixar el dispositiu inservible. Fes clic a D\'acord per confirmar la configuració de color; en cas contrari, la configuració es restablirà al cap de 10 segons."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"La funció Estalvi de bateria no està disponible durant la càrrega"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Estalvi de bateria"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Redueix el rendiment i les dades en segon pla"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index b1cd561..df18347 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -474,4 +474,8 @@
<string name="color_apply" msgid="9212602012641034283">"Použít"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Ověření nastavení"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Některá nastavení barev mohou způsobit, že zařízení nebude použitelné. Kliknutím na OK toto nastavení barev potvrdíte, v opačném případě se nastavení po 10 sekundách resetuje."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Baterie (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Spořič baterie při nabíjení není k dispozici."</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Spořič baterie"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Omezuje výkon a data na pozadí"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 2100c57..d69e943 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Anvend"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Bekræft indstillingerne"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Nogle farveindstillinger kan medføre, at du ikke kan bruge enheden. Klik på OK for at bekræfte disse farveindstillinger. Ellers nulstilles disse indstillinger efter ti sekunder."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Batteri (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparefunktionen er ikke tilgængelig under opladning"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparefunktion"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reducerer ydeevne og baggrundsdata"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 1ad5b26..9e17f253 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -35,7 +35,7 @@
<string name="battery_low_title" msgid="6456385927409742437">"Akku ist schwach"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string>
<string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend. Der Energiesparmodus ist aktiviert."</string>
- <string name="invalid_charger" msgid="4549105996740522523">"USB-Aufladung wird nicht unterstützt.\nVerwenden Sie das mitgelieferte Aufladegerät."</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"USB-Aufladung wird nicht unterstützt.\nVerwende das mitgelieferte Aufladegerät."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Laden per USB wird nicht unterstützt."</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"Verwenden Sie nur das im Lieferumfang enthaltene Ladegerät."</string>
<string name="battery_low_why" msgid="4553600287639198111">"Einstellungen"</string>
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Übernehmen"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Einstellungen bestätigen"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Einige Farbeinstellungen können dazu führen, dass das Gerät nicht mehr genutzt werden kann. Klicke auf \"OK\", um diese Farbeinstellungen zu bestätigen. Anderenfalls werden diese Einstellungen in 10 Sekunden zurückgesetzt."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Akku (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Energiesparmodus"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduzierung der Leistung und Hintergrunddaten"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index f56d630..fb09669 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Εφαρμογή"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Επιβεβαίωση ρυθμίσεων"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Ορισμένες ρυθμίσεις χρωμάτων μπορεί να μην επιτρέπουν τη χρήση αυτής της συσκευής. Κάντε κλικ στην επιλογή OK για να επιβεβαιώσετε αυτές τις ρυθμίσεις χρωμάτων, διαφορετικά θα γίνει επαναφορά αυτών των ρυθμίσεων μετά από 10 δευτερόλεπτα."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Μπαταρία (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Η εξοικονόμηση μπαταρίας δεν είναι διαθέσιμη κατά τη διάρκεια της φόρτισης"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Εξοικονόμηση μπαταρίας"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Μειώνει την απόδοση και τα δεδομένα παρασκηνίου"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index f6fc997..a96c23c 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Apply"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirm Settings"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Some colour settings can make this device unusable. Click OK to confirm these colour settings, otherwise these settings will reset after 10 seconds."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Battery (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduces performance and background data"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index f6fc997..a96c23c 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Apply"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirm Settings"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Some colour settings can make this device unusable. Click OK to confirm these colour settings, otherwise these settings will reset after 10 seconds."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Battery (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduces performance and background data"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index f6fc997..a96c23c 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Apply"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirm Settings"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Some colour settings can make this device unusable. Click OK to confirm these colour settings, otherwise these settings will reset after 10 seconds."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Battery (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Battery Saver not available during charging"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduces performance and background data"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index fd59418..6ba8a43 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirmar la configuración"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Algunas opciones de configuración de color pueden provocar que el dispositivo quede inutilizable. Haz clic en Aceptar para confirmar estos parámetros de color. De lo contrario, la configuración se restablecerá en diez segundos."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Batería (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ahorro de batería no está disponible durante la carga"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Ahorro de batería"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce el rendimiento y el uso de datos en segundo plano"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 223e684..0598078 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirmar configuración"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Algunas opciones de configuración de color pueden hacer que el dispositivo no se pueda utilizar. Haz clic en Aceptar para confirmar esta configuración. Si no lo haces, se restablecerá esta configuración cuando transcurran 10 segundos."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Batería (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ahorro de batería no disponible mientras se carga el dispositivo"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Ahorro de batería"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce el rendimiento y las conexiones automáticas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index f50e78c..8d25d48 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Rakenda"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Seadete kinnitamine"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Mõni värviseade ei saa seadet võib-olla kasutada. Nende värviseadete kinnitamiseks klõpsake OK, muidu lähtestatakse need seaded 10 sekundi pärast."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Aku (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akusäästja pole laadimise ajal saadaval"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Akusäästja"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Vähendab jõudlust ja taustaandmeid"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index c481f52..fc96b42 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Aplikatu"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Berretsi ezarpenak"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Baliteke gailua kolore-ezarpen batzuekin ezin erabili izatea. Kolore-ezarpenak berresteko, sakatu Ados. Bestela, hamar segundoren buruan berrezarriko dira ezarpenak."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (%% <xliff:g id="ID_1">%1$d</xliff:g>)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Bateria-aurrezlea ez dago erabilgarri gailua kargatzen ari denean"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Bateria-aurrezlea"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Errendimendua eta atzeko planoko datuen erabilera murrizten ditu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 3dee7c9..3a98e9c 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"اعمال کردن"</string>
<string name="color_revert_title" msgid="4746666545480534663">"تأیید تنظیمات"</string>
<string name="color_revert_message" msgid="9116001069397996691">"بعضی از تنظیمات رنگ میتوانند این دستگاه را غیرقابل استفاده کنند. برای تأیید این تنظیمات رنگ روی «تأیید» کلیک کنید، در غیر این صورت این تغییرات بعد از ۱۰ ثانیه بازنشانی میشوند."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"باتری (<xliff:g id="ID_1">%1$d</xliff:g>٪٪)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"هنگام شارژ شدن، «بهینهسازی باتری» در دسترس نیست"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"بهینهسازی باتری"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"عملکرد و اطلاعات پسزمینه را کاهش میدهد"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 4a5c302..8c963af 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Käytä"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Vahvista asetukset"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Jotkin väriasetukset voivat häiritä laitteen käyttöä. Vahvista uudet väriasetukset valitsemalla OK. Muussa tapauksessa aiemmat asetukset palautetaan 10 sekunnin kuluttua."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Akku (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Virransäästö ei ole käytettävissä latauksen aikana."</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Virransäästö"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Rajoittaa suorituskykyä ja taustatiedonsiirtoa."</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 2a4f4d9..bcde17a 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Appliquer"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirmer les paramètres"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Certains paramètres de couleurs peuvent rendre cet appareil inutilisable. Cliquez sur « OK » pour valider ces paramètres, sinon ils seront réinitialisés après 10 secondes."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Pile (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Le mode Économie d\'énergie n\'est pas accessible pendant la charge"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Économie d\'énergie"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Réduit les performances et les données en arrière-plan"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 928a8b3..952baab 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Appliquer"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Vérifier les paramètres"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Certains paramètres de couleurs peuvent rendre cet appareil inutilisable. Cliquez sur \"OK\" pour valider ces paramètres, sans quoi ils seront réinitialisés après 10 secondes."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Batterie (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"L\'économiseur de batterie n\'est pas disponible lorsque l\'appareil est en charge."</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Économiseur de batterie"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Limite les performances et les données en arrière-plan."</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 2a951f7..9d490c2 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirmar configuración"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Algunhas opcións de configuración de cor poden facer que este dispositivo sexa inutilizable. Fai clic en Aceptar para confirmar esta configuración de cor; en caso contrario, a configuración restablecerase tras 10 segundos."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Batería (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"A función aforro de batería non está dispoñible durante a carga"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Aforro de batería"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce o rendemento e os datos en segundo plano"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index f331ef9..d45167f 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"લાગુ કરો"</string>
<string name="color_revert_title" msgid="4746666545480534663">"સેટિંગ્સની પુષ્ટિ કરો"</string>
<string name="color_revert_message" msgid="9116001069397996691">"કેટલીક રંગ સેટિંગ્સ આ ઉપકરણને બિનઉપયોગી બનાવી શકે છે. આ રંગ સેટિંગ્સની પુષ્ટિ કરવા માટે ઑકે ક્લિક કરો, અન્યથા 10 સેકંડ પછી આ સેટિંગ્સ ફરીથી સેટ થશે."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"બૅટરી (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ચાર્જિંગ દરમિયાન બૅટરી બચતકર્તા ઉપલબ્ધ નથી"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"બૅટરી બચતકર્તા"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"પ્રદર્શન અને પૃષ્ઠભૂમિ ડેટા ઘટાડે છે"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index b31c1e0..3e2ab2a 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"लागू करें"</string>
<string name="color_revert_title" msgid="4746666545480534663">"सेेटिंग की पुष्टि करें"</string>
<string name="color_revert_message" msgid="9116001069397996691">"कुछ रंग सेटिंग इस डिवाइस को अनुपयोगी बना सकती हैं. इन रंग सेटिंग की पुष्टि करने के लिए ठीक क्लिक करें, अन्यथा 10 सेकंड के बाद ये सेटिंग रीसेट हो जाएंगी."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"बैटरी (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज किए जाने के दौरान बैटरी सेवर उपलब्ध नहीं है"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"बैटरी सेवर"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"निष्पादन और पृष्ठभूमि डेटा को कम करता है"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 3bef6e9..fbdc5fe 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -473,4 +473,8 @@
<string name="color_apply" msgid="9212602012641034283">"Primijeni"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Potvrdite postavke"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Neke postavke boja mogu učiniti uređaj neupotrebljivim. Kliknite U redu da biste potvrdili postavke boja jer će se u suprotnom poništiti za 10 sekundi."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Baterija (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Štednja baterije nije dostupna tijekom punjenja"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Štednja baterije"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Smanjuje količinu rada i pozadinske podatke"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 8dc9a19..9d26c36 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Alkalmaz"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Beállítások megerősítése"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Bizonyos színbeállítások használhatatlanná tehetik ezt az eszközt. A színbeállítás megerősítéséhez kattintson az OK lehetőségre, máskülönben a rendszer 10 másodpercen belül visszaáll a korábbira."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Akkumulátor (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Az Akkumulátorkímélő módot töltés közben nem lehet használni"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Akkumulátorkímélő mód"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Csökkenti a teljesítményt és a háttéradatok használatát"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 99d6980..29e3a29 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Կիրառել"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Հաստատել կարգավորումները"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Գունային որոշ կարգավորումները կարող են այս սարքը օգտագործման համար ոչ պիտանի դարձնել: Սեղմեք Լավ կոճակը՝ գունային այս կարգավորումները հաստատելու համար: Հակառակ դեպքում այս կարգավորումները կվերակայվեն 10 վայրկյան հետո:"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Մարտկոց (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Մարտկոցի տնտեսումը լիցքավորման ժամանակ հասանելի չէ"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Մարտկոցի տնտեսում"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Նվազեցնում է ծանրաբեռնվածությունը և ֆոնային տվյալները"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index f6124db..f7f3370 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Terapkan"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Konfirmasi setelan"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Beberapa setelan warna dapat membuat perangkat ini tidak dapat digunakan. Klik OKE untuk mengonfirmasi setelan warna ini. Jika tidak, setelan ini akan disetel ulang setelah 10 detik."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Baterai (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Penghemat Baterai tidak tersedia selama pengisian daya"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Penghemat Baterai"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Mengurangi performa dan data latar belakang"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 7ea1746..c1d3591 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Nota"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Staðfesta stillingar"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Sumar litastillingar kunna að bitna á notagildi tækisins. Veldu „Í lagi“ til að staðfesta þessar litastillingar, að öðrum kosti verða litirnir endurstilltir eftir tíu sekúndur."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Rafhlaða (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ekki er hægt að nota rafhlöðusparnað meðan á hleðslu stendur"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Rafhlöðusparnaður"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Dregur úr afköstum og bakgrunnsgögnum"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index b395f8b..dd65970 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Applica"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Conferma le impostazioni"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Alcune impostazioni relative ai colori potrebbero rendere inutilizzabile il dispositivo. Fai clic su OK per confermare queste impostazioni; in caso contrario, le impostazioni verranno reimpostate dopo 10 secondi."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Batteria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Risparmio energetico non disponibile durante la ricarica"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Risparmio energetico"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Riduce le prestazioni e i dati in background"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 2c894f5..95bbe20 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -474,4 +474,8 @@
<string name="color_apply" msgid="9212602012641034283">"החל"</string>
<string name="color_revert_title" msgid="4746666545480534663">"אישור הגדרות"</string>
<string name="color_revert_message" msgid="9116001069397996691">"הגדרות צבע מסוימות עלולות להפוך את המכשיר הזה לבלתי שמיש. לחץ על אישור כדי לאשר את הגדרות הצבע האלה, אחרת הגדרות אלה יתאפסו לאחר 10 שניות."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"סוללה (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"תכונת החיסכון בסוללה אינה זמינה בעת טעינת המכשיר"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"חיסכון בסוללה"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"מפחית את רמת הביצועים ואת נתוני הרקע"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 580d995..ed0c7e9 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"適用"</string>
<string name="color_revert_title" msgid="4746666545480534663">"設定の確認"</string>
<string name="color_revert_message" msgid="9116001069397996691">"一部の色設定を適用すると、この端末を使用できなくなることがあります。この色設定を確認するには、[OK] をクリックしてください。確認しない場合、10 秒後に設定はリセットされます。"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"バッテリー(<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電中はバッテリー セーバーは利用できません"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"バッテリー セーバー"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"パフォーマンスとバックグラウンド データを制限します"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 7a51668..5c861f8 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"გამოყენება"</string>
<string name="color_revert_title" msgid="4746666545480534663">"პარამეტრების დადასტურება"</string>
<string name="color_revert_message" msgid="9116001069397996691">"ფერთა ზოგიერთ პარამეტრს ამ მოწყობილობასთან მუშაობის გართულება შეუძლია. ფერთა ამჟამინდელი პარამეტრების დასადასტურებლად, დააწკაპუნეთ „კარგი“-ზე. წინააღმდეგ შემთხვევაში, პარამეტრები 10 წამის შემდეგ ჩამოიყრება."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ბატარეა (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ბატარეის დამზოგი დატენვისას მიწვდომელია"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"ბატარეის დამზოგი"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ამცირებს წარმადობას და ფონურ მონაცემებს"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index eb8995e..42e6a5d 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Қолдану"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Параметрлерді растау"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Кейбір түс параметрлері бұл құрылғыны пайдалану мүмкін емес етуі мүмкін. Бұл түс параметрлерін растау үшін OK түймесін басыңыз, әйтпесе параметрлер 10 секундтан кейін ысырылады."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Батарея (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Зарядтау кезінде Батарея үнемдегіш қол жетімді емес"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Батарея үнемдегіш"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Өнімділікті және фондық деректерді азайтады"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 5c10213..53f5868 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"អនុវត្ត"</string>
<string name="color_revert_title" msgid="4746666545480534663">"បញ្ជាក់ការកំណត់"</string>
<string name="color_revert_message" msgid="9116001069397996691">"ការកំណត់ពណ៌មួយចំនួនអាចធ្វើឲ្យឧបករណ៍នេះមិនអាចប្រើបាន។ សូមចុច យល់ព្រម ដើម្បីបញ្ជាក់ការកំណត់ពណ៌ទាំងនេះ បើមិនដូច្នេះទេការកំណត់ទាំងនេះនឹងកំណត់ឡើងវិញក្នុងរយៈពេល 10 វិនាទីបន្ទាប់។"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ថ្ម (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"កម្មវិធីសន្សំថ្មមិនអាចប្រើបានអំឡុងពេលសាកថ្មទេ"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"កម្មវិធីសន្សំថ្ម"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"កាត់បន្ថយប្រតិបត្តិការ និងទិន្នន័យផ្ទៃខាងក្រោយ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 28b2de5..1b212f0 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"ಅನ್ವಯಿಸು"</string>
<string name="color_revert_title" msgid="4746666545480534663">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಖಚಿತಪಡಿಸಿ"</string>
<string name="color_revert_message" msgid="9116001069397996691">"ಕೆಲವು ಬಣ್ಣ ಸೆಟ್ಟಿಂಗ್ಗಳು ಈ ಸಾಧನವನ್ನು ಅನುಪಯುಕ್ತಗೊಳಿಸಬಹುದು. ಈ ಬಣ್ಣ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಖಚಿತಪಡಿಸಲು ಸರಿ ಕ್ಲಿಕ್ ಮಾಡಿ, ಇಲ್ಲವಾದರೆ ಈ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು 10 ಸೆಕೆಂಡುಗಳ ನಂತರ ಮರುಹೊಂದಿಸಿ."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ಬ್ಯಾಟರಿ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ಚಾರ್ಜಿಂಗ್ ಸಮಯದಲ್ಲಿ ಬ್ಯಾಟರಿ ಸೇವರ್ ಲಭ್ಯವಿರುವುದಿಲ್ಲ"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"ಬ್ಯಾಟರಿ ಸೇವರ್"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ಕಾರ್ಯಕ್ಷಮತೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಡೇಟಾವನ್ನು ಕಡಿಮೆ ಮಾಡುತ್ತದೆ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 110376f..ecff2ad 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"적용"</string>
<string name="color_revert_title" msgid="4746666545480534663">"설정 확인"</string>
<string name="color_revert_message" msgid="9116001069397996691">"일부 색상 설정으로 인해 이 기기를 사용하지 못할 수 있습니다. 확인을 클릭하여 이러한 색상 설정을 확인하지 않으면 10초 후에 설정이 초기화됩니다."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"배터리(<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"충전하는 동안 배터리 세이버는 사용할 수 없습니다."</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"배터리 세이버"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"성능 및 백그라운드 데이터를 줄입니다."</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index ee2ef5d..ad430ec 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Колдонуу"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Жөндөөлөрдү ырастоо"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Айрым түс жөндөөлөрү бул түзмөктү колдонулгус кылып коюшу мүмкүн. Бул түс жөндөөлөрүн ырастоо үчүн OK баскычын чыкылдатыңыз, болбосо бул жөндөөлөр 10 секунддан кийин баштапкы абалына келтирилет."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Батарея (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Батареяны үнөмдөгүч кубаттоо учурунда иштебейт"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Батареяны үнөмдөгүч"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Иштин майнаптуулугун начарлатып, фондук дайындарды чектейт"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index ed103ea..48d94cd 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"ນຳໃຊ້"</string>
<string name="color_revert_title" msgid="4746666545480534663">"ຢືນຢັນການຕັ້ງຄ່າ"</string>
<string name="color_revert_message" msgid="9116001069397996691">"ບາງການຕັ້ງຄ່າສີສາມາດເຮັດໃຫ້ອຸປະກອນນີ້ບໍ່ສາມາດໃຊ້ໄດ້. ຄລິກ ຕົກລົງ ເພື່ອຢືນຢັນການຕັ້ງຄ່າສີເຫຼົ່ານີ້, ຖ້າບໍ່ດັ່ງນັ້ນ ການຕັ້ງຄ່າເຫຼົ່ານີ້ຈະຕັ້ງຄືນໃໝ່ ຫຼັງຈາກ 10 ວິນາທີ."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ແບັດເຕີຣີ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ຕົວປະຢັດແບັດເຕີຣີບໍ່ມີໃຫ້ນຳໃຊ້ໃນລະຫວ່າງການສາກ"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"ຕົວປະຢັດແບັດເຕີຣີ"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ຫຼຸດປະສິທິພາບການໃຊ້ງານ ແລະ ຂໍ້ມູນພື້ນຫຼັງ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index ba98e2b..adb5a53 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -474,4 +474,8 @@
<string name="color_apply" msgid="9212602012641034283">"Taikyti"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Nustatymų patvirtinimas"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Dėl kai kurių spalvų nustatymų įrenginys gali būti netinkamas naudoti. Spustelėkite „Gerai“, kad patvirtintumėte šiuos spalvų nustatymus. Kitaip šie nustatymai bus nustatyti iš naujo po 10 sekundžių."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Akumuliatorius (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akumuliatoriaus tausojimo priemonė nepasiekiama įkraunant"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Akumuliatoriaus tausojimo priemonė"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Sumažinamas našumas ir foninių duomenų naudojimas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 0143fea..b8a6d01 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -473,4 +473,8 @@
<string name="color_apply" msgid="9212602012641034283">"Lietot"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Iestatījumu apstiprināšana"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Noteiktu krāsu iestatījumu dēļ šī ierīce var kļūt nelietojama. Lai apstiprinātu šos krāsu iestatījumus, noklikšķiniet uz Labi. Ja to neizdarīsiet, pēc 10 sekundēm šie iestatījumi tiks atiestatīti."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Akumulators (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Akumulatora jaudas taupīšanas režīms uzlādes laikā nav pieejams."</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Akumulatora jaudas taupīšanas režīms"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Samazina veiktspēju un fona datus."</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index c573139..8ba8094 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Примени"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Потврдете ги поставките"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Некои поставки на боите може да го направат уредот неупотреблив. Кликнете на Во ред за да ги потврдите овие поставки на боите, инаку тие поставки ќе се ресетираат по 10 секунди."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Батерија (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Штедачот на батерија не е достапен при полнење"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Штедач на батерија"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Ја намалува изведбата и податоците во заднина"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index c6a08e6..240373b 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"ബാധകമാക്കുക"</string>
<string name="color_revert_title" msgid="4746666545480534663">"ക്രമീകരണം സ്ഥിരീകരിക്കുക"</string>
<string name="color_revert_message" msgid="9116001069397996691">"ചില വർണ്ണ ക്രമീകരണത്തിന് ഈ ഉപകരണത്തെ ഉപയോഗരഹിതമാക്കാനാകും. ഈ വർണ്ണ ക്രമീകരണം സ്ഥിരീകരിക്കുന്നതിന് ശരി എന്നതിൽ ക്ലിക്കുചെയ്യുക, അല്ലെങ്കിൽ 10 സെക്കൻഡിന് ശേഷം ഈ ക്രമീകരണം പുനഃക്രമീകരിക്കപ്പെടും."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ബാറ്ററി (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ചാർജുചെയ്യുന്ന സമയത്ത് ബാറ്ററി സേവർ ലഭ്യമല്ല"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"ബാറ്ററി സേവർ"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"പ്രവർത്തനവും പശ്ചാത്തല ഡാറ്റയും കുറയ്ക്കുന്നു"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 91c817a..de5c035 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -470,4 +470,8 @@
<string name="color_apply" msgid="9212602012641034283">"Хэрэгжүүлэх"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Тохиргоог баталгаажуулах"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Зарим өнгөний тохиргоо энэ төхөөрөмжийг ашиглах боломжгүй болгож болзошгүй. OK товчлуурыг дарж эдгээр өнгөний тохиргоог зөвшөөрөхгүй бол энэ тохиргоо нь 10 секундын дараа шинэчлэгдэх болно."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Тэжээл (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Цэнэглэх үед тэжээл хэмнэгч ажиллахгүй"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Тэжээл хэмнэгч"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Гүйцэтгэл болон дэвсгэрийн датаг багасгадаг"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 5d3a96f..80dc12e 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"लागू करा"</string>
<string name="color_revert_title" msgid="4746666545480534663">"सेटिंग्जची पुष्टी करा"</string>
<string name="color_revert_message" msgid="9116001069397996691">"काही रंग सेटिंग्ज या डिव्हाइसला निरुपयोगी करू शकतात. या रंग सेटिंग्जची पुष्टी करण्यासाठी ठीक आहे दाबा अन्यथा या सेटिंग्ज 10 सेकंदांनंतर रीसेट होतील."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"बॅटरी (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज करताना बॅटरी बचतकर्ता उपलब्ध नाही"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"बॅटरी बचतकर्ता"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"कार्यप्रदर्शन आणि पार्श्वभूमी डेटा कमी करते"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index f0a013c..ec4b28c 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Gunakan"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Sahkan tetapan"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Sesetengah tetapan warna boleh menjadikan peranti ini tidak dapat digunakan. Klik OK untuk mengesahkan tetapan warna ini, jika tidak, tetapan ini akan ditetapkan semula selepas 10 saat."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Bateri (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Penjimat Bateri tidak tersedia semasa mengecas"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Penjimat Bateri"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Mengurangkan prestasi dan data latar belakang"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 75008cb..257dd43 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"အသုံးပြုပါ"</string>
<string name="color_revert_title" msgid="4746666545480534663">"ဆက်တင်များကို အတည်ပြုပါ"</string>
<string name="color_revert_message" msgid="9116001069397996691">"အချို့သော အရောင်ဆက်တက်များက ဤကိရိယာကို သုံးမရအောင် လုပ်ပစ်နိုင်ပါသည်။ ဤအရောင် ဆက်တင်များကို အတည်ပြုရန် အိုကေကို နှိပ်ပါ၊ သို့မဟုတ် ဤဆက်တင်များကို ၁၀ စက္ကန့် အကြာတွင် ပြန်ညှိလိုက်ပါမည်။"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ဘက်ထရီ ( <xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"အားသွင်းနေချိန်မှာ Battery Saver ကို သုံးမရပါ"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Battery Saver"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"လုပ်ဆောင်မှု နှင့် နောက်ခံ ဒေတာကို လျော့နည်းစေပါသည်"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 295a49a..4446a77 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Bruk"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Bekreft innstillingene"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Noen fargeinnstillinger kan gjøre denne enheten ubrukelig. Klikk på OK for å bekrefte disse fargeinnstillingene, ellers blir de tilbakestilt etter ti sekunder."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Batteri (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparing er ikke tilgjengelig under lading"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparing"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduserer ytelsen og begrenser bakgrunnsdataene"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index d0aa510..f1009550 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"लागू गर्नुहोस्"</string>
<string name="color_revert_title" msgid="4746666545480534663">"सेटिङहरूको पुष्टि गर्नुहोस्"</string>
<string name="color_revert_message" msgid="9116001069397996691">"केही रङ सेटिङहरूले यस यन्त्रलाई अनुपयोगी बनाउन सक्छन्। यी रङ सेटिङहरू पुष्टि गर्न ठीक छ मा क्लिक गर्नुहोस्, अन्यथा यी सेटिङहरू १० सेकेण्डपछि रिसेट हुनेछन्।"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ब्याट्री (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"चार्ज गर्ने समयमा ब्याट्री सेभर उपलब्ध छैन"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"ब्याट्री सेभर"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"कार्यसम्पादन र पृष्ठभूमि डेटा घटाउँछ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 75812ad..c6562d6 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Toepassen"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Instellingen bevestigen"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Bij sommige kleurinstellingen kan het apparaat onbruikbaar worden. Klik op OK om deze kleurinstellingen te bevestigen, anders worden deze instellingen na tien seconden gereset."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Accu (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Accubesparing niet beschikbaar tijdens opladen"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Accubesparing"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Vermindert de prestaties en achtergrondgegevens"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index fc5a3ea..fd17dac 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"ਲਾਗੂ ਕਰੋ"</string>
<string name="color_revert_title" msgid="4746666545480534663">"ਸੈਟਿੰਗਾਂ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ"</string>
<string name="color_revert_message" msgid="9116001069397996691">"ਕੁਝ ਰੰਗ ਸੈਟਿੰਗਾਂ ਇਸ ਡੀਵਾਈਸ ਨੂੰ ਬੇਕਾਰ ਕਰ ਸਕਦੀਆਂ ਹਨ। ਇਹਨਾਂ ਰੰਗ ਸੈਟਿੰਗਾਂ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਠੀਕ \'ਤੇ ਕਲਿੱਕ ਕਰੋ, ਨਹੀਂ ਤਾਂ ਇਹ ਸੈਟਿੰਗਾਂ 10 ਸਕਿੰਟ ਬਾਅਦ ਮੁੜ-ਸੈੱਟ ਹੋ ਜਾਣਗੀਆਂ।"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"ਬੈਟਰੀ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ਬੈਟਰੀ ਸੇਵਰ ਚਾਰਜਿੰਗ ਦੌਰਾਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"ਬੈਟਰੀ ਸੇਵਰ"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ਕਾਰਗੁਜ਼ਾਰੀ ਅਤੇ ਬੈਕਗ੍ਰਾਊਂਡ ਡੈਟੇ ਨੂੰ ਘਟਾਉਂਦਾ ਹੈ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 8ef50c9..3630429 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -474,4 +474,8 @@
<string name="color_apply" msgid="9212602012641034283">"Zastosuj"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Potwierdź ustawienia"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Niektóre ustawienia kolorów mogą utrudniać korzystanie z urządzenia. Kliknij OK, by potwierdzić te ustawienia kolorów. Jeśli tego nie zrobisz, zostaną one zresetowane po 10 sekundach."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Oszczędzanie baterii nie jest dostępne podczas ładowania"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Oszczędzanie baterii"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Zmniejsza wydajność i ogranicza dane w tle"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 8f61478..7fa28ad 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirmar configurações"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Algumas configurações de cor podem tornar o dispositivo inutilizável. Clique em \"OK\" para confirmar essas configurações de cor; caso contrário, essas configurações serão redefinidas após 10 segundos."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"A Economia de bateria não fica disponível durante o carregamento"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Economia de bateria"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduz o desempenho e os dados em segundo plano"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 8a727a1..3d29d7e 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirmar as definições"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Algumas definições de cor podem tornar este dispositivo instável. Clique em OK para confirmar estas definições de cor. Caso contrário, estas definições serão repostas após 10 segundos."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Poupança de bateria não disponível durante o carregamento"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Poupança de bateria"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduz o desempenho e os dados de segundo plano"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 8f61478..7fa28ad 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Aplicar"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirmar configurações"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Algumas configurações de cor podem tornar o dispositivo inutilizável. Clique em \"OK\" para confirmar essas configurações de cor; caso contrário, essas configurações serão redefinidas após 10 segundos."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"A Economia de bateria não fica disponível durante o carregamento"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Economia de bateria"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduz o desempenho e os dados em segundo plano"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3ab8bf2..13f3ef2 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -473,4 +473,8 @@
<string name="color_apply" msgid="9212602012641034283">"Aplicați"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Confirmați setările"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Unele setări pentru culori pot face dispozitivul să nu mai funcționeze. Dați clic pe OK pentru a confirma aceste setări pentru culori. În caz contrar, acestea se vor reseta după 10 secunde."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Baterie (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Economisirea bateriei nu este disponibilă pe durata încărcării"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Economisirea bateriei"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce performanța și datele de fundal"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 6ffd333..4d7ee69 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -474,4 +474,8 @@
<string name="color_apply" msgid="9212602012641034283">"Применить"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Подтвердите настройки"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Некоторые цветовые настройки могут затруднить работу с устройством. Чтобы применить выбранные параметры, нажмите \"ОК\". В противном случае они будут сброшены через 10 секунд."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Батарея (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режим энергосбережения нельзя включить во время зарядки"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим энергосбережения"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Замедляет работу устройства и ограничивает фоновую передачу данных"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index efbc042..f73ff6c 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"යොදන්න"</string>
<string name="color_revert_title" msgid="4746666545480534663">"සැකසීම් තහවුරු කරන්න"</string>
<string name="color_revert_message" msgid="9116001069397996691">"සමහර වර්ණ සැකසීම් මෙම උපාංගය භාවිත කළ නොහැකි තත්ත්වයට පත් කළ හැකිය. මෙම වර්ණ සැකසීම් තහවුරු කිරීමට හරි ක්ලික් කරන්න, නැතහොත් මෙම සැකසීම් තත්පර 10කට පසුව යළි සකසනු ඇත."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"බැටරිය (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ආරෝපණය අතරතුර බැටරි සුරැකුම ලබා ගත නොහැකිය."</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"බැටරි සුරැකුම"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ක්රියාකාරිත්වය සහ පසුබිම් දත්ත අඩු කරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 31985b4..ddf2de8 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -474,4 +474,8 @@
<string name="color_apply" msgid="9212602012641034283">"Použiť"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Potvrdenie nastavení"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Niektoré nastavenia farieb môžu toto zariadenie znefunkčniť. Tieto nastavenia farieb potvrdíte kliknutím na tlačidlo OK, ináč sa tieto nastavenia o 10 sekúnd obnovia."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Batéria (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Počas nabíjania nie je Šetrič batérie k dispozícii"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Šetrič batérie"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Obmedzí výkonnosť a údaje na pozadí"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 7c8d443..249c1ad 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -474,4 +474,8 @@
<string name="color_apply" msgid="9212602012641034283">"Uporabi"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Potrditev nastavitev"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Zaradi nekaterih barvnih nastavitev lahko postane ta naprava neuporabna. Kliknite »V redu«, če želite potrditi te barvne nastavitve. V nasprotnem primeru se bodo čez 10 sekund ponastavile na prvotno vrednost."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Akumulator (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Varčevanje z energijo akumulatorja med polnjenjem ni na voljo"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Varčevanje z energijo akumulatorja"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Omeji zmogljivost delovanja in prenos podatkov v ozadju"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index c8add94..60e2e80 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Zbato"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Konfirmo cilësimet"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Disa cilësime ngjyrash mund ta bëjnë këtë pajisje të papërdorshme. Kliko OK për të konfirmuar këto cilësime ngjyrash, përndryshe këto cilësime do të rivendosen pas 10 sekondash."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Bateria (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"\"Kursyesi i baterisë\" nuk është i disponueshëm gjatë karikimit"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Kursyesi i baterisë"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Pakëson veprimtarinë dhe të dhënat në sfond"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index bec1fd4..4954f05 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -473,4 +473,8 @@
<string name="color_apply" msgid="9212602012641034283">"Примени"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Потврдите подешавања"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Нека подешавања боја могу да учине уређај неупотребљивим. Кликните на Потврди да бисте потврдили ова подешавања боја, пошто ће се у супротном ова подешавања ресетовати након 10 секунди."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Батерија (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Уштеда батерије није доступна током пуњења"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Уштеда батерије"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Смањује перформансе и позадинске податке"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index d4a9cdc..8f67f0e 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Verkställ"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Bekräfta inställningarna"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Vissa färginställningar kan göra den här enheten oanvändbar. Klicka på OK om du vill bekräfta färginställningarna, annars återställs inställningarna efter 10 sekunder."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Batteri (<xliff:g id="ID_1">%1$d</xliff:g> %%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Batterisparläget är inte tillgängligt vid laddning"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Batterisparläge"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Minskar prestanda och bakgrundsdata"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 2ffbf2b..0f66826 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Tumia"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Thibitisha mipangilio"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Baadhi ya mipangilio ya rangi inaweza kufanya kifaa hiki kisitumike. Bofya Sawa ili uthibitishe mipangilio hii ya rangi, vinginevyo, mipangilio hii itajiweka upya baada ya sekunde 10."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Betri (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Kiokoa Betri hakipatikani unapochaji betri"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Kiokoa Betri"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Hupunguza data ya chini chini na utendaji"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index 168a11b..0653843 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"பயன்படுத்து"</string>
<string name="color_revert_title" msgid="4746666545480534663">"அமைப்புகளை உறுதிப்படுத்து"</string>
<string name="color_revert_message" msgid="9116001069397996691">"சில வண்ண அமைப்புகள் இந்தச் சாதனத்தைப் பயன்படுத்த முடியாதபடி செய்யலாம். இந்த வண்ண அமைப்புகளை உறுதிப்படுத்த, சரி என்பதைக் கிளிக் செய்யவும், இல்லையெனில் இந்த அமைப்புகள் 10 வினாடிகளுக்குப் பின் மீட்டமைக்கப்படும்."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"பேட்டரி (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"சார்ஜ் செய்யும் போது பேட்டரி சேமிப்பானைப் பயன்படுத்த முடியாது"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"பேட்டரி சேமிப்பான்"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"செயல்திறனையும் பின்புலத்தில் தரவு செயலாக்கப்படுவதையும் குறைக்கும்"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 086dede..f711544 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"వర్తింపజేయి"</string>
<string name="color_revert_title" msgid="4746666545480534663">"సెట్టింగ్లను నిర్ధారించండి"</string>
<string name="color_revert_message" msgid="9116001069397996691">"కొన్ని రంగు సెట్టింగ్ల వలన ఈ పరికరం ఉపయోగించలేని విధంగా అయిపోవచ్చు. ఈ రంగు సెట్టింగ్లను నిర్ధారించడానికి సరే క్లిక్ చేయండి లేదంటే ఈ సెట్టింగ్లు 10 సెకన్ల తర్వాత రీసెట్ చేయబడతాయి."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"బ్యాటరీ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ఛార్జ్ అవుతున్న సమయంలో బ్యాటరీ సేవర్ అందుబాటులో ఉండదు"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"బ్యాటరీ సేవర్"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"పనితీరుని మరియు నేపథ్య డేటాను తగ్గిస్తుంది"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 8c1a7d3..8c284a1 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"ใช้"</string>
<string name="color_revert_title" msgid="4746666545480534663">"ยืนยันการตั้งค่า"</string>
<string name="color_revert_message" msgid="9116001069397996691">"การตั้งค่าสีบางอย่างอาจทำให้อุปกรณ์นี้ใช้งานไม่ได้ คลิกตกลงเพื่อยืนยันการตั้งค่าสีเหล่านี้ มิฉะนั้นระบบจะรีเซ็ตการตั้งค่าหลังจาก 10 วินาที"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"แบตเตอรี่ (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ไม่สามารถใช้โหมดประหยัดแบตเตอรี่ระหว่างการชาร์จ"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"โหมดประหยัดแบตเตอรี่"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ลดประสิทธิภาพการทำงานและข้อมูลแบ็กกราวด์"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 4bee3ea..33e5968 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Ilapat"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Kumpirmahin ang mga setting"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Maaaring hindi magamit ang device na ito dahil sa ilang setting ng kulay. I-click ang OK upang kumpirmahin ang mga setting ng kulay na ito, kung hindi ay mare-reset ang mga setting na ito pagkatapos ng 10 segundo."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Baterya (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Hindi available ang Pangtipid sa Baterya kapag nagcha-charge"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Pangtipid sa Baterya"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Binabawasan ang pagganap at data sa background"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index c0ecc78..d937dc3 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Uygula"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Ayarları onaylayın"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Bazı renkler bu cihazı kullanılmaz yapabilir. Bu renkleri onaylamak için Tamam\'ı tıklayın. Tıklamazsanız bu ayarlar 10 saniye sonra sıfırlanacaktır."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Pil (%%<xliff:g id="ID_1">%1$d</xliff:g>)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Şarj sırasında Pil Tasarrufu özelliği kullanılamaz"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Pil Tasarrufu"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Performansı ve arka plan verilerini azaltır"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 3109403..5503cb8 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -474,4 +474,8 @@
<string name="color_apply" msgid="9212602012641034283">"Застосувати"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Підтвердити налаштування"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Деякі налаштування кольорів можуть зробити цей пристрій непридатним для використання. Натисніть OK, щоб підтвердити налаштування, інакше їх буде скинуто через 10 секунд."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Акумулятор (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Режим економії заряду акумулятора недоступний під час заряджання"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Режим економії заряду акумулятора"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Знижується продуктивність і обмежується обмін даними у фоновому режимі"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 52aa580..f3dea97 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"لاگو کریں"</string>
<string name="color_revert_title" msgid="4746666545480534663">"ترتیبات کی توثیق کریں"</string>
<string name="color_revert_message" msgid="9116001069397996691">"رنگوں کی کچھ ترتیبات اس آلے کو ناقابل استعمال بنا سکتی ہیں۔ رنگوں کی ان ترتیبات کی توثیق کرنے کیلئے ٹھیک ہے پر کلک کریں، بصورت دیگر 10 سیکنڈ بعد یہ ترتیبات ری سیٹ ہو جائیں گی۔"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"بیٹری (%%<xliff:g id="ID_1">%1$d</xliff:g>)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"چارجنگ کے دوران بیٹری سیور دستیاب نہیں ہے"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"بیٹری سیور"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"کارکردگی اور پس منظر کا ڈیٹا کم کر دیتا ہے"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index a849cfc..c781082 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Qo‘llash"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Sozlamalarni tasdiqlang"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Ba’zi rang sozlamalari qurilmadan foydalanishni qiyinlashtirish mumkin. Tanlgan parametrlarni tasdiqlash uchun “OK” tugmasini bosing. Aks holda, ular 10 soniyadan so‘ng qayta tiklanadi."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Quvvat (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Quvvat tejash rejimidan quvvatlash vaqtida foydalanib bo‘lmaydi"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Quvvat tejash rejimi"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Unumdorlikni pasaytiradi va fonda internetdan foydalanishni cheklaydi"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index fa835ba..38283ad 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Áp dụng"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Xác nhận cài đặt"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Một số cài đặt màu có thể khiến thiết bị này không sử dụng được. Hãy nhấp vào OK để xác nhận các cài đặt màu này, nếu không những cài đặt này sẽ được đặt lại sau 10 giây."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Pin (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Trình tiết kiệm pin không khả dụng trong khi sạc"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Trình tiết kiệm pin"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Giảm hiệu suất và dữ liệu nền"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 1e2dba3..8534d82 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"应用"</string>
<string name="color_revert_title" msgid="4746666545480534663">"确认设置"</string>
<string name="color_revert_message" msgid="9116001069397996691">"部分颜色设置可能会导致此设备无法使用。请点击“确定”确认这些颜色设置,否则,系统将在 10 秒后重置这些设置。"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"电池 (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充电过程中无法使用节电助手"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"节电助手"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低性能并限制后台流量"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 0782354..0f0ac69 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"套用"</string>
<string name="color_revert_title" msgid="4746666545480534663">"確認設定"</string>
<string name="color_revert_message" msgid="9116001069397996691">"部分顏色設定會令此裝置無法使用。請按一下 [確定] 加以確認,否則這些顏色設定將於 10 秒後重設。"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"電池電量 (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電時無法使用「省電模式」"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"省電模式"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低效能並限制背景數據傳輸"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 29520f6..3139e41 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"套用"</string>
<string name="color_revert_title" msgid="4746666545480534663">"確認設定"</string>
<string name="color_revert_message" msgid="9116001069397996691">"部分顏色設定可能會造成這部裝置無法使用。請按一下 [確定] 來確認您要使用這類顏色設定,否則系統將在 10 秒後重設這些設定。"</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"電池 (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充電時無法使用節約耗電量模式"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"節約耗電量"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低效能並限制背景資料傳輸"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 3ee46ea..b93596a 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -472,4 +472,8 @@
<string name="color_apply" msgid="9212602012641034283">"Sebenzisa"</string>
<string name="color_revert_title" msgid="4746666545480534663">"Qinisekisa izilungiselelo"</string>
<string name="color_revert_message" msgid="9116001069397996691">"Ezinye izilungiselelo zombala zingenza le divayisi ingasebenziseki. Chofoza ku-KULUNGILE ukuze uqinisekise lezi zilungiselelo zombala, uma kungenjalo lezi zilungiselelo zizosethwa kabusha ngemuva kwamasekhondi angu-10."</string>
+ <string name="battery_panel_title" msgid="3476715163685592453">"Ibhethri (<xliff:g id="ID_1">%1$d</xliff:g>%%)"</string>
+ <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Isilondolozi sebhethri asitholakali ngesikhathi sokushaja"</string>
+ <string name="battery_detail_switch_title" msgid="6285872470260795421">"Isilondolozi sebhethri"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Sehlisa ukusebenza nedatha yasemuva"</string>
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 905da13..a80a5de 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -36,7 +36,6 @@
<color name="system_warning_color">#fff4511e</color><!-- deep orange 600 -->
<color name="qs_text">#FFFFFFFF</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
- <color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
<color name="qs_subhead">#99FFFFFF</color><!-- 60% white -->
<color name="qs_detail_empty">#24B0BEC5</color><!-- 14% blue grey 200 -->
<color name="qs_detail_button">#FFB0BEC5</color><!-- 100% blue grey 200 -->
@@ -48,6 +47,7 @@
<color name="data_usage_graph_warning">#FFFFFFFF</color>
<color name="status_bar_clock_color">#FFFFFFFF</color>
<color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all -->
+ <color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black -->
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
@@ -100,10 +100,9 @@
<color name="current_user_border_color">@color/system_accent_color</color>
<!-- The "inside" of a notification, reached via longpress -->
- <color name="notification_guts_bg_color">@*android:color/material_deep_teal_500</color>
- <color name="notification_guts_title_color">#B2DFDB</color>
- <color name="notification_guts_text_color">#FFFFFFFF</color>
- <color name="notification_guts_btn_color">#FFFFFFFF</color>
+ <color name="notification_guts_bg_color">@*android:color/material_grey_50</color>
+ <color name="notification_guts_slider_color">@*android:color/material_deep_teal_500</color>
+ <color name="notification_guts_secondary_slider_color">#858383</color>
<color name="assist_orb_color">#ffffff</color>
@@ -147,4 +146,8 @@
<color name="default_remote_input_background">@*android:color/notification_default_color</color>
<color name="remote_input_hint">#4dffffff</color>
+
+ <color name="qs_tile_tint_unavailable">#40ffffff</color>
+ <color name="qs_tile_tint_inactive">#4dffffff</color>
+ <color name="qs_tile_tint_active">#ffffffff</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 955af82..e98ec82 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -177,6 +177,9 @@
<!-- The animation duration for scrolling the stack to a particular item. -->
<integer name="recents_animate_task_stack_scroll_duration">200</integer>
+ <!-- The animation duration for scrolling the stack to a particular item. -->
+ <integer name="recents_auto_advance_duration">2000</integer>
+
<!-- The animation duration for entering and exiting the history. -->
<integer name="recents_history_transition_duration">250</integer>
@@ -279,5 +282,8 @@
<!-- Whether to show the full screen user switcher. -->
<bool name="config_enableFullscreenUserSwitcher">false</bool>
+ <!-- SystemUIFactory component -->
+ <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>
+
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 097c352..3fb5f18 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -167,6 +167,8 @@
<dimen name="segmented_button_spacing">0dp</dimen>
<dimen name="borderless_button_radius">2dp</dimen>
+ <dimen name="restricted_padlock_pading">4dp</dimen>
+
<!-- How far the expanded QS panel peeks from the header in collapsed state. -->
<dimen name="qs_peek_height">0dp</dimen>
@@ -298,7 +300,7 @@
<dimen name="swipe_helper_falsing_threshold">70dp</dimen>
<dimen name="notifications_top_padding">4dp</dimen>
-
+
<!-- Minimum distance the user has to drag down to go to the full shade. -->
<dimen name="keyguard_drag_down_min_distance">100dp</dimen>
@@ -509,7 +511,7 @@
<!-- The maximum width of the navigation bar ripples. -->
<dimen name="key_button_ripple_max_width">95dp</dimen>
-
+
<!-- Inset shadow for FakeShadowDrawable. It is used to avoid gaps between the card
and the shadow. -->
<dimen name="fake_shadow_inset">1dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4136c11..de49677 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3,16 +3,16 @@
/**
* Copyright (c) 2009, The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
@@ -1173,6 +1173,11 @@
<!-- Description for the toggle for fast-toggling recents via the recents button. DO NOT TRANSLATE -->
<string name="overview_fast_toggle_via_button_desc">Enable launch timeout while paging</string>
+ <!-- Toggles the fast-toggling indicator. DO NOT TRANSLATE -->
+ <string name="overview_fast_toggle_indicator">Enable fast toggle indicator</string>
+ <!-- Description for the fast-toggling indicator. DO NOT TRANSLATE -->
+ <string name="overview_fast_toggle_indicator_desc">Show an indicator for the launch timeout</string>
+
<!-- Toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE -->
<string name="overview_initial_state_paging">Initialize to paging</string>
<!-- Description for the toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE -->
@@ -1281,4 +1286,13 @@
<!-- Summary of switch for battery saver [CHAR LIMIT=NONE] -->
<string name="battery_detail_switch_summary">Reduces performance and background data</string>
+ <!-- User visible title for the system-wide keyboard shortcuts list. -->
+ <string name="keyboard_shortcut_group_system">System</string>
+ <!-- User visible title for the keyboard shortcut that takes the user to the home screen. -->
+ <string name="keyboard_shortcut_group_system_home">Home</string>
+ <!-- User visible title for the the keyboard shortcut that takes the user to the recents screen. -->
+ <string name="keyboard_shortcut_group_system_recents">Recents</string>
+ <!-- User visible title for the the keyboard shortcut that triggers the back action. -->
+ <string name="keyboard_shortcut_group_system_back">Back</string>
+
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 527b638..4329f78 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -319,10 +319,30 @@
<style name="TextAppearance.NotificationGuts">
<item name="android:textSize">14sp</item>
- <item name="android:textColor">@color/notification_guts_btn_color</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:textColor">@android:color/black</item>
+ </style>
+
+ <style name="TextAppearance.NotificationGuts.Header">
+ <item name="android:alpha">.38</item>
+ <item name="android:textSize">12sp</item>
+ </style>
+
+ <style name="TextAppearance.NotificationGuts.Secondary">
+ <item name="android:alpha">.54</item>
+ </style>
+
+ <style name="TextAppearance.NotificationGuts.Primary">
+ <item name="android:alpha">.87</item>
+ <item name="android:textSize">16sp</item>
+ </style>
+
+ <style name="TextAppearance.NotificationGuts.Button">
+ <item name="android:textSize">14sp</item>
<item name="android:textAllCaps">true</item>
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:gravity">center</item>
+ <item name="android:textColor">@*android:color/material_deep_teal_500</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 90cd394..11ef735d 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -84,6 +84,12 @@
android:title="@string/overview_fast_toggle_via_button"
android:summary="@string/overview_fast_toggle_via_button_desc" />
+ <com.android.systemui.tuner.TunerSwitch
+ android:key="overview_fast_toggle_indicator"
+ android:title="@string/overview_fast_toggle_indicator"
+ android:summary="@string/overview_fast_toggle_indicator_desc"
+ android:dependency="overview_fast_toggle_via_button" />
+
</PreferenceScreen>
<SwitchPreference
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 870e0af..fabc7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -86,6 +86,7 @@
final private int[] mTmpPos = new int[2];
private int mFalsingThreshold;
private boolean mTouchAboveFalsingThreshold;
+ private boolean mDisableHwLayers;
public SwipeHelper(int swipeDirection, Callback callback, Context context) {
mCallback = callback;
@@ -115,6 +116,10 @@
mPagingTouchSlop = pagingTouchSlop;
}
+ public void setDisableHardwareLayers(boolean disableHwLayers) {
+ mDisableHwLayers = disableHwLayers;
+ }
+
private float getPos(MotionEvent ev) {
return mSwipeDirection == X ? ev.getX() : ev.getY();
}
@@ -147,7 +152,7 @@
}
}
- private float getSize(View v) {
+ protected float getSize(View v) {
return mSwipeDirection == X ? v.getMeasuredWidth() :
v.getMeasuredHeight();
}
@@ -178,10 +183,12 @@
if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
if (FADE_OUT_DURING_SWIPE && dismissable) {
float alpha = swipeProgress;
- if (alpha != 0f && alpha != 1f) {
- animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- } else {
- animView.setLayerType(View.LAYER_TYPE_NONE, null);
+ if (!mDisableHwLayers) {
+ if (alpha != 0f && alpha != 1f) {
+ animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ } else {
+ animView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
}
animView.setAlpha(getSwipeProgressForOffset(animView));
}
@@ -345,7 +352,9 @@
duration = fixedDuration;
}
- animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ if (!mDisableHwLayers) {
+ animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ }
ObjectAnimator anim = createTranslationAnimation(animView, newPos);
if (useAccelerateInterpolator) {
anim.setInterpolator(mFastOutLinearInInterpolator);
@@ -362,7 +371,9 @@
if (endAction != null) {
endAction.run();
}
- animView.setLayerType(View.LAYER_TYPE_NONE, null);
+ if (!mDisableHwLayers) {
+ animView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
}
});
anim.addUpdateListener(new AnimatorUpdateListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 19e299254..2d056bf 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -80,6 +80,8 @@
// the theme set there.
setTheme(R.style.systemui_theme);
+ SystemUIFactory.createFromConfig(this);
+
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
new file mode 100644
index 0000000..681b39e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.ViewGroup;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+
+/**
+ * Class factory to provide customizable SystemUI components.
+ */
+public class SystemUIFactory {
+ private static final String TAG = "SystemUIFactory";
+
+ static SystemUIFactory mFactory;
+
+ public static SystemUIFactory getInstance() {
+ return mFactory;
+ }
+
+ public static void createFromConfig(Context context) {
+ final String clsName = context.getString(R.string.config_systemUIFactoryComponent);
+ if (clsName == null || clsName.length() == 0) {
+ throw new RuntimeException("No SystemUIFactory component configured");
+ }
+
+ try {
+ Class<?> cls = null;
+ cls = context.getClassLoader().loadClass(clsName);
+ mFactory = (SystemUIFactory) cls.newInstance();
+ } catch (Throwable t) {
+ Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);
+ throw new RuntimeException(t);
+ }
+ }
+
+ public SystemUIFactory() {}
+
+ public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
+ ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
+ return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
+ }
+
+ public KeyguardBouncer createKeyguardBouncer(Context context, ViewMediatorCallback callback,
+ LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
+ ViewGroup container) {
+ return new KeyguardBouncer(context, callback, lockPatternUtils, windowManager, container);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 443778e..9a00b4b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -69,6 +69,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.statusbar.phone.FingerprintUnlockController;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -474,10 +475,12 @@
ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
+ @Override
public void userActivity() {
KeyguardViewMediator.this.userActivity();
}
+ @Override
public void keyguardDone(boolean strongAuth) {
if (!mKeyguardDonePending) {
KeyguardViewMediator.this.keyguardDone(true /* authenticated */);
@@ -487,6 +490,7 @@
}
}
+ @Override
public void keyguardDoneDrawing() {
mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
}
@@ -588,8 +592,9 @@
updateInputRestrictedLocked();
mTrustManager.reportKeyguardShowingChanged();
- mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(mContext,
- mViewMediatorCallback, mLockPatternUtils);
+ mStatusBarKeyguardViewManager =
+ SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,
+ mViewMediatorCallback, mLockPatternUtils);
final ContentResolver cr = mContext.getContentResolver();
mDeviceInteractive = mPM.isInteractive();
@@ -741,8 +746,7 @@
long timeout;
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
- if ((!user.isManagedProfile() && LockPatternUtils.isSeparateWorkChallengeEnabled())
+ if ((mLockPatternUtils.isSeparateProfileChallengeEnabled(userId))
|| policyTimeout <= 0) {
timeout = lockAfterTimeout;
} else {
@@ -782,9 +786,9 @@
private void doKeyguardLaterLockedForChildProfiles() {
UserManager um = UserManager.get(mContext);
List<UserInfo> profiles = um.getEnabledProfiles(UserHandle.myUserId());
- if (LockPatternUtils.isSeparateWorkChallengeEnabled() && profiles.size() > 1) {
+ if (profiles.size() > 1) {
for (UserInfo info : profiles) {
- if (info.id != UserHandle.myUserId() && info.isManagedProfile()) {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(info.id)) {
long userTimeout = getLockTimeout(info.id);
long userWhen = SystemClock.elapsedRealtime() + userTimeout;
Intent lockIntent = new Intent(DELAYED_LOCK_PROFILE_ACTION);
@@ -1248,7 +1252,9 @@
if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
+ sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
synchronized (KeyguardViewMediator.this) {
- doKeyguardLocked(null);
+ if (mDelayedShowingSequence == sequence) {
+ doKeyguardLocked(null);
+ }
}
} else if (DELAYED_LOCK_PROFILE_ACTION.equals(intent.getAction())) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, 0);
@@ -1698,6 +1704,7 @@
mHandler.removeMessages(KEYGUARD_DONE_PENDING_TIMEOUT);
}
+ @Override
public void onBootCompleted() {
mUpdateMonitor.dispatchBootCompleted();
synchronized (this) {
@@ -1731,10 +1738,15 @@
public void onActivityDrawn() {
mHandler.sendEmptyMessage(ON_ACTIVITY_DRAWN);
}
+
public ViewMediatorCallback getViewMediatorCallback() {
return mViewMediatorCallback;
}
+ public LockPatternUtils getLockPatternUtils() {
+ return mLockPatternUtils;
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print(" mSystemReady: "); pw.println(mSystemReady);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
index 01eb5f9..7651ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
@@ -81,7 +81,11 @@
}
}
}
-
+ if (state.disabledByPolicy) {
+ iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color));
+ } else {
+ iv.clearColorFilter();
+ }
}
protected int getIconMeasureMode() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 91f88b9..fd07e50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -329,16 +329,19 @@
drawTile(r, state);
}
}
+
@Override
public void onShowDetail(boolean show) {
QSPanel.this.showDetail(show, r);
}
+
@Override
public void onToggleStateChanged(boolean state) {
if (mDetailRecord == r) {
fireToggleStateChanged(state);
}
}
+
@Override
public void onScanStateChanged(boolean state) {
r.scanState = state;
@@ -352,7 +355,7 @@
announceForAccessibility(announcement);
}
};
- r.tile.setCallback(callback);
+ r.tile.addCallback(callback);
final View.OnClickListener click = new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -415,6 +418,9 @@
}
protected void handleShowDetail(Record r, boolean show) {
+ if (show && !mExpanded) {
+ mHost.animateExpandQS();
+ }
if (r instanceof TileRecord) {
handleShowDetailTile((TileRecord) r, show);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 8ce6da2..de7c02d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -23,10 +23,13 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
+
+import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.qs.QSTile.State;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -43,9 +46,12 @@
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
/**
* Base quick-settings tile, extend this to create a new tile.
*
@@ -62,7 +68,7 @@
protected final H mHandler;
protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
- private Callback mCallback;
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
protected TState mState = newTileState();
private TState mTmpState = newTileState();
private boolean mAnnounceNextStateChange;
@@ -119,8 +125,8 @@
// safe to call from any thread
- public void setCallback(Callback callback) {
- mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
+ public void addCallback(Callback callback) {
+ mHandler.obtainMessage(H.ADD_CALLBACK, callback).sendToTarget();
}
public void click() {
@@ -177,8 +183,8 @@
// call only on tile worker looper
- private void handleSetCallback(Callback callback) {
- mCallback = callback;
+ private void handleAddCallback(Callback callback) {
+ mCallbacks.add(callback);
handleRefreshState(null);
}
@@ -206,12 +212,14 @@
private void handleStateChanged() {
boolean delayAnnouncement = shouldAnnouncementBeDelayed();
- if (mCallback != null) {
- mCallback.onStateChanged(mState);
+ if (mCallbacks.size() != 0) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onStateChanged(mState);
+ }
if (mAnnounceNextStateChange && !delayAnnouncement) {
String announcement = composeChangeAnnouncement();
if (announcement != null) {
- mCallback.onAnnouncementRequested(announcement);
+ mCallbacks.get(0).onAnnouncementRequested(announcement);
}
}
}
@@ -227,20 +235,20 @@
}
private void handleShowDetail(boolean show) {
- if (mCallback != null) {
- mCallback.onShowDetail(show);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onShowDetail(show);
}
}
private void handleToggleStateChanged(boolean state) {
- if (mCallback != null) {
- mCallback.onToggleStateChanged(state);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onToggleStateChanged(state);
}
}
private void handleScanStateChanged(boolean state) {
- if (mCallback != null) {
- mCallback.onScanStateChanged(state);
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onScanStateChanged(state);
}
}
@@ -250,11 +258,23 @@
protected void handleDestroy() {
setListening(false);
- mCallback = null;
+ mCallbacks.clear();
+ }
+
+ protected void checkIfRestrictionEnforced(State state, String userRestriction) {
+ EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+ userRestriction, UserHandle.myUserId());
+ if (admin != null) {
+ state.disabledByPolicy = true;
+ state.enforcedAdmin = admin;
+ } else {
+ state.disabledByPolicy = false;
+ state.enforcedAdmin = null;
+ }
}
protected final class H extends Handler {
- private static final int SET_CALLBACK = 1;
+ private static final int ADD_CALLBACK = 1;
private static final int CLICK = 2;
private static final int SECONDARY_CLICK = 3;
private static final int LONG_CLICK = 4;
@@ -274,13 +294,19 @@
public void handleMessage(Message msg) {
String name = null;
try {
- if (msg.what == SET_CALLBACK) {
- name = "handleSetCallback";
- handleSetCallback((QSTile.Callback)msg.obj);
+ if (msg.what == ADD_CALLBACK) {
+ name = "handleAddCallback";
+ handleAddCallback((QSTile.Callback)msg.obj);
} else if (msg.what == CLICK) {
name = "handleClick";
- mAnnounceNextStateChange = true;
- handleClick();
+ if (mState.disabledByPolicy) {
+ Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
+ mContext, mState.enforcedAdmin);
+ mHost.startActivityDismissingKeyguard(intent);
+ } else {
+ mAnnounceNextStateChange = true;
+ handleClick();
+ }
} else if (msg.what == SECONDARY_CLICK) {
name = "handleSecondaryClick";
handleSecondaryClick();
@@ -333,6 +359,7 @@
void startRunnableDismissingKeyguard(Runnable runnable);
void warn(String message, Throwable t);
void collapsePanels();
+ void animateExpandQS();
void openPanels();
Looper getLooper();
Context getContext();
@@ -433,6 +460,8 @@
public CharSequence contentDescription;
public CharSequence dualLabelContentDescription;
public boolean autoMirrorDrawable = true;
+ public boolean disabledByPolicy;
+ public EnforcedAdmin enforcedAdmin;
public boolean copyTo(State other) {
if (other == null) throw new IllegalArgumentException();
@@ -442,12 +471,16 @@
|| !Objects.equals(other.contentDescription, contentDescription)
|| !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable)
|| !Objects.equals(other.dualLabelContentDescription,
- dualLabelContentDescription);
+ dualLabelContentDescription)
+ || !Objects.equals(other.disabledByPolicy, disabledByPolicy)
+ || !Objects.equals(other.enforcedAdmin, enforcedAdmin);
other.icon = icon;
other.label = label;
other.contentDescription = contentDescription;
other.dualLabelContentDescription = dualLabelContentDescription;
other.autoMirrorDrawable = autoMirrorDrawable;
+ other.disabledByPolicy = disabledByPolicy;
+ enforcedAdmin.copyTo(other.enforcedAdmin);
return changed;
}
@@ -463,6 +496,8 @@
sb.append(",contentDescription=").append(contentDescription);
sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
sb.append(",autoMirrorDrawable=").append(autoMirrorDrawable);
+ sb.append(",disabledByPolicy=").append(disabledByPolicy);
+ sb.append(",enforcedAdmin=").append(enforcedAdmin);
return sb.append(']');
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 41ac4d9..664ca39 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -19,32 +19,33 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Typeface;
import android.util.MathUtils;
-import android.util.TypedValue;
import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.View;
+import android.widget.ImageView;
import android.widget.TextView;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
- private static final Typeface CONDENSED = Typeface.create("sans-serif-condensed",
- Typeface.NORMAL);
-
protected final Context mContext;
+ private QSIconView mIconView;
private final int mTileSpacingPx;
private int mTilePaddingTopPx;
private TextView mLabel;
+ private ImageView mPadLock;
public QSTileView(Context context, QSIconView icon) {
super(context, icon);
mContext = context;
+ mIconView = icon;
final Resources res = context.getResources();
mTileSpacingPx = res.getDimensionPixelSize(R.dimen.qs_tile_spacing);
+
setClipChildren(false);
setClickable(true);
@@ -76,16 +77,10 @@
private void createLabel() {
final Resources res = mContext.getResources();
- mLabel = new TextView(mContext);
- mLabel.setTextColor(mContext.getColor(R.color.qs_tile_text));
- mLabel.setGravity(Gravity.CENTER_HORIZONTAL);
- mLabel.setMinLines(2);
- mLabel.setPadding(0, 0, 0, 0);
- mLabel.setTypeface(CONDENSED);
- mLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- res.getDimensionPixelSize(R.dimen.qs_tile_text_size));
- mLabel.setClickable(false);
- addView(mLabel);
+ View view = LayoutInflater.from(mContext).inflate(R.layout.qs_tile_label, null);
+ mLabel = (TextView) view.findViewById(R.id.tile_label);
+ mPadLock = (ImageView) view.findViewById(R.id.restricted_padlock);
+ addView(view);
}
public void init(OnClickListener clickPrimary, OnLongClickListener longClick) {
@@ -96,5 +91,7 @@
protected void handleStateChanged(QSTile.State state) {
super.handleStateChanged(state);
mLabel.setText(state.label);
+ mLabel.setEnabled(!state.disabledByPolicy);
+ mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 5fa38c6..5782800 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -60,14 +60,8 @@
}
@Override
- protected void handleShowDetail(QSPanel.Record r, boolean show) {
- if (show) {
- mHeader.performClick();
- mFullPanel.showDetail(show, r);
- } else {
- // Not sure how we would end up here...
- super.handleShowDetail(r, show);
- }
+ protected void showDetail(boolean show, Record r) {
+ // Do nothing, will be handled by the QSPanel.
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
index 95ff611..ac4f05f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java
@@ -19,8 +19,9 @@
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
-
+import android.graphics.drawable.Drawable;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
public class BlankCustomTile extends QSTile<QSTile.State> {
@@ -72,7 +73,9 @@
try {
PackageManager pm = mContext.getPackageManager();
ServiceInfo info = pm.getServiceInfo(mComponent, 0);
- state.icon = new DrawableIcon(info.loadIcon(pm));
+ Drawable drawable = info.loadIcon(pm);
+ drawable.setTint(mContext.getColor(R.color.qs_tile_tint_active));
+ state.icon = new DrawableIcon(drawable);
state.label = info.loadLabel(pm).toString();
state.contentDescription = state.label;
} catch (Exception e) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
index 7448493..eab4dca 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -69,6 +69,11 @@
}
@Override
+ protected void showDetail(boolean show, Record r) {
+ // No detail here.
+ }
+
+ @Override
protected void onDetachedFromWindow() {
// Don't allow the super to unregister the tunable.
}
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 eefff30..d398b64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -25,11 +25,14 @@
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.phone.QSTileHost;
@@ -88,6 +91,7 @@
mTile.setIcon(tile.getIcon());
mTile.setLabel(tile.getLabel());
mTile.setContentDescription(tile.getContentDescription());
+ mTile.setState(tile.getState());
}
public void onDialogShown() {
@@ -147,6 +151,9 @@
@Override
protected void handleClick() {
+ if (mTile.getState() == Tile.STATE_UNAVAILABLE) {
+ return;
+ }
try {
if (DEBUG) Log.d(TAG, "Adding token");
mWindowManager.addWindowToken(mToken, WindowManager.LayoutParams.TYPE_QS_DIALOG);
@@ -172,9 +179,15 @@
@Override
protected void handleUpdateState(State state, Object arg) {
Drawable drawable = mTile.getIcon().loadDrawable(mContext);
- drawable.setTint(mContext.getColor(android.R.color.white));
+ int color = mContext.getColor(getColor(mTile.getState()));
+ drawable.setTint(color);
state.icon = new DrawableIcon(drawable);
state.label = mTile.getLabel();
+ if (mTile.getState() == Tile.STATE_UNAVAILABLE) {
+ state.label = new SpannableStringBuilder().append(state.label,
+ new ForegroundColorSpan(color),
+ SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
+ }
if (mTile.getContentDescription() != null) {
state.contentDescription = mTile.getContentDescription();
} else {
@@ -187,6 +200,30 @@
return MetricsLogger.QS_CUSTOM;
}
+ public void startUnlockAndRun() {
+ mHost.startRunnableDismissingKeyguard(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mService.onUnlockComplete();
+ } catch (RemoteException e) {
+ }
+ }
+ });
+ }
+
+ private static int getColor(int state) {
+ switch (state) {
+ case Tile.STATE_UNAVAILABLE:
+ return R.color.qs_tile_tint_unavailable;
+ case Tile.STATE_INACTIVE:
+ return R.color.qs_tile_tint_inactive;
+ case Tile.STATE_ACTIVE:
+ return R.color.qs_tile_tint_active;
+ }
+ return 0;
+ }
+
public static ComponentName getComponentFromSpec(String spec) {
final String action = spec.substring(PREFIX.length(), spec.length() - 1);
if (action.isEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
index d41cdde..3830ac5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
@@ -104,4 +104,14 @@
return false;
}
}
+
+ public boolean onUnlockComplete() {
+ try {
+ mService.onUnlockComplete();
+ return true;
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from TileService", e);
+ return false;
+ }
+ }
}
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 8c5e87e..4977d80 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -55,6 +55,7 @@
private static final int MSG_ON_ADDED = 0;
private static final int MSG_ON_REMOVED = 1;
private static final int MSG_ON_CLICK = 2;
+ private static final int MSG_ON_UNLOCK_COMPLETE = 3;
// Bind retry control.
private static final int MAX_BIND_RETRIES = 5;
@@ -174,6 +175,15 @@
onClick(mClickBinder);
}
}
+ if (queue.contains(MSG_ON_UNLOCK_COMPLETE)) {
+ if (DEBUG) Log.d(TAG, "Handling pending onUnlockComplete");
+ if (!mListening) {
+ Log.w(TAG, "Managed to get unlock on non-listening state...");
+ // Skipping unlock since lost click privileges.
+ } else {
+ onUnlockComplete();
+ }
+ }
if (queue.contains(MSG_ON_REMOVED)) {
if (DEBUG) Log.d(TAG, "Handling pending onRemoved");
if (mListening) {
@@ -348,6 +358,15 @@
}
@Override
+ public void onUnlockComplete() {
+ if (DEBUG) Log.d(TAG, "onUnlockComplete");
+ if (mWrapper == null || !mWrapper.onUnlockComplete()) {
+ queueMessage(MSG_ON_UNLOCK_COMPLETE);
+ handleDeath();
+ }
+ }
+
+ @Override
public IBinder asBinder() {
return mWrapper != null ? mWrapper.asBinder() : null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index a831c87..44d8776 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -36,6 +36,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
import java.util.ArrayList;
import java.util.Collections;
@@ -199,6 +200,16 @@
}
@Override
+ public void onStartActivity(Tile tile) {
+ ComponentName componentName = tile.getComponentName();
+ verifyCaller(componentName.getPackageName());
+ CustomTile customTile = getTileForComponent(componentName);
+ if (customTile != null) {
+ mHost.collapsePanels();
+ }
+ }
+
+ @Override
public void updateStatusIcon(Tile tile, Icon icon, String contentDescription) {
final ComponentName componentName = tile.getComponentName();
String packageName = componentName.getPackageName();
@@ -228,6 +239,28 @@
}
}
+ @Override
+ public void startUnlockAndRun(Tile tile) {
+ ComponentName componentName = tile.getComponentName();
+ verifyCaller(componentName.getPackageName());
+ CustomTile customTile = getTileForComponent(componentName);
+ if (customTile != null) {
+ customTile.startUnlockAndRun();
+ }
+ }
+
+ @Override
+ public boolean isLocked() {
+ KeyguardMonitor keyguardMonitor = mHost.getKeyguardMonitor();
+ return keyguardMonitor.isShowing();
+ }
+
+ @Override
+ public boolean isSecure() {
+ KeyguardMonitor keyguardMonitor = mHost.getKeyguardMonitor();
+ return keyguardMonitor.isSecure() && keyguardMonitor.isShowing();
+ }
+
private CustomTile getTileForComponent(ComponentName component) {
synchronized (mServices) {
return mTiles.get(component);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 4d9b266..39eda6b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -22,6 +22,7 @@
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.view.LayoutInflater;
@@ -99,14 +100,6 @@
@Override
public void handleClick() {
- if (mController.isVolumeRestricted()) {
- // Collapse the panels, so the user can see the toast.
- mHost.collapsePanels();
- SysUIToast.makeText(mContext, mContext.getString(
- com.android.internal.R.string.error_message_change_not_allowed),
- Toast.LENGTH_LONG).show();
- return;
- }
MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
if (mState.value) {
mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
@@ -123,6 +116,8 @@
final boolean newValue = zen != Global.ZEN_MODE_OFF;
final boolean valueChanged = state.value != newValue;
state.value = newValue;
+ state.disabledByPolicy = mController.isVolumeRestricted();
+ checkIfRestrictionEnforced(state, UserManager.DISALLOW_ADJUST_VOLUME);
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 21c5c96..167c611 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -43,6 +43,7 @@
private TextView mName;
private Typeface mRegularTypeface;
private Typeface mActivatedTypeface;
+ private View mRestrictedPadlock;
public UserDetailItemView(Context context) {
this(context, null);
@@ -59,6 +60,7 @@
public UserDetailItemView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.UserDetailItemView, defStyleAttr, defStyleRes);
final int N = a.getIndexCount();
@@ -95,6 +97,12 @@
mAvatar.setDrawable(picture);
}
+ public void setDisabledByAdmin(boolean disabled) {
+ mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE);
+ mName.setEnabled(!disabled);
+ mAvatar.setDisabled(disabled);
+ }
+
@Override
protected void onFinishInflate() {
mAvatar = (UserAvatarView) findViewById(R.id.user_picture);
@@ -106,6 +114,7 @@
mActivatedTypeface = mName.getTypeface();
}
updateTypeface();
+ mRestrictedPadlock = findViewById(R.id.restricted_padlock);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index d4f54b6..b44ef0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -16,17 +16,18 @@
package com.android.systemui.qs.tiles;
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.R;
-import com.android.systemui.qs.PseudoGridView;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-
import android.content.Context;
+import android.content.Intent;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.systemui.R;
+import com.android.systemui.qs.PseudoGridView;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
/**
* Quick settings detail view for user switching.
*/
@@ -55,11 +56,13 @@
public static class Adapter extends UserSwitcherController.BaseUserAdapter
implements OnClickListener {
- private Context mContext;
+ private final Context mContext;
+ private final UserSwitcherController mController;
public Adapter(Context context, UserSwitcherController controller) {
super(controller);
mContext = context;
+ mController = controller;
}
@Override
@@ -77,6 +80,7 @@
v.bind(name, item.picture);
}
v.setActivated(item.isCurrent);
+ v.setDisabledByAdmin(item.isDisabledByAdmin);
v.setTag(item);
return v;
}
@@ -85,8 +89,14 @@
public void onClick(View view) {
UserSwitcherController.UserRecord tag =
(UserSwitcherController.UserRecord) view.getTag();
- MetricsLogger.action(mContext, MetricsLogger.QS_SWITCH_USER);
- switchTo(tag);
+ if (tag.isDisabledByAdmin) {
+ final Intent intent = RestrictedLockUtils.getShowAdminSupportDetailsIntent(
+ mContext, tag.enforcedAdmin);
+ mController.startActivity(intent);
+ } else {
+ MetricsLogger.action(mContext, MetricsLogger.QS_SWITCH_USER);
+ switchTo(tag);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index e4d8067..db55f28 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -113,16 +113,12 @@
private FinishRecentsRunnable mFinishLaunchHomeRunnable;
// The trigger to automatically launch the current task
- private DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
- @Override
- public void run() {
- dismissRecentsToFocusedTask();
- }
- });
+ private int mFocusTimerDuration;
+ private DozeTrigger mIterateTrigger;
/**
* A common Runnable to finish Recents by launching Home with an animation depending on the
- * last activity launch state. Generally we always launch home when we exit Recents rather than
+ * 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 {
@@ -196,13 +192,13 @@
loader.loadTasks(this, plan, loadOpts);
TaskStack stack = plan.getTaskStack();
+ ArrayList<Task> tasks = stack.getStackTasks();
+ int taskCount = stack.getTaskCount();
mRecentsView.setTaskStack(stack);
// Mark the task that is the launch target
int launchTaskIndexInStack = 0;
if (launchState.launchedToTaskId != -1) {
- ArrayList<Task> tasks = stack.getStackTasks();
- int taskCount = tasks.size();
for (int j = 0; j < taskCount; j++) {
Task t = tasks.get(j);
if (t.key.id == launchState.launchedToTaskId) {
@@ -214,12 +210,12 @@
}
// Animate the SystemUI scrims into view
- boolean hasStatusBarScrim = stack.getStackTaskCount() > 0;
+ boolean hasStatusBarScrim = taskCount > 0;
boolean animateStatusBarScrim = launchState.launchedFromHome;
- boolean hasNavBarScrim = (stack.getStackTaskCount() > 0) && !config.hasTransposedNavBar;
+ boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
boolean animateNavBarScrim = true;
- mScrimViews.prepareEnterRecentsAnimation(hasStatusBarScrim, animateStatusBarScrim, hasNavBarScrim,
- animateNavBarScrim);
+ mScrimViews.prepareEnterRecentsAnimation(hasStatusBarScrim, animateStatusBarScrim,
+ hasNavBarScrim, animateNavBarScrim);
// Keep track of whether we launched from the nav bar button or via alt-tab
if (launchState.launchedWithAltTab) {
@@ -236,7 +232,6 @@
MetricsLogger.count(this, "overview_source_home", 1);
}
// Keep track of the total stack task count
- int taskCount = stack.getStackTaskCount();
MetricsLogger.histogram(this, "overview_task_count", taskCount);
}
@@ -357,6 +352,14 @@
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
+ mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
+ mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
+ @Override
+ public void run() {
+ dismissRecentsToFocusedTask();
+ }
+ });
+
// Create the home intent runnable
Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
homeIntent.addCategory(Intent.CATEGORY_HOME);
@@ -543,7 +546,8 @@
if (backward) {
EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
} else {
- EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ EventBus.getDefault().send(
+ new FocusNextTaskViewEvent(false /* showTimerIndicator */));
}
mLastTabKeyEventTime = SystemClock.elapsedRealtime();
@@ -555,7 +559,8 @@
return true;
}
case KeyEvent.KEYCODE_DPAD_UP: {
- EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ EventBus.getDefault().send(
+ new FocusNextTaskViewEvent(false /* showTimerIndicator */));
return true;
}
case KeyEvent.KEYCODE_DPAD_DOWN: {
@@ -564,12 +569,14 @@
}
case KeyEvent.KEYCODE_DEL:
case KeyEvent.KEYCODE_FORWARD_DEL: {
- EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
+ if (event.getRepeatCount() <= 0) {
+ EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
- // Keep track of deletions by keyboard
- MetricsLogger.histogram(this, "overview_task_dismissed_source",
- Constants.Metrics.DismissSourceKeyboard);
- return true;
+ // Keep track of deletions by keyboard
+ MetricsLogger.histogram(this, "overview_task_dismissed_source",
+ Constants.Metrics.DismissSourceKeyboard);
+ return true;
+ }
}
default:
break;
@@ -579,7 +586,10 @@
@Override
public void onUserInteraction() {
- EventBus.getDefault().send(new UserInteractionEvent());
+ // TODO: Prevent creating so many events here
+ final RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ EventBus.getDefault().send(new UserInteractionEvent(debugFlags.isFastToggleRecentsEnabled()
+ && debugFlags.isFastToggleIndicatorEnabled()));
}
@Override
@@ -603,11 +613,14 @@
public final void onBusEvent(IterateRecentsEvent event) {
if (!dismissHistory()) {
+ final RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+
// Focus the next task
- EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ EventBus.getDefault().send(
+ new FocusNextTaskViewEvent(debugFlags.isFastToggleRecentsEnabled()
+ && debugFlags.isFastToggleIndicatorEnabled()));
// Start dozing after the recents button is clicked
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
if (debugFlags.isFastToggleRecentsEnabled()) {
if (!mIterateTrigger.isDozing()) {
mIterateTrigger.startDozing();
@@ -615,6 +628,8 @@
mIterateTrigger.poke();
}
}
+
+ MetricsLogger.action(this, MetricsLogger.ACTION_OVERVIEW_PAGE);
}
}
@@ -701,7 +716,7 @@
intent.setComponent(intent.resolveActivity(getPackageManager()));
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(intent).startActivities(null,
- new UserHandle(event.task.key.userId));
+ new UserHandle(event.task.key.userId));
// Keep track of app-info invocations
MetricsLogger.count(this, "overview_app_info", 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 67135d5..61780f8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -139,7 +139,7 @@
} else {
// In portrait, the search bar appears on the top (which already has the inset)
int top = searchBarBounds.isEmpty() ? topInset : 0;
- taskStackBounds.set(windowBounds.left, searchBarBounds.bottom + top,
+ taskStackBounds.set(windowBounds.left, windowBounds.top + searchBarBounds.bottom + top,
windowBounds.right - rightInset, windowBounds.bottom);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index c323522..3151fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -27,6 +27,7 @@
public class RecentsDebugFlags implements TunerService.Tunable {
private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button";
+ private static final String KEY_FAST_TOGGLE_INDICATOR = "overview_fast_toggle_indicator";
private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging";
public static class Static {
@@ -49,6 +50,7 @@
}
private boolean mFastToggleRecents;
+ private boolean mFastToggleIndicator;
private boolean mInitialStatePaging;
/**
@@ -58,7 +60,8 @@
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_FAST_TOGGLE, KEY_INITIAL_STATE_PAGING);
+ TunerService.get(context).addTunable(this, KEY_FAST_TOGGLE, KEY_FAST_TOGGLE_INDICATOR,
+ KEY_INITIAL_STATE_PAGING);
}
/**
@@ -69,6 +72,13 @@
}
/**
+ * @return whether we are enabling the fast toggle indicator.
+ */
+ public boolean isFastToggleIndicatorEnabled() {
+ return mFastToggleIndicator;
+ }
+
+ /**
* @return whether the initial stack state is paging.
*/
public boolean isInitialStatePaging() {
@@ -82,6 +92,10 @@
mFastToggleRecents = (newValue != null) &&
(Integer.parseInt(newValue) != 0);
break;
+ case KEY_FAST_TOGGLE_INDICATOR:
+ mFastToggleIndicator = (newValue != null) &&
+ (Integer.parseInt(newValue) != 0);
+ break;
case KEY_INITIAL_STATE_PAGING:
mInitialStatePaging = (newValue != null) &&
(Integer.parseInt(newValue) != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index ddeb8dc..7c25d24 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -110,9 +110,7 @@
/** Preloads the next task */
public void run() {
- // TODO: Temporarily skip this if multi stack is enabled
- /*
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ RecentsConfiguration config = Recents.getConfiguration();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
RecentsTaskLoader loader = Recents.getTaskLoader();
SystemServicesProxy ssp = Recents.getSystemServices();
@@ -134,7 +132,6 @@
launchOpts.onlyLoadPausedActivities = true;
loader.loadTasks(mContext, plan, launchOpts);
}
- */
}
}
@@ -375,7 +372,7 @@
sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
TaskStack stack = sInstanceLoadPlan.getTaskStack();
- if (stack.getStackTaskCount() > 0) {
+ if (stack.getTaskCount() > 0) {
// We try and draw the thumbnail transition bitmap in parallel before
// toggle/show recents is called
preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView);
@@ -406,7 +403,7 @@
TaskStack focusedStack = plan.getTaskStack();
// Return early if there are no tasks in the focused stack
- if (focusedStack == null || focusedStack.getStackTaskCount() == 0) return;
+ if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
// Return early if there is no running task
@@ -458,7 +455,7 @@
TaskStack focusedStack = plan.getTaskStack();
// Return early if there are no tasks in the focused stack
- if (focusedStack == null || focusedStack.getStackTaskCount() == 0) return;
+ if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
ActivityManager.RunningTaskInfo runningTask = ssp.getTopMostTask();
// Return early if there is no running task (can't determine affiliated tasks in this case)
@@ -848,7 +845,7 @@
return;
}
- boolean hasRecentTasks = stack.getStackTaskCount() > 0;
+ boolean hasRecentTasks = stack.getTaskCount() > 0;
boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
if (useThumbnailTransition) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index b0c8ff3..212c7f4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -290,6 +290,28 @@
}
/**
+ * An event that can be reusable, only used for situations where we want to reduce memory
+ * allocations when events are sent frequently (ie. on scroll).
+ */
+ public static class ReusableEvent extends Event {
+
+ private int mDispatchCount;
+
+ protected ReusableEvent() {}
+
+ @Override
+ void onPostDispatch() {
+ super.onPostDispatch();
+ mDispatchCount++;
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ throw new CloneNotSupportedException();
+ }
+ }
+
+ /**
* An inter-process event super class that allows us to track user state across subscriber
* invocations.
*/
@@ -770,8 +792,11 @@
event.onPreDispatch();
// We need to clone the list in case a subscriber unregisters itself during traversal
+ // TODO: Investigate whether we can skip the object creation here
eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
- for (final EventHandler eventHandler : eventHandlers) {
+ int eventHandlerCount = eventHandlers.size();
+ for (int i = 0; i < eventHandlerCount; i++) {
+ final EventHandler eventHandler = eventHandlers.get(i);
if (eventHandler.subscriber.getReference() != null) {
if (event.requiresPost) {
mHandler.post(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
index cb5011a..ad9feb6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/StackViewScrolledEvent.java
@@ -16,16 +16,21 @@
package com.android.systemui.recents.events.ui;
+import android.util.MutableInt;
import com.android.systemui.recents.events.EventBus;
/**
* This is sent whenever a new scroll gesture happens on a stack view.
*/
-public class StackViewScrolledEvent extends EventBus.Event {
+public class StackViewScrolledEvent extends EventBus.ReusableEvent {
- public final int yMovement;
+ public final MutableInt yMovement;
- public StackViewScrolledEvent(int yMovement) {
- this.yMovement = yMovement;
+ public StackViewScrolledEvent() {
+ yMovement = new MutableInt(0);
+ }
+
+ public void updateY(int y) {
+ yMovement.value = y;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
index 6e6cd84..5a132c2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/UserInteractionEvent.java
@@ -22,5 +22,10 @@
* This is sent whenever the user interacts with the activity.
*/
public class UserInteractionEvent extends EventBus.Event {
- // Simple event
+
+ public final boolean showTimerIndicator;
+
+ public UserInteractionEvent(boolean showTimerIndicator) {
+ this.showTimerIndicator = showTimerIndicator;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
index 21321f2..b85ddac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDropTargetChangedEvent.java
@@ -25,6 +25,7 @@
*/
public class DragDropTargetChangedEvent extends EventBus.Event {
+ // The task that is currently being dragged
public final Task task;
public final DropTarget dropTarget;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
index 171ab5e..def4ae1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/focus/FocusNextTaskViewEvent.java
@@ -22,5 +22,10 @@
* Focuses the next task view in the stack.
*/
public class FocusNextTaskViewEvent extends EventBus.Event {
- // Simple event
+
+ public final boolean showTimerIndicator;
+
+ public FocusNextTaskViewEvent(boolean showTimerIndicator) {
+ this.showTimerIndicator = showTimerIndicator;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
index f0fa1da..80597bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -26,6 +26,7 @@
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
@@ -129,6 +130,9 @@
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.startActivityFromRecents(v.getContext(), task.key.id, task.title,
ActivityOptions.makeBasic());
+
+ MetricsLogger.action(v.getContext(), MetricsLogger.ACTION_OVERVIEW_SELECT,
+ task.key.getComponent().toString());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
index a2f5159..39bb6ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
@@ -28,6 +28,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
@@ -99,6 +100,8 @@
});
mAdapter.updateTasks(getContext(), stack);
mIsVisible = true;
+
+ MetricsLogger.visible(mRecyclerView.getContext(), MetricsLogger.OVERVIEW_HISTORY);
}
/**
@@ -129,6 +132,8 @@
setVisibility(View.INVISIBLE);
}
mIsVisible = false;
+
+ MetricsLogger.hidden(mRecyclerView.getContext(), MetricsLogger.OVERVIEW_HISTORY);
}
/**
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 3406da9..01de60c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -60,6 +60,7 @@
import android.view.Display;
import android.view.IDockedStackListener;
import android.view.WindowManager;
+import android.view.WindowManager.KeyboardShortcutsReceiver;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.app.AssistUtils;
@@ -306,11 +307,12 @@
}
/** Docks a task to the side of the screen and starts it. */
- public void startTaskInDockedMode(int taskId, int createMode) {
+ public void startTaskInDockedMode(Context context, int taskId, int createMode) {
if (mIam == null) return;
try {
- final ActivityOptions options = ActivityOptions.makeBasic();
+ // TODO: Determine what animation we want for the incoming task
+ final ActivityOptions options = ActivityOptions.makeCustomAnimation(context, 0, 0);
options.setDockCreateMode(createMode);
options.setLaunchStackId(DOCKED_STACK_ID);
mIam.startActivityFromRecents(taskId, options.toBundle());
@@ -365,7 +367,7 @@
}
/**
- * @return whether there are any docked tasks.
+ * @return whether there are any docked tasks for the current user.
*/
public boolean hasDockedTask() {
if (mIam == null) return false;
@@ -373,6 +375,9 @@
ActivityManager.StackInfo stackInfo = null;
try {
stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
+ if (stackInfo != null && stackInfo.userId != getCurrentUser()) {
+ return false;
+ }
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -915,4 +920,20 @@
e.printStackTrace();
}
}
+
+ /**
+ * Calculates the size of the dock divider in the current orientation.
+ */
+ public int getDockedDividerSize(Context context) {
+ Resources res = context.getResources();
+ int dividerWindowWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ int dividerInsets = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
+ return dividerWindowWidth - 2 * dividerInsets;
+ }
+
+ public void requestKeyboardShortcuts(Context context, KeyboardShortcutsReceiver receiver) {
+ mWm.requestAppKeyboardShortcuts(receiver);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 2bf2ccb..33f116b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -18,13 +18,43 @@
import android.animation.Animator;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.IntProperty;
+import android.util.Property;
import android.view.View;
import android.view.ViewParent;
/* Common code */
public class Utilities {
+ public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
+ new IntProperty<Drawable>("drawableAlpha") {
+ @Override
+ public void setValue(Drawable object, int alpha) {
+ object.setAlpha(alpha);
+ }
+
+ @Override
+ public Integer get(Drawable object) {
+ return object.getAlpha();
+ }
+ };
+
+ public static final Property<Drawable, Rect> DRAWABLE_RECT =
+ new Property<Drawable, Rect>(Rect.class, "drawableBounds") {
+ @Override
+ public void set(Drawable object, Rect bounds) {
+ object.setBounds(bounds);
+ }
+
+ @Override
+ public Rect get(Drawable object) {
+ return object.getBounds();
+ }
+ };
+
/**
* @return the first parent walking up the view hierarchy that has the given class type.
*
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 d8dfce5..822ad77 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -25,7 +25,9 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
+
import com.android.systemui.Prefs;
+import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -120,6 +122,8 @@
preloadRawTasks(isTopTaskHome);
}
+ String dismissDescFormat = mContext.getString(
+ R.string.accessibility_recents_item_will_be_dismissed);
long lastStackActiveTime = Prefs.getLong(mContext,
Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, 0);
long newLastStackActiveTime = -1;
@@ -143,6 +147,7 @@
// Load the title, icon, and color
String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
String contentDescription = loader.getAndUpdateContentDescription(taskKey, title, res);
+ String dismissDescription = String.format(dismissDescFormat, contentDescription);
Drawable icon = isStackTask
? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
: null;
@@ -151,8 +156,8 @@
// Add the task to the stack
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
- thumbnail, title, contentDescription, activityColor, !isStackTask,
- t.bounds, t.taskDescription);
+ thumbnail, title, contentDescription, dismissDescription, activityColor,
+ !isStackTask, t.bounds, t.taskDescription);
allTasks.add(task);
}
@@ -219,7 +224,7 @@
/** Returns whether there are any tasks in any stacks. */
public boolean hasTasks() {
if (mStack != null) {
- return mStack.getStackTaskCount() > 0;
+ return mStack.getTaskCount() > 0;
}
return false;
}
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 f7e2b9d..29e7077 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -54,6 +54,8 @@
public long firstActiveTime;
public long lastActiveTime;
+ private int mHashCode;
+
public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime,
long lastActiveTime) {
this.id = id;
@@ -62,6 +64,12 @@
this.userId = userId;
this.firstActiveTime = firstActiveTime;
this.lastActiveTime = lastActiveTime;
+ updateHashCode();
+ }
+
+ public void setStackId(int stackId) {
+ this.stackId = stackId;
+ updateHashCode();
}
public ComponentName getComponent() {
@@ -79,7 +87,7 @@
@Override
public int hashCode() {
- return Objects.hash(id, stackId, userId);
+ return mHashCode;
}
@Override
@@ -90,6 +98,10 @@
+ "lat: " + lastActiveTime + ", "
+ getComponent().getPackageName();
}
+
+ private void updateHashCode() {
+ mHashCode = Objects.hash(id, stackId, userId);
+ }
}
public TaskKey key;
@@ -113,6 +125,7 @@
public Bitmap thumbnail;
public String title;
public String contentDescription;
+ public String dismissDescription;
public int colorPrimary;
public boolean useLightOnPrimaryColor;
@@ -139,9 +152,9 @@
}
public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
- Bitmap thumbnail, String title, String contentDescription, int colorPrimary,
- boolean isHistorical, Rect bounds,
- ActivityManager.TaskDescription taskDescription) {
+ Bitmap thumbnail, String title, String contentDescription,
+ String dismissDescription, int colorPrimary, boolean isHistorical,
+ Rect bounds, ActivityManager.TaskDescription taskDescription) {
boolean isInAffiliationGroup = (affiliationTaskId != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
this.key = key;
@@ -151,6 +164,7 @@
this.thumbnail = thumbnail;
this.title = title;
this.contentDescription = contentDescription;
+ this.dismissDescription = dismissDescription;
this.colorPrimary = hasAffiliationGroupColor ? affiliationColor : colorPrimary;
this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary,
Color.WHITE) > 3f;
@@ -169,6 +183,7 @@
this.thumbnail = o.thumbnail;
this.title = o.title;
this.contentDescription = o.contentDescription;
+ this.dismissDescription = o.dismissDescription;
this.colorPrimary = o.colorPrimary;
this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
this.bounds = o.bounds;
@@ -201,7 +216,7 @@
* Updates the stack id of this task.
*/
public void setStackId(int stackId) {
- key.stackId = stackId;
+ key.setStackId(stackId);
int callbackCount = mCallbacks.size();
for (int i = 0; i < callbackCount; i++) {
mCallbacks.get(i).onTaskStackIdChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
index 288f07c..15f6b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
@@ -1,7 +1,8 @@
package com.android.systemui.recents.model;
+import android.util.ArrayMap;
+
import java.util.ArrayList;
-import java.util.HashMap;
/** Represents a grouping of tasks witihin a stack. */
public class TaskGrouping {
@@ -11,7 +12,7 @@
Task.TaskKey mFrontMostTaskKey;
ArrayList<Task.TaskKey> mTaskKeys = new ArrayList<Task.TaskKey>();
- HashMap<Task.TaskKey, Integer> mTaskKeyIndices = new HashMap<Task.TaskKey, Integer>();
+ ArrayMap<Task.TaskKey, Integer> mTaskKeyIndices = new ArrayMap<>();
/** Creates a group with a specified affiliation. */
public TaskGrouping(int affiliation) {
@@ -94,9 +95,10 @@
return;
}
+ int taskCount = mTaskKeys.size();
mFrontMostTaskKey = mTaskKeys.get(mTaskKeys.size() - 1);
mTaskKeyIndices.clear();
- int taskCount = mTaskKeys.size();
+ mTaskKeyIndices.ensureCapacity(taskCount);
for (int i = 0; i < taskCount; i++) {
Task.TaskKey k = mTaskKeys.get(i);
mTaskKeyIndices.put(k, i);
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 856200d..21d0bb6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,16 +16,27 @@
package com.android.systemui.recents.model;
+import android.animation.Animator;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.SparseArray;
+import android.view.animation.Interpolator;
+import com.android.internal.policy.DockedDividerUtils;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.misc.NamedCounter;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -35,8 +46,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Random;
@@ -44,6 +53,11 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
/**
@@ -59,17 +73,14 @@
*/
class FilteredTaskList {
- private static final String TAG = "FilteredTaskList";
- private static final boolean DEBUG = false;
-
ArrayList<Task> mTasks = new ArrayList<>();
ArrayList<Task> mFilteredTasks = new ArrayList<>();
- HashMap<Task.TaskKey, Integer> mTaskIndices = new HashMap<>();
+ ArrayMap<Task.TaskKey, Integer> mTaskIndices = new ArrayMap<>();
TaskFilter mFilter;
/** Sets the task filter, saving the current touch state */
boolean setFilter(TaskFilter filter) {
- ArrayList<Task> prevFilteredTasks = new ArrayList<Task>(mFilteredTasks);
+ ArrayList<Task> prevFilteredTasks = new ArrayList<>(mFilteredTasks);
mFilter = filter;
updateFilteredTasks();
if (!prevFilteredTasks.equals(mFilteredTasks)) {
@@ -180,8 +191,9 @@
/** Updates the mapping of tasks to indices. */
private void updateFilteredTaskIndices() {
- mTaskIndices.clear();
int taskCount = mFilteredTasks.size();
+ mTaskIndices.clear();
+ mTaskIndices.ensureCapacity(taskCount);
for (int i = 0; i < taskCount; i++) {
Task t = mFilteredTasks.get(i);
mTaskIndices.put(t.key, i);
@@ -229,30 +241,36 @@
public static class DockState implements DropTarget {
private static final int DOCK_AREA_ALPHA = 192;
- public static final DockState NONE = new DockState(-1, 96, null, null);
- public static final DockState LEFT = new DockState(
+ public static final DockState NONE = new DockState(DOCKED_INVALID, -1, 80, null, null, null);
+ public static final DockState LEFT = new DockState(DOCKED_LEFT,
DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
- new RectF(0, 0, 0.15f, 1), new RectF(0, 0, 0.15f, 1));
- public static final DockState TOP = new DockState(
+ new RectF(0, 0, 0.125f, 1), new RectF(0, 0, 0.125f, 1),
+ new RectF(0, 0, 0.5f, 1));
+ public static final DockState TOP = new DockState(DOCKED_TOP,
DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, DOCK_AREA_ALPHA,
- new RectF(0, 0, 1, 0.15f), new RectF(0, 0, 1, 0.15f));
- public static final DockState RIGHT = new DockState(
+ new RectF(0, 0, 1, 0.125f), new RectF(0, 0, 1, 0.125f),
+ new RectF(0, 0, 1, 0.5f));
+ public static final DockState RIGHT = new DockState(DOCKED_RIGHT,
DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
- new RectF(0.85f, 0, 1, 1), new RectF(0.85f, 0, 1, 1));
- public static final DockState BOTTOM = new DockState(
+ new RectF(0.875f, 0, 1, 1), new RectF(0.875f, 0, 1, 1),
+ new RectF(0.5f, 0, 1, 1));
+ public static final DockState BOTTOM = new DockState(DOCKED_BOTTOM,
DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, DOCK_AREA_ALPHA,
- new RectF(0, 0.85f, 1, 1), new RectF(0, 0.85f, 1, 1));
+ new RectF(0, 0.875f, 1, 1), new RectF(0, 0.875f, 1, 1),
+ new RectF(0, 0.5f, 1, 1));
@Override
- public boolean acceptsDrop(int x, int y, int width, int height) {
- return touchAreaContainsPoint(width, height, x, y);
+ public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+ return isCurrentTarget
+ ? areaContainsPoint(expandedTouchDockArea, width, height, x, y)
+ : areaContainsPoint(touchArea, width, height, x, y);
}
// Represents the view state of this dock state
public class ViewState {
public final int dockAreaAlpha;
public final ColorDrawable dockAreaOverlay;
- private ObjectAnimator dockAreaOverlayAnimator;
+ private AnimatorSet dockAreaOverlayAnimator;
private ViewState(int alpha) {
dockAreaAlpha = alpha;
@@ -261,56 +279,130 @@
}
/**
- * Creates a new alpha animation.
+ * Creates a new bounds and alpha animation.
*/
- public void startAlphaAnimation(int alpha, int duration) {
+ public void startAnimation(Rect bounds, int alpha, int duration,
+ Interpolator interpolator, boolean animateAlpha, boolean animateBounds) {
+ if (dockAreaOverlayAnimator != null) {
+ dockAreaOverlayAnimator.cancel();
+ }
+
+ ArrayList<Animator> animators = new ArrayList<>();
if (dockAreaOverlay.getAlpha() != alpha) {
- if (dockAreaOverlayAnimator != null) {
- dockAreaOverlayAnimator.cancel();
+ if (animateAlpha) {
+ animators.add(ObjectAnimator.ofInt(dockAreaOverlay,
+ Utilities.DRAWABLE_ALPHA, dockAreaOverlay.getAlpha(), alpha));
+ } else {
+ dockAreaOverlay.setAlpha(alpha);
}
- dockAreaOverlayAnimator = ObjectAnimator.ofInt(dockAreaOverlay, "alpha", alpha);
+ }
+ if (bounds != null && !dockAreaOverlay.getBounds().equals(bounds)) {
+ if (animateBounds) {
+ PropertyValuesHolder prop = PropertyValuesHolder.ofObject(
+ Utilities.DRAWABLE_RECT, new RectEvaluator(new Rect()),
+ dockAreaOverlay.getBounds(), bounds);
+ animators.add(ObjectAnimator.ofPropertyValuesHolder(dockAreaOverlay, prop));
+ } else {
+ dockAreaOverlay.setBounds(bounds);
+ }
+ }
+ if (!animators.isEmpty()) {
+ dockAreaOverlayAnimator = new AnimatorSet();
+ dockAreaOverlayAnimator.playTogether(animators);
dockAreaOverlayAnimator.setDuration(duration);
+ dockAreaOverlayAnimator.setInterpolator(interpolator);
dockAreaOverlayAnimator.start();
}
}
}
+ public final int dockSide;
public final int createMode;
public final ViewState viewState;
- private final RectF dockArea;
private final RectF touchArea;
+ private final RectF dockArea;
+ private final RectF expandedTouchDockArea;
/**
* @param createMode used to pass to ActivityManager to dock the task
* @param touchArea the area in which touch will initiate this dock state
* @param dockArea the visible dock area
+ * @param expandedTouchDockArea the areain which touch will continue to dock after entering
+ * the initial touch area. This is also the new dock area to
+ * draw.
*/
- DockState(int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea) {
+ DockState(int dockSide, int createMode, int dockAreaAlpha, RectF touchArea, RectF dockArea,
+ RectF expandedTouchDockArea) {
+ this.dockSide = dockSide;
this.createMode = createMode;
this.viewState = new ViewState(dockAreaAlpha);
this.dockArea = dockArea;
this.touchArea = touchArea;
+ this.expandedTouchDockArea = expandedTouchDockArea;
}
/**
- * Returns whether {@param x} and {@param y} are contained in the touch area scaled to the
+ * Returns whether {@param x} and {@param y} are contained in the area scaled to the
* given {@param width} and {@param height}.
*/
- public boolean touchAreaContainsPoint(int width, int height, float x, float y) {
- int left = (int) (touchArea.left * width);
- int top = (int) (touchArea.top * height);
- int right = (int) (touchArea.right * width);
- int bottom = (int) (touchArea.bottom * height);
+ public boolean areaContainsPoint(RectF area, int width, int height, float x, float y) {
+ int left = (int) (area.left * width);
+ int top = (int) (area.top * height);
+ int right = (int) (area.right * width);
+ int bottom = (int) (area.bottom * height);
return x >= left && y >= top && x <= right && y <= bottom;
}
/**
* Returns the docked task bounds with the given {@param width} and {@param height}.
*/
- public Rect getDockedBounds(int width, int height) {
+ public Rect getPreDockedBounds(int width, int height) {
return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
(int) (dockArea.right * width), (int) (dockArea.bottom * height));
}
+
+ /**
+ * Returns the expanded docked task bounds with the given {@param width} and
+ * {@param height}.
+ */
+ public Rect getDockedBounds(int width, int height, int dividerSize, Rect insets,
+ Resources res) {
+ // Calculate the docked task bounds
+ boolean isHorizontalDivision =
+ res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+ int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+ insets, width, height, dividerSize);
+ Rect newWindowBounds = new Rect();
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, newWindowBounds,
+ width, height, dividerSize);
+ return newWindowBounds;
+ }
+
+ /**
+ * Returns the task stack bounds with the given {@param width} and
+ * {@param height}.
+ */
+ public Rect getDockedTaskStackBounds(int width, int height, int dividerSize, Rect insets,
+ Resources res) {
+ RecentsConfiguration config = Recents.getConfiguration();
+
+ // Calculate the inverse docked task bounds
+ boolean isHorizontalDivision =
+ res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
+ int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+ insets, width, height, dividerSize);
+ Rect newWindowBounds = new Rect();
+ DockedDividerUtils.calculateBoundsForPosition(position,
+ DockedDividerUtils.invertDockSide(dockSide), newWindowBounds, width, height,
+ dividerSize);
+
+ // Calculate the task stack bounds from the new window bounds
+ Rect searchBarSpaceBounds = new Rect();
+ Rect taskStackBounds = new Rect();
+ config.getTaskStackBounds(newWindowBounds, insets.top, insets.right,
+ searchBarSpaceBounds, taskStackBounds);
+ return taskStackBounds;
+ }
}
// A comparator that sorts tasks by their last active time
@@ -344,7 +436,7 @@
TaskStackCallbacks mCb;
ArrayList<TaskGrouping> mGroups = new ArrayList<>();
- HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<>();
+ ArrayMap<Integer, TaskGrouping> mAffinitiesGroups = new ArrayMap<>();
public TaskStack() {
// Ensure that we only show non-docked tasks
@@ -446,6 +538,7 @@
mCb.onHistoryTaskRemoved(this, t);
}
}
+ mRawTaskList.remove(t);
}
/**
@@ -456,8 +549,8 @@
*/
public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
// Compute a has set for each of the tasks
- HashMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
- HashMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
+ ArrayMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
+ ArrayMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
ArrayList<Task> newTasks = new ArrayList<>();
@@ -588,16 +681,32 @@
}
/**
- * Returns the number of tasks in the active stack.
+ * Returns the number of stack and freeform tasks.
*/
- public int getStackTaskCount() {
+ public int getTaskCount() {
return mStackTaskList.size();
}
/**
- * Returns the number of freeform tasks in the active stack.
+ * Returns the number of stack tasks.
*/
- public int getStackTaskFreeformCount() {
+ public int getStackTaskCount() {
+ ArrayList<Task> tasks = mStackTaskList.getTasks();
+ int stackCount = 0;
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (!task.isFreeformTask()) {
+ stackCount++;
+ }
+ }
+ return stackCount;
+ }
+
+ /**
+ * Returns the number of freeform tasks.
+ */
+ public int getFreeformTaskCount() {
ArrayList<Task> tasks = mStackTaskList.getTasks();
int freeformCount = 0;
int taskCount = tasks.size();
@@ -664,7 +773,7 @@
*/
public void createAffiliatedGroupings(Context context) {
if (RecentsDebugFlags.Static.EnableSimulatedTaskGroups) {
- HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
+ ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
// Sort all tasks by increasing firstActiveTime of the task
ArrayList<Task> tasks = mStackTaskList.getTasks();
Collections.sort(tasks, new Comparator<Task>() {
@@ -729,9 +838,10 @@
mStackTaskList.set(tasks);
} else {
// Create the task groups
- HashMap<Task.TaskKey, Task> tasksMap = new HashMap<>();
+ ArrayMap<Task.TaskKey, Task> tasksMap = new ArrayMap<>();
ArrayList<Task> tasks = mStackTaskList.getTasks();
int taskCount = tasks.size();
+ tasksMap.ensureCapacity(taskCount);
for (int i = 0; i < taskCount; i++) {
Task t = tasks.get(i);
TaskGrouping group;
@@ -773,12 +883,12 @@
* Computes the components of tasks in this stack that have been removed as a result of a change
* in the specified package.
*/
- public HashSet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
+ public ArraySet<ComponentName> computeComponentsRemoved(String packageName, int userId) {
// Identify all the tasks that should be removed as a result of the package being removed.
// Using a set to ensure that we callback once per unique component.
SystemServicesProxy ssp = Recents.getSystemServices();
- HashSet<ComponentName> existingComponents = new HashSet<>();
- HashSet<ComponentName> removedComponents = new HashSet<>();
+ ArraySet<ComponentName> existingComponents = new ArraySet<>();
+ ArraySet<ComponentName> removedComponents = new ArraySet<>();
ArrayList<Task.TaskKey> taskKeys = getTaskKeys();
for (Task.TaskKey t : taskKeys) {
// Skip if this doesn't apply to the current user
@@ -816,8 +926,8 @@
/**
* Given a list of tasks, returns a map of each task's key to the task.
*/
- private HashMap<Task.TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
- HashMap<Task.TaskKey, Task> map = new HashMap<>();
+ private ArrayMap<Task.TaskKey, Task> createTaskKeyMapFromList(List<Task> tasks) {
+ ArrayMap<Task.TaskKey, Task> map = new ArrayMap<>(tasks.size());
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
index 8ae00a7..3ad368c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
@@ -25,5 +25,5 @@
* Returns whether this target can accept this drop. The x,y are relative to the top level
* RecentsView, and the width/height are of the RecentsView.
*/
- boolean acceptsDrop(int x, int y, int width, int height);
+ boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget);
}
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 890713e..d3a1e91 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -17,13 +17,12 @@
package com.android.systemui.recents.views;
import android.content.Context;
-import android.graphics.Rect;
import android.graphics.RectF;
+import android.util.ArrayMap;
import com.android.systemui.R;
import com.android.systemui.recents.model.Task;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
/**
@@ -32,7 +31,7 @@
public class FreeformWorkspaceLayoutAlgorithm {
// Optimization, allows for quick lookup of task -> rect
- private HashMap<Task.TaskKey, RectF> mTaskRectMap = new HashMap<>();
+ private ArrayMap<Task.TaskKey, RectF> mTaskRectMap = new ArrayMap<>();
private int mTaskPadding;
@@ -49,6 +48,7 @@
public void update(List<Task> freeformTasks, TaskStackLayoutAlgorithm stackLayout) {
Collections.reverse(freeformTasks);
mTaskRectMap.clear();
+ mTaskRectMap.ensureCapacity(freeformTasks.size());
int numFreeformTasks = stackLayout.mNumFreeformTasks;
if (!freeformTasks.isEmpty()) {
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 51cae86..b363ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -165,7 +165,7 @@
int taskIndexFromFront = 0;
int taskIndex = stack.indexOfStackTask(task);
if (taskIndex > -1) {
- taskIndexFromFront = stack.getStackTaskCount() - taskIndex - 1;
+ taskIndexFromFront = stack.getTaskCount() - taskIndex - 1;
}
EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront));
} else {
@@ -289,7 +289,7 @@
// never happen)
specs.add(composeOffscreenAnimationSpec(t, offscreenTaskRect));
} else {
- layoutAlgorithm.getStackTransform(task, stackScroll, mTmpTransform, null);
+ layoutAlgorithm.getStackTransform(t, stackScroll, mTmpTransform, null);
specs.add(composeAnimationSpec(tv, mTmpTransform, true /* addHeaderBitmap */));
}
}
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 e28e2b3..501f052 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -63,6 +63,7 @@
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.util.ArrayList;
import java.util.Collections;
@@ -76,8 +77,7 @@
*/
public class RecentsView extends FrameLayout {
- private static final String TAG = "RecentsView";
- private static final boolean DEBUG = false;
+ private static final int DOCK_AREA_OVERLAY_TRANSITION_DURATION = 135;
private final Handler mHandler;
@@ -89,6 +89,7 @@
private boolean mAwaitingFirstLayout = true;
private boolean mLastTaskLaunchedWasFreeform;
private Rect mSystemInsets = new Rect();
+ private int mDividerSize;
private RecentsTransitionHelper mTransitionHelper;
private RecentsViewTouchHandler mTouchHandler;
@@ -118,12 +119,15 @@
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setWillNotDraw(false);
+
+ SystemServicesProxy ssp = Recents.getSystemServices();
mHandler = new Handler();
mTransitionHelper = new RecentsTransitionHelper(getContext(), mHandler);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_linear_in);
+ mDividerSize = ssp.getDockedDividerSize(context);
mTouchHandler = new RecentsViewTouchHandler(this);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
@@ -163,7 +167,7 @@
}
// Update the top level view's visibilities
- if (stack.getStackTaskCount() > 0) {
+ if (stack.getTaskCount() > 0) {
hideEmptyView();
} else {
showEmptyView();
@@ -470,52 +474,77 @@
public final void onBusEvent(DragStartEvent event) {
updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
- TaskStack.DockState.NONE.viewState.dockAreaAlpha);
+ true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+ true /* animateAlpha */, false /* animateBounds */);
}
public final void onBusEvent(DragDropTargetChangedEvent event) {
if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) {
updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
- TaskStack.DockState.NONE.viewState.dockAreaAlpha);
+ true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha,
+ true /* animateAlpha */, true /* animateBounds */);
} else {
final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
- updateVisibleDockRegions(new TaskStack.DockState[] {dockState}, -1);
+ updateVisibleDockRegions(new TaskStack.DockState[] {dockState},
+ false /* isDefaultDockState */, -1, true /* animateAlpha */,
+ true /* animateBounds */);
}
}
public final void onBusEvent(final DragEndEvent event) {
- // Animate the overlay alpha back to 0
- updateVisibleDockRegions(null, -1);
-
// Handle the case where we drop onto a dock region
if (event.dropTarget instanceof TaskStack.DockState) {
- TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
+ final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
+
+ // Hide the dock region
+ updateVisibleDockRegions(null, false /* isDefaultDockState */, -1,
+ false /* animateAlpha */, false /* animateBounds */);
+
TaskStackLayoutAlgorithm stackLayout = mTaskStackView.getStackAlgorithm();
TaskStackViewScroller stackScroller = mTaskStackView.getScroller();
TaskViewTransform tmpTransform = new TaskViewTransform();
+ // We translated the view but we need to animate it back from the current layout-space
+ // rect to its final layout-space rect
+ int x = (int) event.taskView.getTranslationX();
+ int y = (int) event.taskView.getTranslationY();
+ Rect taskViewRect = new Rect(event.taskView.getLeft(), event.taskView.getTop(),
+ event.taskView.getRight(), event.taskView.getBottom());
+ taskViewRect.offset(x, y);
+ event.taskView.setTranslationX(0);
+ event.taskView.setTranslationY(0);
+ event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
+ taskViewRect.right, taskViewRect.bottom);
+
// Remove the task view after it is docked
+ mTaskStackView.updateLayout(false /* boundScroll */);
stackLayout.getStackTransform(event.task, stackScroller.getStackScroll(), tmpTransform,
null);
- tmpTransform.scale = event.taskView.getScaleX();
- tmpTransform.rect.offset(event.taskView.getTranslationX(),
- event.taskView.getTranslationY());
+ tmpTransform.alpha = 0;
+ tmpTransform.scale = 1f;
+ tmpTransform.rect.set(taskViewRect);
mTaskStackView.updateTaskViewToTransform(event.taskView, tmpTransform,
- new TaskViewAnimation(150, mFastOutLinearInInterpolator,
+ new TaskViewAnimation(125, PhoneStatusBar.ALPHA_OUT,
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ // Dock the task and launch it
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ssp.startTaskInDockedMode(getContext(), event.task.key.id,
+ dockState.createMode);
+ launchTask(event.task, null, INVALID_STACK_ID);
+
mTaskStackView.getStack().removeTask(event.task);
}
}));
- // Dock the task and launch it
- SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode);
- launchTask(event.task, null, INVALID_STACK_ID);
MetricsLogger.action(mContext,
MetricsLogger.ACTION_WINDOW_DOCK_DRAG_DROP);
+ } else {
+ // Animate the overlay alpha back to 0
+ updateVisibleDockRegions(null, true /* isDefaultDockState */, -1,
+ true /* animateAlpha */, false /* animateBounds */);
}
}
@@ -638,7 +667,9 @@
/**
* Updates the dock region to match the specified dock state.
*/
- private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates, int overrideAlpha) {
+ private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates,
+ boolean isDefaultDockState, int overrideAlpha, boolean animateAlpha,
+ boolean animateBounds) {
ArraySet<TaskStack.DockState> newDockStatesSet = new ArraySet<>();
if (newDockStates != null) {
Collections.addAll(newDockStatesSet, newDockStates);
@@ -647,14 +678,21 @@
TaskStack.DockState.ViewState viewState = dockState.viewState;
if (newDockStates == null || !newDockStatesSet.contains(dockState)) {
// This is no longer visible, so hide it
- viewState.startAlphaAnimation(0, 150);
+ viewState.startAnimation(null, 0, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
+ PhoneStatusBar.ALPHA_OUT, animateAlpha, animateBounds);
} else {
// This state is now visible, update the bounds and show it
int alpha = (overrideAlpha != -1 ? overrideAlpha : viewState.dockAreaAlpha);
- viewState.dockAreaOverlay.setBounds(
- dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight()));
- viewState.dockAreaOverlay.setCallback(this);
- viewState.startAlphaAnimation(alpha, 150);
+ Rect bounds = isDefaultDockState
+ ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight())
+ : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
+ mDividerSize, mSystemInsets, getResources());
+ if (viewState.dockAreaOverlay.getCallback() != this) {
+ viewState.dockAreaOverlay.setCallback(this);
+ viewState.dockAreaOverlay.setBounds(bounds);
+ }
+ viewState.startAnimation(bounds, alpha, DOCK_AREA_OVERLAY_TRANSITION_DURATION,
+ PhoneStatusBar.ALPHA_IN, animateAlpha, animateBounds);
}
}
}
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 473334b..0ca46a0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -19,6 +19,7 @@
import android.content.res.Configuration;
import android.graphics.Point;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.EventBus;
@@ -64,13 +65,16 @@
private Point mTaskViewOffset = new Point();
private Point mDownPos = new Point();
- private boolean mDragging;
+ private boolean mDragRequested;
+ private boolean mIsDragging;
+ private float mDragSlop;
private DropTarget mLastDropTarget;
private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
public RecentsViewTouchHandler(RecentsView rv) {
mRv = rv;
+ mDragSlop = ViewConfiguration.get(rv.getContext()).getScaledTouchSlop();
}
/**
@@ -96,13 +100,13 @@
/** Touch preprocessing for handling below */
public boolean onInterceptTouchEvent(MotionEvent ev) {
handleTouchEvent(ev);
- return mDragging;
+ return mDragRequested;
}
/** Handles touch events once we have intercepted them */
public boolean onTouchEvent(MotionEvent ev) {
handleTouchEvent(ev);
- return mDragging;
+ return mDragRequested;
}
/**** Events ****/
@@ -110,7 +114,9 @@
public final void onBusEvent(DragStartEvent event) {
SystemServicesProxy ssp = Recents.getSystemServices();
mRv.getParent().requestDisallowInterceptTouchEvent(true);
- mDragging = true;
+ mDragRequested = true;
+ // We defer starting the actual drag handling until the user moves past the drag slop
+ mIsDragging = false;
mDragTask = event.task;
mTaskView = event.taskView;
mDropTargets.clear();
@@ -137,7 +143,7 @@
}
public final void onBusEvent(DragEndEvent event) {
- mDragging = false;
+ mDragRequested = false;
mDragTask = null;
mTaskView = null;
mLastDropTarget = null;
@@ -153,25 +159,45 @@
mDownPos.set((int) ev.getX(), (int) ev.getY());
break;
case MotionEvent.ACTION_MOVE: {
- if (mDragging) {
- int width = mRv.getMeasuredWidth();
- int height = mRv.getMeasuredHeight();
- float evX = ev.getX();
- float evY = ev.getY();
- float x = evX - mTaskViewOffset.x;
- float y = evY - mTaskViewOffset.y;
+ float evX = ev.getX();
+ float evY = ev.getY();
+ float x = evX - mTaskViewOffset.x;
+ float y = evY - mTaskViewOffset.y;
- DropTarget currentDropTarget = null;
- for (DropTarget target : mDropTargets) {
- if (target.acceptsDrop((int) evX, (int) evY, width, height)) {
- currentDropTarget = target;
- break;
- }
+ if (mDragRequested) {
+ if (!mIsDragging) {
+ mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop;
}
- if (mLastDropTarget != currentDropTarget) {
- mLastDropTarget = currentDropTarget;
- EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
- currentDropTarget));
+ if (mIsDragging) {
+ int width = mRv.getMeasuredWidth();
+ int height = mRv.getMeasuredHeight();
+
+ DropTarget currentDropTarget = null;
+
+ // Give priority to the current drop target to retain the touch handling
+ if (mLastDropTarget != null) {
+ if (mLastDropTarget.acceptsDrop((int) evX, (int) evY, width, height,
+ true /* isCurrentTarget */)) {
+ currentDropTarget = mLastDropTarget;
+ }
+ }
+
+ // Otherwise, find the next target to handle this event
+ if (currentDropTarget == null) {
+ for (DropTarget target : mDropTargets) {
+ if (target.acceptsDrop((int) evX, (int) evY, width, height,
+ false /* isCurrentTarget */)) {
+ currentDropTarget = target;
+ break;
+ }
+ }
+ }
+ if (mLastDropTarget != currentDropTarget) {
+ mLastDropTarget = currentDropTarget;
+ EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask,
+ currentDropTarget));
+ }
+
}
mTaskView.setTranslationX(x);
@@ -181,7 +207,7 @@
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
- if (mDragging) {
+ if (mDragRequested) {
EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
mLastDropTarget));
break;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
deleted file mode 100644
index b7c1de3..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents.views;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.util.DisplayMetrics;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-
-/**
- * This class facilitates swipe to dismiss. It defines an interface to be implemented by the
- * by the class hosting the views that need to swiped, and, using this interface, handles touch
- * events and translates / fades / animates the view as it is dismissed.
- */
-public class SwipeHelper {
- static final String TAG = "SwipeHelper";
- private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
- private static final boolean CONSTRAIN_SWIPE = true;
- private static final boolean FADE_OUT_DURING_SWIPE = true;
- private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
-
- public static final int X = 0;
- public static final int Y = 1;
-
- private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
- private Interpolator mLinearOutSlowInInterpolator;
-
- private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
- private int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms
- private int MAX_ESCAPE_ANIMATION_DURATION = 150; // ms
- private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 250; // ms
-
- public static float ALPHA_FADE_START = 0.15f; // fraction of thumbnail width
- // where fade starts
- static final float ALPHA_FADE_END = 0.65f; // fraction of thumbnail width
- // beyond which alpha->0
- private float mMinAlpha = 0f;
-
- private float mPagingTouchSlop;
- Callback mCallback;
- private int mSwipeDirection;
- private VelocityTracker mVelocityTracker;
-
- private float mInitialTouchPos;
- private boolean mDragging;
- private float mSnapBackTranslationX;
-
- private View mCurrView;
- private boolean mCanCurrViewBeDimissed;
- private float mDensityScale;
-
- public boolean mAllowSwipeTowardsStart = true;
- public boolean mAllowSwipeTowardsEnd = true;
- private boolean mRtl;
-
- public SwipeHelper(Context context, int swipeDirection, Callback callback, float densityScale,
- float pagingTouchSlop) {
- mCallback = callback;
- mSwipeDirection = swipeDirection;
- mVelocityTracker = VelocityTracker.obtain();
- mDensityScale = densityScale;
- mPagingTouchSlop = pagingTouchSlop;
- mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.linear_out_slow_in);
- }
-
- public void setDensityScale(float densityScale) {
- mDensityScale = densityScale;
- }
-
- public void setSnapBackTranslationX(float translationX) {
- mSnapBackTranslationX = translationX;
- }
-
- public void setPagingTouchSlop(float pagingTouchSlop) {
- mPagingTouchSlop = pagingTouchSlop;
- }
-
- public void cancelOngoingDrag() {
- if (mDragging) {
- if (mCurrView != null) {
- mCallback.onDragCancelled(mCurrView);
- setTranslation(mCurrView, 0);
- mCallback.onSnapBackCompleted(mCurrView);
- mCurrView = null;
- }
- mDragging = false;
- }
- }
-
- public void resetTranslation(View v) {
- setTranslation(v, 0);
- }
-
- private float getPos(MotionEvent ev) {
- return mSwipeDirection == X ? ev.getX() : ev.getY();
- }
-
- private float getTranslation(View v) {
- return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
- }
-
- private float getVelocity(VelocityTracker vt) {
- return mSwipeDirection == X ? vt.getXVelocity() :
- vt.getYVelocity();
- }
-
- private ObjectAnimator createTranslationAnimation(View v, float newPos) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(v,
- mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
- return anim;
- }
-
- private float getPerpendicularVelocity(VelocityTracker vt) {
- return mSwipeDirection == X ? vt.getYVelocity() :
- vt.getXVelocity();
- }
-
- private void setTranslation(View v, float translate) {
- if (mSwipeDirection == X) {
- v.setTranslationX(translate);
- } else {
- v.setTranslationY(translate);
- }
- }
-
- private float getSize(View v) {
- final DisplayMetrics dm = v.getContext().getResources().getDisplayMetrics();
- return mSwipeDirection == X ? dm.widthPixels : dm.heightPixels;
- }
-
- public void setMinAlpha(float minAlpha) {
- mMinAlpha = minAlpha;
- }
-
- float getAlphaForOffset(View view) {
- float viewSize = getSize(view);
- final float fadeSize = ALPHA_FADE_END * viewSize;
- float result = 1.0f;
- float pos = getTranslation(view);
- if (pos >= viewSize * ALPHA_FADE_START) {
- result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
- } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
- result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
- }
- result = Math.min(result, 1.0f);
- result = Math.max(result, 0f);
- return Math.max(mMinAlpha, result);
- }
-
- /**
- * Determines whether the given view has RTL layout.
- */
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- public static boolean isLayoutRtl(View view) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
- } else {
- return false;
- }
- }
-
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- final int action = ev.getAction();
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mDragging = false;
- mCurrView = mCallback.getChildAtPosition(ev);
- mVelocityTracker.clear();
- if (mCurrView != null) {
- mRtl = isLayoutRtl(mCurrView);
- mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
- mVelocityTracker.addMovement(ev);
- mInitialTouchPos = getPos(ev);
- } else {
- mCanCurrViewBeDimissed = false;
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (mCurrView != null) {
- mVelocityTracker.addMovement(ev);
- float pos = getPos(ev);
- float delta = pos - mInitialTouchPos;
- if (Math.abs(delta) > mPagingTouchSlop) {
- mCallback.onBeginDrag(mCurrView);
- mDragging = true;
- mInitialTouchPos = pos - getTranslation(mCurrView);
- }
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mDragging = false;
- mCurrView = null;
- break;
- }
- return mDragging;
- }
-
- /**
- * @param view The view to be dismissed
- * @param velocity The desired pixels/second speed at which the view should move
- */
- private void dismissChild(final View view, float velocity) {
- final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
- float newPos;
- if (velocity < 0
- || (velocity == 0 && getTranslation(view) < 0)
- // if we use the Menu to dismiss an item in landscape, animate up
- || (velocity == 0 && getTranslation(view) == 0 && mSwipeDirection == Y)) {
- newPos = -getSize(view);
- } else {
- newPos = getSize(view);
- }
- int duration = MAX_ESCAPE_ANIMATION_DURATION;
- if (velocity != 0) {
- duration = Math.min(duration,
- (int) (Math.abs(newPos - getTranslation(view)) *
- 1000f / Math.abs(velocity)));
- } else {
- duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
- }
-
- ValueAnimator anim = createTranslationAnimation(view, newPos);
- anim.setInterpolator(sLinearInterpolator);
- anim.setDuration(duration);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCallback.onChildDismissed(view);
- if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
- view.setAlpha(1.f);
- }
- }
- });
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
- view.setAlpha(getAlphaForOffset(view));
- }
- }
- });
- anim.start();
- }
-
- private void snapChild(final View view, float velocity) {
- final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
- ValueAnimator anim = createTranslationAnimation(view, mSnapBackTranslationX);
- int duration = SNAP_ANIM_LEN;
- anim.setDuration(duration);
- anim.setInterpolator(mLinearOutSlowInInterpolator);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
- view.setAlpha(getAlphaForOffset(view));
- }
- mCallback.onSwipeChanged(mCurrView, view.getTranslationX());
- }
- });
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
- view.setAlpha(1.0f);
- }
- mCallback.onSnapBackCompleted(view);
- }
- });
- anim.start();
- }
-
- public boolean onTouchEvent(MotionEvent ev) {
- if (!mDragging) {
- if (!onInterceptTouchEvent(ev)) {
- return mCanCurrViewBeDimissed;
- }
- }
-
- mVelocityTracker.addMovement(ev);
- final int action = ev.getAction();
- switch (action) {
- case MotionEvent.ACTION_OUTSIDE:
- case MotionEvent.ACTION_MOVE:
- if (mCurrView != null) {
- float delta = getPos(ev) - mInitialTouchPos;
- setSwipeAmount(delta);
- mCallback.onSwipeChanged(mCurrView, delta);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (mCurrView != null) {
- endSwipe(mVelocityTracker);
- }
- break;
- }
- return true;
- }
-
- private void setSwipeAmount(float amount) {
- // don't let items that can't be dismissed be dragged more than
- // maxScrollDistance
- if (CONSTRAIN_SWIPE
- && (!isValidSwipeDirection(amount) || !mCallback.canChildBeDismissed(mCurrView))) {
- float size = getSize(mCurrView);
- float maxScrollDistance = 0.15f * size;
- if (Math.abs(amount) >= size) {
- amount = amount > 0 ? maxScrollDistance : -maxScrollDistance;
- } else {
- amount = maxScrollDistance * (float) Math.sin((amount/size)*(Math.PI/2));
- }
- }
- setTranslation(mCurrView, amount);
- if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
- float alpha = getAlphaForOffset(mCurrView);
- mCurrView.setAlpha(alpha);
- }
- }
-
- private boolean isValidSwipeDirection(float amount) {
- if (mSwipeDirection == X) {
- if (mRtl) {
- return (amount <= 0) ? mAllowSwipeTowardsEnd : mAllowSwipeTowardsStart;
- } else {
- return (amount <= 0) ? mAllowSwipeTowardsStart : mAllowSwipeTowardsEnd;
- }
- }
-
- // Vertical swipes are always valid.
- return true;
- }
-
- private void endSwipe(VelocityTracker velocityTracker) {
- velocityTracker.computeCurrentVelocity(1000 /* px/sec */);
- float velocity = getVelocity(velocityTracker);
- float perpendicularVelocity = getPerpendicularVelocity(velocityTracker);
- float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
- float translation = getTranslation(mCurrView);
- // Decide whether to dismiss the current view
- boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
- Math.abs(translation) > 0.6 * getSize(mCurrView);
- boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
- (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
- (velocity > 0) == (translation > 0);
-
- boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
- && isValidSwipeDirection(translation)
- && (childSwipedFastEnough || childSwipedFarEnough);
-
- if (dismissChild) {
- // flingadingy
- dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
- } else {
- // snappity
- mCallback.onDragCancelled(mCurrView);
- snapChild(mCurrView, velocity);
- }
- }
-
- public interface Callback {
- View getChildAtPosition(MotionEvent ev);
-
- boolean canChildBeDismissed(View v);
-
- void onBeginDrag(View v);
-
- void onSwipeChanged(View v, float delta);
-
- void onChildDismissed(View v);
-
- void onSnapBackCompleted(View v);
-
- void onDragCancelled(View v);
- }
-}
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 80a35de..2930f4d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -97,7 +97,7 @@
Task launchTargetTask = stack.getLaunchTarget();
// Break early if there are no tasks
- if (stack.getStackTaskCount() == 0) {
+ if (stack.getTaskCount() == 0) {
return;
}
@@ -159,7 +159,7 @@
Task launchTargetTask = stack.getLaunchTarget();
// Break early if there are no tasks
- if (stack.getStackTaskCount() == 0) {
+ if (stack.getTaskCount() == 0) {
return;
}
@@ -229,7 +229,7 @@
TaskStack stack = mStackView.getStack();
// Break early if there are no tasks
- if (stack.getStackTaskCount() == 0) {
+ if (stack.getTaskCount() == 0) {
return;
}
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 c2bb745..2fa99ce 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -21,6 +21,8 @@
import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.FloatProperty;
import android.util.Property;
import android.view.animation.AnimationUtils;
@@ -37,7 +39,6 @@
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
-import java.util.HashMap;
/**
* Used to describe a visible range that can be normalized to [0, 1].
@@ -137,9 +138,8 @@
public static StackState getStackStateForStack(TaskStack stack) {
SystemServicesProxy ssp = Recents.getSystemServices();
boolean hasFreeformWorkspaces = ssp.hasFreeformWorkspaceSupport();
- int taskCount = stack.getStackTaskCount();
- int freeformCount = stack.getStackTaskFreeformCount();
- int stackCount = taskCount - freeformCount;
+ int freeformCount = stack.getFreeformTaskCount();
+ int stackCount = stack.getStackTaskCount();
if (hasFreeformWorkspaces && stackCount > 0 && freeformCount > 0) {
return SPLIT;
} else if (hasFreeformWorkspaces && freeformCount > 0) {
@@ -270,7 +270,7 @@
int mMaxTranslationZ;
// Optimization, allows for quick lookup of task -> index
- private HashMap<Task.TaskKey, Integer> mTaskIndexMap = new HashMap<>();
+ private ArrayMap<Task.TaskKey, Integer> mTaskIndexMap = new ArrayMap<>();
// The freeform workspace layout
FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
@@ -373,7 +373,7 @@
* Computes the minimum and maximum scroll progress values and the progress values for each task
* in the stack.
*/
- void update(TaskStack stack) {
+ void update(TaskStack stack, ArraySet<Task> ignoreTasksSet) {
SystemServicesProxy ssp = Recents.getSystemServices();
// Clear the progress map
@@ -393,6 +393,9 @@
ArrayList<Task> stackTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
+ if (ignoreTasksSet.contains(task)) {
+ continue;
+ }
if (task.isFreeformTask()) {
freeformTasks.add(task);
} else {
@@ -405,6 +408,7 @@
// Put each of the tasks in the progress map at a fixed index (does not need to actually
// map to a scroll position, just by index)
int taskCount = stackTasks.size();
+ mTaskIndexMap.ensureCapacity(taskCount);
for (int i = 0; i < taskCount; i++) {
Task task = stackTasks.get(i);
mTaskIndexMap.put(task.key, i);
@@ -645,7 +649,11 @@
y += (mStackRect.top - mTaskRect.top);
z = Math.max(mMinTranslationZ, Math.min(mMaxTranslationZ,
mMinTranslationZ + (p * (mMaxTranslationZ - mMinTranslationZ))));
- relP = unfocusedP;
+ if (mNumStackTasks == 1) {
+ relP = 1f;
+ } else {
+ relP = Math.min(mMaxScrollP, unfocusedP);
+ }
}
// Fill out the transform
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 9568fac..713cfc3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -28,8 +28,8 @@
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.Settings;
-import android.util.IntProperty;
-import android.util.Property;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -77,8 +77,7 @@
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collections;
import java.util.List;
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
@@ -105,19 +104,6 @@
private static final int DRAG_SCALE_DURATION = 175;
private static final float DRAG_SCALE_FACTOR = 1.05f;
- public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
- new IntProperty<Drawable>("drawableAlpha") {
- @Override
- public void setValue(Drawable object, int alpha) {
- object.setAlpha(alpha);
- }
-
- @Override
- public Integer get(Drawable object) {
- return object.getAlpha();
- }
- };
-
TaskStack mStack;
TaskStackLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScroller mStackScroller;
@@ -126,6 +112,7 @@
GradientDrawable mFreeformWorkspaceBackground;
ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
ViewPool<TaskView, Task> mViewPool;
+ boolean mStartTimerIndicator;
ArrayList<TaskView> mTaskViews = new ArrayList<>();
ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
@@ -135,6 +122,7 @@
Task mFocusedTask;
int mTaskCornerRadiusPx;
+ private int mDividerSize;
boolean mTaskViewsClipDirty = true;
boolean mAwaitingFirstLayout = true;
@@ -142,10 +130,14 @@
boolean mTouchExplorationEnabled;
boolean mScreenPinningEnabled;
- Rect mTaskStackBounds = new Rect();
+ // The stable stack bounds are the full bounds that we were measured with from RecentsView
+ Rect mStableStackBounds = new Rect();
+ // The current stack bounds are dynamic and may change as the user drags and drops
+ Rect mStackBounds = new Rect();
int[] mTmpVisibleRange = new int[2];
Rect mTmpRect = new Rect();
- HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<>();
+ ArrayMap<Task.TaskKey, TaskView> mTmpTaskViewMap = new ArrayMap<>();
+ ArraySet<Task> mTmpTaskSet = new ArraySet<>();
List<TaskView> mTmpTaskViews = new ArrayList<>();
TaskViewTransform mTmpTransform = new TaskViewTransform();
LayoutInflater mInflater;
@@ -157,23 +149,35 @@
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- mTaskViewsClipDirty = true;
- invalidate();
+ if (!mTaskViewsClipDirty) {
+ mTaskViewsClipDirty = true;
+ invalidate();
+ }
}
};
// The drop targets for a task drag
private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
@Override
- public boolean acceptsDrop(int x, int y, int width, int height) {
- return mLayoutAlgorithm.mFreeformRect.contains(x, y);
+ public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+ // This drop target has a fixed bounds and should be checked last, so just fall through
+ // if it is the current target
+ if (!isCurrentTarget) {
+ return mLayoutAlgorithm.mFreeformRect.contains(x, y);
+ }
+ return false;
}
};
private DropTarget mStackDropTarget = new DropTarget() {
@Override
- public boolean acceptsDrop(int x, int y, int width, int height) {
- return mLayoutAlgorithm.mStackRect.contains(x, y);
+ public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+ // This drop target has a fixed bounds and should be checked last, so just fall through
+ // if it is the current target
+ if (!isCurrentTarget) {
+ return mLayoutAlgorithm.mStackRect.contains(x, y);
+ }
+ return false;
}
};
@@ -195,6 +199,7 @@
com.android.internal.R.interpolator.fast_out_slow_in);
mTaskCornerRadiusPx = res.getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
+ mDividerSize = ssp.getDockedDividerSize(context);
int taskBarDismissDozeDelaySeconds = getResources().getInteger(
R.integer.recents_task_bar_dismiss_delay_seconds);
@@ -223,11 +228,8 @@
@Override
protected void onAttachedToWindow() {
- SystemServicesProxy ssp = Recents.getSystemServices();
- mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
- mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
- Settings.System.LOCK_TO_APP_ENABLED) != 0;
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+ readSystemFlags();
super.onAttachedToWindow();
}
@@ -333,6 +335,7 @@
mUIDozeTrigger.resetTrigger();
mStackScroller.reset();
mLayoutAlgorithm.reset();
+ readSystemFlags();
requestLayout();
}
@@ -346,9 +349,8 @@
* This call ignores freeform tasks.
*/
private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
- ArrayList<Task> tasks,
- float stackScroll,
- int[] visibleRangeOut) {
+ ArrayList<Task> tasks, float stackScroll,
+ int[] visibleRangeOut, ArraySet<Task> ignoreTasksSet) {
int taskTransformCount = taskTransforms.size();
int taskCount = tasks.size();
int frontMostVisibleIndex = -1;
@@ -369,6 +371,10 @@
TaskViewTransform frontTransform = null;
for (int i = taskCount - 1; i >= 0; i--) {
Task task = tasks.get(i);
+ if (ignoreTasksSet.contains(task)) {
+ continue;
+ }
+
TaskViewTransform transform = mLayoutAlgorithm.getStackTransform(task, stackScroll,
taskTransforms.get(i), frontTransform);
@@ -409,28 +415,34 @@
* they are initially picked up from the pool, when they will be placed in a suitable initial
* position.
*/
- private void bindTaskViewsWithStack() {
+ private void bindTaskViewsWithStack(ArraySet<Task> ignoreTasksSet) {
final float stackScroll = mStackScroller.getStackScroll();
final int[] visibleStackRange = mTmpVisibleRange;
// Get all the task transforms
final ArrayList<Task> tasks = mStack.getStackTasks();
- final boolean isValidVisibleStackRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
- stackScroll, visibleStackRange);
+ final boolean isValidVisibleStackRange = updateStackTransforms(mCurrentTaskTransforms,
+ tasks, stackScroll, visibleStackRange, ignoreTasksSet);
// Return all the invisible children to the pool
- mTmpTaskViewMap.clear();
final List<TaskView> taskViews = getTaskViews();
final int taskViewCount = taskViews.size();
int lastFocusedTaskIndex = -1;
+ mTmpTaskViewMap.clear();
+ mTmpTaskViewMap.ensureCapacity(tasks.size());
for (int i = taskViewCount - 1; i >= 0; i--) {
final TaskView tv = taskViews.get(i);
final Task task = tv.getTask();
final int taskIndex = mStack.indexOfStackTask(task);
+ // Skip ignored tasks
+ if (ignoreTasksSet.contains(task)) {
+ continue;
+ }
+
if (task.isFreeformTask() ||
visibleStackRange[1] <= taskIndex && taskIndex <= visibleStackRange[0]) {
- mTmpTaskViewMap.put(task, tv);
+ mTmpTaskViewMap.put(task.key, tv);
} else {
if (mTouchExplorationEnabled) {
lastFocusedTaskIndex = taskIndex;
@@ -442,16 +454,21 @@
// Pick up all the newly visible children
int lastVisStackIndex = isValidVisibleStackRange ? visibleStackRange[1] : 0;
- for (int i = mStack.getStackTaskCount() - 1; i >= lastVisStackIndex; i--) {
+ for (int i = mStack.getTaskCount() - 1; i >= lastVisStackIndex; i--) {
final Task task = tasks.get(i);
final TaskViewTransform transform = mCurrentTaskTransforms.get(i);
+ // Skip ignored tasks
+ if (ignoreTasksSet.contains(task)) {
+ continue;
+ }
+
// Skip the invisible non-freeform stack tasks
if (i > visibleStackRange[0] && !task.isFreeformTask()) {
continue;
}
- TaskView tv = mTmpTaskViewMap.get(task);
+ TaskView tv = mTmpTaskViewMap.get(task.key);
if (tv == null) {
tv = mViewPool.pickUpViewFromPool(task, task);
if (task.isFreeformTask()) {
@@ -495,8 +512,16 @@
/**
* Cancels any existing {@link TaskView} animations, and updates each {@link TaskView} to its
* current position as defined by the {@link TaskStackLayoutAlgorithm}.
+ *
+ * @param ignoreTasks the set of tasks to ignore in the relayout
*/
- private void updateTaskViewsToLayout(TaskViewAnimation animation) {
+ private void updateTaskViewsToLayout(TaskViewAnimation animation, Task... ignoreTasks) {
+ // Keep track of the ignore tasks
+ ArraySet<Task> ignoreTasksSet = mTmpTaskSet;
+ ignoreTasksSet.clear();
+ ignoreTasksSet.ensureCapacity(ignoreTasks.length);
+ Collections.addAll(ignoreTasksSet, ignoreTasks);
+
// If we had a deferred animation, cancel that
mDeferredTaskViewUpdateAnimation = null;
@@ -504,7 +529,7 @@
cancelAllTaskViewAnimations();
// Fetch the current set of TaskViews
- bindTaskViewsWithStack();
+ bindTaskViewsWithStack(ignoreTasksSet);
// Animate them to their final transforms with the given animation
List<TaskView> taskViews = getTaskViews();
@@ -514,6 +539,10 @@
final int taskIndex = mStack.indexOfStackTask(tv.getTask());
final TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
+ if (ignoreTasksSet.contains(tv.getTask())) {
+ continue;
+ }
+
updateTaskViewToTransform(tv, transform, animation);
}
}
@@ -541,8 +570,7 @@
*/
private void cancelAllTaskViewAnimations() {
List<TaskView> taskViews = getTaskViews();
- int taskViewCount = taskViews.size();
- for (int i = 0; i < taskViewCount; i++) {
+ for (int i = taskViews.size() - 1; i >= 0; i--) {
final TaskView tv = taskViews.get(i);
tv.cancelTransformAnimation();
}
@@ -593,10 +621,20 @@
mTaskViewsClipDirty = false;
}
- /** Updates the min and max virtual scroll bounds */
- void updateLayout(boolean boundScrollToNewMinMax) {
+ /**
+ * Updates the min and max virtual scroll bounds.
+ *
+ * @param ignoreTasks the set of tasks to ignore in the relayout
+ */
+ void updateLayout(boolean boundScrollToNewMinMax, Task... ignoreTasks) {
+ // Keep track of the ingore tasks
+ ArraySet<Task> ignoreTasksSet = mTmpTaskSet;
+ ignoreTasksSet.clear();
+ ignoreTasksSet.ensureCapacity(ignoreTasks.length);
+ Collections.addAll(ignoreTasksSet, ignoreTasks);
+
// Compute the min and max scroll values
- mLayoutAlgorithm.update(mStack);
+ mLayoutAlgorithm.update(mStack, ignoreTasksSet);
// Update the freeform workspace
SystemServicesProxy ssp = Recents.getSystemServices();
@@ -623,24 +661,55 @@
*/
private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
final boolean requestViewFocus) {
+ return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, false);
+ }
+
+ /**
+ * Sets the focused task to the provided (bounded taskIndex).
+ *
+ * @return whether or not the stack will scroll as a part of this focus change
+ */
+ private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
+ final boolean requestViewFocus, final boolean showTimerIndicator) {
// Find the next task to focus
- int newFocusedTaskIndex = mStack.getStackTaskCount() > 0 ?
- Math.max(0, Math.min(mStack.getStackTaskCount() - 1, taskIndex)) : -1;
+ int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
+ Math.max(0, Math.min(mStack.getTaskCount() - 1, taskIndex)) : -1;
final Task newFocusedTask = (newFocusedTaskIndex != -1) ?
mStack.getStackTasks().get(newFocusedTaskIndex) : null;
// Reset the last focused task state if changed
if (mFocusedTask != null) {
resetFocusedTask(mFocusedTask);
+
+ // Cancel the timer indicator, if applicable
+ if (showTimerIndicator) {
+ final TaskView tv = getChildViewForTask(mFocusedTask);
+ if (tv != null) {
+ tv.getHeaderView().cancelFocusTimerIndicator();
+ }
+ }
}
boolean willScroll = false;
+
mFocusedTask = newFocusedTask;
+
if (newFocusedTask != null) {
+ // Start the timer indicator, if applicable
+ if (showTimerIndicator) {
+ final TaskView tv = getChildViewForTask(mFocusedTask);
+ if (tv != null) {
+ tv.getHeaderView().startFocusTimerIndicator();
+ } else {
+ // The view is null; set a flag for later
+ mStartTimerIndicator = true;
+ }
+ }
+
Runnable focusTaskRunnable = new Runnable() {
@Override
public void run() {
- TaskView tv = getChildViewForTask(newFocusedTask);
+ final TaskView tv = getChildViewForTask(newFocusedTask);
if (tv != null) {
tv.setFocusedState(true, requestViewFocus);
}
@@ -694,10 +763,28 @@
* @param animated determines whether to actually draw the highlight along with the change in
* focus.
* @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
- * happens
+ * happens.
*/
public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
boolean cancelWindowAnimations) {
+ setRelativeFocusedTask(forward, stackTasksOnly, animated, false, false);
+ }
+
+ /**
+ * Sets the focused task relative to the currently focused task.
+ *
+ * @param forward whether to go to the next task in the stack (along the curve) or the previous
+ * @param stackTasksOnly if set, will ensure that the traversal only goes along stack tasks, and
+ * if the currently focused task is not a stack task, will set the focus
+ * to the first visible stack task
+ * @param animated determines whether to actually draw the highlight along with the change in
+ * focus.
+ * @param cancelWindowAnimations if set, will attempt to cancel window animations if a scroll
+ * happens.
+ * @param showTimerIndicator determines whether or not to show an indicator for the task auto-advance.
+ */
+ public void setRelativeFocusedTask(boolean forward, boolean stackTasksOnly, boolean animated,
+ boolean cancelWindowAnimations, boolean showTimerIndicator) {
int newIndex = mStack.indexOfStackTask(mFocusedTask);
if (mFocusedTask != null) {
if (stackTasksOnly) {
@@ -721,7 +808,7 @@
} else {
// No restrictions, lets just move to the new task (looping forward/backwards if
// necessary)
- int taskCount = mStack.getStackTaskCount();
+ int taskCount = mStack.getTaskCount();
newIndex = (newIndex + (forward ? -1 : 1) + taskCount) % taskCount;
}
} else {
@@ -733,7 +820,7 @@
}
if (newIndex != -1) {
boolean willScroll = setFocusedTask(newIndex, true /* scrollToTask */,
- true /* requestViewFocus */);
+ true /* requestViewFocus */, showTimerIndicator);
if (willScroll && cancelWindowAnimations) {
// As we iterate to the next/previous task, cancel any current/lagging window
// transition animations
@@ -774,7 +861,7 @@
event.setToIndex(mStack.indexOfStackTask(frontMostTask.getTask()));
event.setContentDescription(frontMostTask.getTask().title);
}
- event.setItemCount(mStack.getStackTaskCount());
+ event.setItemCount(mStack.getTaskCount());
event.setScrollY(mStackScroller.mScroller.getCurrY());
event.setMaxScrollY(mStackScroller.progressToScrollRange(mLayoutAlgorithm.mMaxScrollP));
}
@@ -790,7 +877,7 @@
if (focusedTaskIndex > 0) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
- if (focusedTaskIndex < mStack.getStackTaskCount() - 1) {
+ if (focusedTaskIndex < mStack.getTaskCount() - 1) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
}
@@ -868,14 +955,18 @@
}
}
- /** Computes the stack and task rects */
- public void computeRects(Rect taskStackBounds) {
+ /**
+ * Computes the stack and task rects.
+ *
+ * @param ignoreTasks the set of tasks to ignore in the relayout
+ */
+ public void computeRects(Rect taskStackBounds, boolean boundScroll, Task... ignoreTasks) {
// Compute the rects in the stack algorithm
mLayoutAlgorithm.initialize(taskStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
// Update the scroll bounds
- updateLayout(false);
+ updateLayout(boundScroll, ignoreTasks);
}
/**
@@ -895,9 +986,19 @@
return mLayoutAlgorithm.computeStackVisibilityReport(mStack.getStackTasks());
}
+ /**
+ * Updates the expected task stack bounds for this stack view.
+ */
public void setTaskStackBounds(Rect taskStackBounds, Rect systemInsets) {
- mTaskStackBounds.set(taskStackBounds);
+ // We can get spurious measure passes with the old bounds when docking, and since we are
+ // using the current stack bounds during drag and drop, don't overwrite them until we
+ // actually get new bounds
+ if (!taskStackBounds.equals(mStableStackBounds)) {
+ mStableStackBounds.set(taskStackBounds);
+ mStackBounds.set(taskStackBounds);
+ }
mLayoutAlgorithm.setSystemInsets(systemInsets);
+ requestLayout();
}
/**
@@ -910,14 +1011,15 @@
int height = MeasureSpec.getSize(heightMeasureSpec);
// Compute our stack/task rects
- computeRects(mTaskStackBounds);
+ computeRects(mStackBounds, false);
// 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();
}
- bindTaskViewsWithStack();
+ mTmpTaskSet.clear();
+ bindTaskViewsWithStack(mTmpTaskSet);
// Measure each of the TaskViews
mTmpTaskViews.clear();
@@ -996,7 +1098,7 @@
// until after the enter-animation
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getStackTaskCount());
+ int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
if (focusedTaskIndex != -1) {
setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
false /* requestViewFocus */);
@@ -1011,14 +1113,9 @@
}
}
- public boolean isTransformedTouchPointInView(float x, float y, TaskView tv) {
- final float[] point = new float[2];
- point[0] = x;
- point[1] = y;
- transformPointToViewLocal(point, tv);
- x = point[0];
- y = point[1];
- return (0 <= x) && (x < tv.getWidth()) && (0 <= y) && (y < tv.getHeight());
+ public boolean isTouchPointInView(float x, float y, TaskView tv) {
+ return (tv.getLeft() <= x && x <= tv.getRight()) &&
+ (tv.getTop() <= y && y <= tv.getBottom());
}
@Override
@@ -1087,11 +1184,9 @@
// Get the stack scroll of the task to anchor to (since we are removing something, the
// front most task will be our anchor task)
- Task anchorTask = null;
+ Task anchorTask = mStack.getStackFrontMostTask();
float prevAnchorTaskScroll = 0;
- boolean pullStackForward = stack.getStackTaskCount() > 0;
- if (pullStackForward) {
- anchorTask = mStack.getStackFrontMostTask();
+ if (anchorTask != null) {
prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
}
@@ -1102,7 +1197,7 @@
// Since the max scroll progress is offset from the bottom of the stack, just scroll
// to ensure that the new front most task is now fully visible
mStackScroller.setStackScroll(mLayoutAlgorithm.mMaxScrollP);
- } else if (pullStackForward) {
+ } else if (anchorTask != null) {
// Otherwise, offset the scroll by the movement of the anchor task
float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
float stackScrollOffset = (anchorTaskScroll - prevAnchorTaskScroll);
@@ -1139,11 +1234,8 @@
}
// If there are no remaining tasks, then just close recents
- if (mStack.getStackTaskCount() == 0) {
- boolean shouldFinishActivity = (mStack.getStackTaskCount() == 0);
- if (shouldFinishActivity) {
- EventBus.getDefault().send(new AllTaskViewsDismissedEvent());
- }
+ if (mStack.getTaskCount() == 0) {
+ EventBus.getDefault().send(new AllTaskViewsDismissedEvent());
}
}
@@ -1210,6 +1302,11 @@
tv.setClipViewInStack(true);
if (mFocusedTask == task) {
tv.setFocusedState(true, false /* requestViewFocus */);
+ if (mStartTimerIndicator) {
+ // The timer indicator couldn't be started before, so start it now
+ tv.getHeaderView().startFocusTimerIndicator();
+ mStartTimerIndicator = false;
+ }
}
// Restore the action button visibility if it is the front most task view
@@ -1251,7 +1348,7 @@
public final void onBusEvent(PackagesChangedEvent event) {
// Compute which components need to be removed
- HashSet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
+ ArraySet<ComponentName> removedComponents = mStack.computeComponentsRemoved(
event.packageName, event.userId);
// For other tasks, just remove them directly if they no longer exist
@@ -1318,7 +1415,8 @@
}
public final void onBusEvent(FocusNextTaskViewEvent event) {
- setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */);
+ setRelativeFocusedTask(true, false /* stackTasksOnly */, true /* animated */, false,
+ event.showTimerIndicator);
}
public final void onBusEvent(FocusPreviousTaskViewEvent event) {
@@ -1328,6 +1426,9 @@
public final void onBusEvent(UserInteractionEvent event) {
// Poke the doze trigger on user interaction
mUIDozeTrigger.poke();
+ if (event.showTimerIndicator && mFocusedTask != null) {
+ getChildViewForTask(mFocusedTask).getHeaderView().cancelFocusTimerIndicator();
+ }
}
public final void onBusEvent(RecentsVisibilityChangedEvent event) {
@@ -1348,6 +1449,7 @@
mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
mTmpTransform, null);
mTmpTransform.scale = finalScale;
+ mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
updateTaskViewToTransform(event.taskView, mTmpTransform,
new TaskViewAnimation(DRAG_SCALE_DURATION, mFastOutSlowInInterpolator));
}
@@ -1361,7 +1463,23 @@
}
public final void onBusEvent(DragDropTargetChangedEvent event) {
- // TODO: Animate the freeform workspace background etc.
+ if (event.dropTarget instanceof TaskStack.DockState) {
+ // Calculate the new task stack bounds that matches the window size that Recents will
+ // have after the drop
+ final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
+ mStackBounds.set(dockState.getDockedTaskStackBounds(getMeasuredWidth(),
+ getMeasuredHeight(), mDividerSize, mLayoutAlgorithm.mSystemInsets,
+ getResources()));
+ computeRects(mStackBounds, true /* boundScroll */, event.task /* ignoreTask */);
+ updateTaskViewsToLayout(new TaskViewAnimation(250, mFastOutSlowInInterpolator),
+ event.task /* ignoreTask */);
+ } else {
+ // Restore the pre-drag task stack bounds
+ mStackBounds.set(mStableStackBounds);
+ computeRects(mStackBounds, true /* boundScroll */);
+ updateTaskViewsToLayout(new TaskViewAnimation(250, mFastOutSlowInInterpolator),
+ event.task /* ignoreTask */);
+ }
}
public final void onBusEvent(final DragEndEvent event) {
@@ -1420,7 +1538,7 @@
}
public final void onBusEvent(StackViewScrolledEvent event) {
- mLayoutAlgorithm.updateFocusStateOnScroll(event.yMovement);
+ mLayoutAlgorithm.updateFocusStateOnScroll(event.yMovement.value);
}
public final void onBusEvent(IterateRecentsEvent event) {
@@ -1433,7 +1551,7 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
mEnterAnimationComplete = true;
- if (mStack.getStackTaskCount() > 0) {
+ if (mStack.getTaskCount() > 0) {
// Start the task enter animations
mAnimationHelper.startEnterAnimation(event.getAnimationTrigger());
@@ -1505,7 +1623,7 @@
Utilities.cancelAnimationWithoutCallbacks(mFreeformWorkspaceBackgroundAnimator);
mFreeformWorkspaceBackgroundAnimator = ObjectAnimator.ofInt(mFreeformWorkspaceBackground,
- DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
+ Utilities.DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
mFreeformWorkspaceBackgroundAnimator.setDuration(duration);
mFreeformWorkspaceBackgroundAnimator.setInterpolator(interpolator);
mFreeformWorkspaceBackgroundAnimator.start();
@@ -1546,4 +1664,14 @@
private boolean shouldShowHistoryButton() {
return !mStack.getHistoricalTasks().isEmpty();
}
+
+ /**
+ * Reads current system flags related to accessibility and screen pinning.
+ */
+ private void readSystemFlags() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
+ mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
+ Settings.System.LOCK_TO_APP_ENABLED) != 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 c748efc..a0bb0ef 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.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -29,6 +30,7 @@
import android.view.ViewParent;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.SwipeHelper;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.events.EventBus;
@@ -71,6 +73,7 @@
// Used to calculate when a tap is outside a task view rectangle.
final int mWindowTouchSlop;
+ private final StackViewScrolledEvent mStackViewScrolledEvent = new StackViewScrolledEvent();
SwipeHelper mSwipeHelper;
boolean mInterceptedBySwipeHelper;
@@ -79,19 +82,21 @@
Resources res = context.getResources();
ViewConfiguration configuration = ViewConfiguration.get(context);
mContext = context;
+ mSv = sv;
+ mScroller = scroller;
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
mScrollTouchSlop = configuration.getScaledTouchSlop();
mWindowTouchSlop = configuration.getScaledWindowTouchSlop();
- mSv = sv;
- mScroller = scroller;
mFlingAnimUtils = new FlingAnimationUtils(context, 0.2f);
-
- float densityScale = res.getDisplayMetrics().density;
mOverscrollSize = res.getDimensionPixelSize(R.dimen.recents_stack_overscroll);
- mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale,
- configuration.getScaledPagingTouchSlop());
- mSwipeHelper.setMinAlpha(1f);
+ mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, context) {
+ @Override
+ protected float getSize(View v) {
+ return mSv.getWidth();
+ }
+ };
+ mSwipeHelper.setDisableHardwareLayers(true);
}
/** Velocity tracker helpers */
@@ -116,7 +121,7 @@
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
if (tv.getVisibility() == View.VISIBLE) {
- if (mSv.isTransformedTouchPointInView(x, y, tv)) {
+ if (mSv.isTouchPointInView(x, y, tv)) {
return tv;
}
}
@@ -207,7 +212,8 @@
if (DEBUG) {
Log.d(TAG, "scroll: " + curScrollP);
}
- EventBus.getDefault().send(new StackViewScrolledEvent(y - mLastY));
+ mStackViewScrolledEvent.updateY(y - mLastY);
+ EventBus.getDefault().send(mStackViewScrolledEvent);
}
mLastY = y;
@@ -343,7 +349,7 @@
@Override
public void onBeginDrag(View v) {
TaskView tv = (TaskView) v;
- mSwipeHelper.setSnapBackTranslationX(tv.getTranslationX());
+
// Disable clipping with the stack while we are swiping
tv.setClipViewInStack(false);
// Disallow touch events from this task view
@@ -356,8 +362,8 @@
}
@Override
- public void onSwipeChanged(View v, float delta) {
- // Do nothing
+ public boolean updateSwipeProgress(View v, boolean dismissable, float swipeProgress) {
+ return true;
}
@Override
@@ -375,7 +381,7 @@
}
@Override
- public void onSnapBackCompleted(View v) {
+ public void onChildSnappedBack(View v) {
TaskView tv = (TaskView) v;
// Re-enable clipping with the stack
tv.setClipViewInStack(true);
@@ -387,4 +393,20 @@
public void onDragCancelled(View v) {
// Do nothing
}
+
+ @Override
+ public View getChildContentView(View v) {
+ return v;
+ }
+
+ @Override
+ public boolean isAntiFalsingNeeded() {
+ return false;
+ }
+
+ @Override
+ public float getFalsingThresholdFactor() {
+ return 0;
+ }
+
}
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 bc441b2..db4db63 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -257,7 +257,9 @@
mTmpAnimators.clear();
toTransform.applyToTaskView(this, mTmpAnimators, toAnimation, !config.fakeShadows);
if (toAnimation.isImmediate()) {
- setTaskProgress(toTransform.p);
+ if (Float.compare(getTaskProgress(), toTransform.p) != 0) {
+ setTaskProgress(toTransform.p);
+ }
if (toAnimation.listener != null) {
toAnimation.listener.onAnimationEnd(null);
}
@@ -286,7 +288,7 @@
mActionButtonView.setScaleX(1f);
mActionButtonView.setScaleY(1f);
- mActionButtonView.setAlpha(1f);
+ mActionButtonView.setAlpha(0f);
mActionButtonView.setTranslationZ(mActionButtonTranslationZ);
}
@@ -360,6 +362,10 @@
updateDimFromTaskProgress();
}
+ public TaskViewHeader getHeaderView() {
+ return mHeaderView;
+ }
+
/** Returns the current task progress. */
public float getTaskProgress() {
return mTaskProgress;
@@ -455,7 +461,6 @@
.scaleY(1f)
.setDuration(fadeInDuration)
.setInterpolator(PhoneStatusBar.ALPHA_IN)
- .withLayer()
.start();
}
} else {
@@ -494,7 +499,6 @@
mActionButtonView.setVisibility(View.INVISIBLE);
}
})
- .withLayer()
.start();
}
} else {
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 6a47424..e7717ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -23,17 +23,20 @@
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.support.v4.graphics.ColorUtils;
+import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
@@ -51,12 +54,12 @@
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
/* The task bar view */
public class TaskViewHeader extends FrameLayout
implements View.OnClickListener, View.OnLongClickListener {
private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.125f;
+ private static final long FOCUS_INDICATOR_INTERVAL_MS = 30;
/**
* A color drawable that draws a slight highlight at the top to help it stand out.
@@ -124,6 +127,7 @@
ImageView mIconView;
TextView mTitleView;
int mMoveTaskTargetStackId = INVALID_STACK_ID;
+ ProgressBar mFocusTimerIndicator;
// Header drawables
Rect mTaskViewRect = new Rect();
@@ -132,9 +136,12 @@
float mDimAlpha;
Drawable mLightDismissDrawable;
Drawable mDarkDismissDrawable;
+ Drawable mLightFreeformIcon;
+ Drawable mDarkFreeformIcon;
+ Drawable mLightFullscreenIcon;
+ Drawable mDarkFullscreenIcon;
int mTaskBarViewLightTextColor;
int mTaskBarViewDarkTextColor;
- String mDismissContentDescription;
// Header background
private HighlightColorDrawable mBackground;
@@ -145,6 +152,10 @@
Interpolator mFastOutSlowInInterpolator;
Interpolator mFastOutLinearInInterpolator;
+ long mFocusIndicatorProgress;
+ private CountDownTimer mFocusTimerCountDown;
+ long mFocusTimerDuration;
+
public TaskViewHeader(Context context) {
this(context, null);
}
@@ -165,12 +176,15 @@
Resources res = context.getResources();
mLightDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_light);
mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark);
- mDismissContentDescription = context.getString(
- R.string.accessibility_recents_item_will_be_dismissed);
mCornerRadius = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius);
mHighlightHeight = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight);
mTaskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color);
mTaskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color);
+ mLightFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_light);
+ mDarkFreeformIcon = context.getDrawable(R.drawable.recents_move_task_freeform_dark);
+ mLightFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_light);
+ mDarkFullscreenIcon = context.getDrawable(R.drawable.recents_move_task_fullscreen_dark);
+
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -182,6 +196,7 @@
setBackground(mBackground);
mDimLayerPaint.setColor(Color.argb(255, 0, 0, 0));
mDimLayerPaint.setAntiAlias(true);
+ mFocusTimerDuration = res.getInteger(R.integer.recents_auto_advance_duration);
}
@Override
@@ -193,6 +208,7 @@
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
mDismissButton.setOnClickListener(this);
mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
+ mFocusTimerIndicator = (ProgressBar) findViewById(R.id.focus_timer_indicator);
// Hide the backgrounds if they are ripple drawables
if (mIconView.getBackground() instanceof RippleDrawable) {
@@ -213,7 +229,9 @@
mTaskViewRect.set(0, 0, width, height);
boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE;
int appIconWidth = mIconView.getMeasuredWidth();
- int activityDescWidth = mTitleView.getMeasuredWidth();
+ int activityDescWidth = (mTask != null)
+ ? (int) mTitleView.getPaint().measureText(mTask.title)
+ : mTitleView.getMeasuredWidth();
int dismissIconWidth = mDismissButton.getMeasuredWidth();
int moveTaskIconWidth = mMoveTaskButton.getVisibility() == View.VISIBLE
? mMoveTaskButton.getMeasuredWidth()
@@ -268,6 +286,41 @@
mCornerRadius, mCornerRadius, mDimLayerPaint);
}
+ /** Starts the focus timer. */
+ public void startFocusTimerIndicator() {
+ mFocusTimerIndicator.setVisibility(View.VISIBLE);
+ mFocusTimerIndicator.setMax((int) mFocusTimerDuration);
+ if (mFocusTimerCountDown == null) {
+ mFocusTimerCountDown = new CountDownTimer(mFocusTimerDuration,
+ FOCUS_INDICATOR_INTERVAL_MS) {
+ public void onTick(long millisUntilFinished) {
+ mFocusTimerIndicator.setProgress((int) millisUntilFinished);
+ }
+
+ public void onFinish() {
+ mFocusTimerIndicator.setProgress((int) mFocusTimerDuration);
+ }
+ }.start();
+ } else {
+ mFocusTimerCountDown.start();
+ }
+ }
+
+ /** Cancels the focus timer. */
+ public void cancelFocusTimerIndicator() {
+ if (mFocusTimerCountDown != null && mFocusTimerIndicator != null) {
+ mFocusTimerCountDown.cancel();
+ mFocusTimerIndicator.setProgress(0);
+ mFocusTimerIndicator.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ /** Returns the secondary color for a primary color. */
+ int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
+ int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
+ return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
+ }
+
/**
* Sets the dim alpha, only used when we are not using hardware layers.
* (see RecentsConfiguration.useHardwareLayers)
@@ -307,22 +360,21 @@
mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
mLightDismissDrawable : mDarkDismissDrawable);
- mDismissButton.setContentDescription(String.format(mDismissContentDescription,
- t.contentDescription));
+ mDismissButton.setContentDescription(t.dismissDescription);
// When freeform workspaces are enabled, then update the move-task button depending on the
// current task
if (ssp.hasFreeformWorkspaceSupport()) {
if (t.isFreeformTask()) {
mMoveTaskTargetStackId = FULLSCREEN_WORKSPACE_STACK_ID;
- mMoveTaskButton.setImageResource(t.useLightOnPrimaryColor
- ? R.drawable.recents_move_task_fullscreen_light
- : R.drawable.recents_move_task_fullscreen_dark);
+ mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
+ ? mLightFullscreenIcon
+ : mDarkFullscreenIcon);
} else {
mMoveTaskTargetStackId = FREEFORM_WORKSPACE_STACK_ID;
- mMoveTaskButton.setImageResource(t.useLightOnPrimaryColor
- ? R.drawable.recents_move_task_freeform_light
- : R.drawable.recents_move_task_freeform_dark);
+ mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor
+ ? mLightFreeformIcon
+ : mDarkFreeformIcon);
}
if (mMoveTaskButton.getVisibility() != View.VISIBLE) {
mMoveTaskButton.setVisibility(View.VISIBLE);
@@ -330,6 +382,11 @@
mMoveTaskButton.setOnClickListener(this);
}
+ mFocusTimerIndicator.getProgressDrawable()
+ .setColorFilter(
+ getSecondaryColor(t.colorPrimary, t.useLightOnPrimaryColor),
+ PorterDuff.Mode.SRC_IN);
+
// In accessibility, a single click on the focused app info button will show it
if (ssp.isTouchExplorationEnabled()) {
mIconView.setOnClickListener(this);
@@ -359,7 +416,10 @@
}
}
- /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+ /**
+ * Mark this task view that the user does has not interacted with the stack after a certain
+ * time.
+ */
void setNoUserInteractionState() {
if (mDismissButton.getVisibility() != View.VISIBLE) {
mDismissButton.animate().cancel();
@@ -368,7 +428,10 @@
}
}
- /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
+ /**
+ * Resets the state tracking that the user has not interacted with the stack after a certain
+ * time.
+ */
void resetNoUserInteractionState() {
mDismissButton.setVisibility(View.INVISIBLE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 824d10a..c16703e8 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -363,21 +363,6 @@
return mStartPosition + touchY - mStartY;
}
- private int invertDockSide(int dockSide) {
- switch (dockSide) {
- case WindowManager.DOCKED_LEFT:
- return WindowManager.DOCKED_RIGHT;
- case WindowManager.DOCKED_TOP:
- return WindowManager.DOCKED_BOTTOM;
- case WindowManager.DOCKED_RIGHT:
- return WindowManager.DOCKED_LEFT;
- case WindowManager.DOCKED_BOTTOM:
- return WindowManager.DOCKED_TOP;
- default:
- return WindowManager.DOCKED_INVALID;
- }
- }
-
private void alignTopLeft(Rect containingRect, Rect rect) {
int width = rect.width();
int height = rect.height();
@@ -409,8 +394,9 @@
mLastResizeRect.set(mDockedRect);
if (taskPosition != TASK_POSITION_SAME) {
- calculateBoundsForPosition(position, invertDockSide(mDockSide), mOtherRect);
- int dockSideInverted = invertDockSide(mDockSide);
+ calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+ mOtherRect);
+ int dockSideInverted = DockedDividerUtils.invertDockSide(mDockSide);
int taskPositionDocked =
restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget);
int taskPositionOther =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 3e0ea90..b36fb7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -20,50 +20,92 @@
import android.app.Dialog;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
-import android.view.Gravity;
+import android.os.Handler;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import android.view.WindowManager.KeyboardShortcutsReceiver;
import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+
+import java.util.List;
+
+import static android.content.Context.LAYOUT_INFLATER_SERVICE;
+import static android.graphics.Color.TRANSPARENT;
+import static android.view.Gravity.TOP;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
/**
* Contains functionality for handling keyboard shortcuts.
*/
public class KeyboardShortcuts {
+ private static final String TAG = "KeyboardShortcuts";
+
private Dialog mKeyboardShortcutsDialog;
public KeyboardShortcuts() {}
- public void toggleKeyboardShortcuts(Context context) {
+ public void toggleKeyboardShortcuts(final Context context) {
if (mKeyboardShortcutsDialog == null) {
- // Create dialog.
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
- LayoutInflater inflater = (LayoutInflater) context.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- final View keyboardShortcutsView = inflater.inflate(
- R.layout.keyboard_shortcuts_view, null);
-
- populateKeyboardShortcuts(keyboardShortcutsView.findViewById(
- R.id.keyboard_shortcuts_wrapper));
- dialogBuilder.setView(keyboardShortcutsView);
- mKeyboardShortcutsDialog = dialogBuilder.create();
- mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
-
- // Setup window.
- Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
- keyboardShortcutsWindow.setType(
- WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
- keyboardShortcutsWindow.setBackgroundDrawable(
- new ColorDrawable(android.graphics.Color.TRANSPARENT));
- keyboardShortcutsWindow.setGravity(Gravity.TOP);
- mKeyboardShortcutsDialog.show();
+ Recents.getSystemServices().requestKeyboardShortcuts(context,
+ new KeyboardShortcutsReceiver() {
+ @Override
+ public void onKeyboardShortcutsReceived(
+ final List<KeyboardShortcutGroup> result) {
+ KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup(
+ context.getString(R.string.keyboard_shortcut_group_system));
+ systemGroup.addItem(new KeyboardShortcutInfo(
+ context.getString(R.string.keyboard_shortcut_group_system_home),
+ '\u2386', KeyEvent.META_META_ON));
+ systemGroup.addItem(new KeyboardShortcutInfo(
+ context.getString(R.string.keyboard_shortcut_group_system_back),
+ '\u007F', KeyEvent.META_META_ON));
+ systemGroup.addItem(new KeyboardShortcutInfo(
+ context.getString(R.string.keyboard_shortcut_group_system_recents),
+ '\u0009', KeyEvent.META_ALT_ON));
+ result.add(systemGroup);
+ Log.i(TAG, "Keyboard shortcuts received: " + String.valueOf(result));
+ showKeyboardShortcutsDialog(context);
+ }
+ });
} else {
dismissKeyboardShortcutsDialog();
}
}
+ private void showKeyboardShortcutsDialog(Context context) {
+ // Create dialog.
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+ LAYOUT_INFLATER_SERVICE);
+ final View keyboardShortcutsView = inflater.inflate(
+ R.layout.keyboard_shortcuts_view, null);
+
+ populateKeyboardShortcuts(keyboardShortcutsView.findViewById(
+ R.id.keyboard_shortcuts_wrapper));
+ dialogBuilder.setView(keyboardShortcutsView);
+ mKeyboardShortcutsDialog = dialogBuilder.create();
+ mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true);
+
+ // Setup window.
+ Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow();
+ keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG);
+ keyboardShortcutsWindow.setBackgroundDrawable(
+ new ColorDrawable(TRANSPARENT));
+ keyboardShortcutsWindow.setGravity(TOP);
+ keyboardShortcutsView.post(new Runnable() {
+ public void run() {
+ mKeyboardShortcutsDialog.show();
+ }
+ });
+ }
+
public void dismissKeyboardShortcutsDialog() {
if (mKeyboardShortcutsDialog != null) {
mKeyboardShortcutsDialog.dismiss();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 99436a1..347ba3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -41,13 +41,13 @@
*/
public class KeyguardBouncer {
- private Context mContext;
- private ViewMediatorCallback mCallback;
- private LockPatternUtils mLockPatternUtils;
- private ViewGroup mContainer;
+ protected Context mContext;
+ protected ViewMediatorCallback mCallback;
+ protected LockPatternUtils mLockPatternUtils;
+ protected ViewGroup mContainer;
private StatusBarWindowManager mWindowManager;
- private KeyguardHostView mKeyguardView;
- private ViewGroup mRoot;
+ protected KeyguardHostView mKeyguardView;
+ protected ViewGroup mRoot;
private boolean mShowingSoon;
private int mBouncerPromptReason;
private FalsingManager mFalsingManager;
@@ -134,7 +134,7 @@
public void hide(boolean destroyView) {
mFalsingManager.onBouncerHidden();
cancelShowRunnable();
- if (mKeyguardView != null) {
+ if (mKeyguardView != null) {
mKeyguardView.cancelDismissAction();
mKeyguardView.cleanUp();
}
@@ -184,13 +184,13 @@
mBouncerPromptReason = mCallback.getBouncerPromptReason();
}
- private void ensureView() {
+ protected void ensureView() {
if (mRoot == null) {
inflateView();
}
}
- private void inflateView() {
+ protected void inflateView() {
removeView();
mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
mKeyguardView = (KeyguardHostView) mRoot.findViewById(R.id.keyguard_host_view);
@@ -201,7 +201,7 @@
mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
}
- private void removeView() {
+ protected void removeView() {
if (mRoot != null && mRoot.getParent() == mContainer) {
mContainer.removeView(mRoot);
mRoot = null;
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 50e88d3..78497ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -82,6 +82,7 @@
import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewStub;
import android.view.WindowManager;
@@ -279,9 +280,9 @@
VolumeComponent mVolumeComponent;
KeyguardUserSwitcher mKeyguardUserSwitcher;
FlashlightController mFlashlightController;
- UserSwitcherController mUserSwitcherController;
+ protected UserSwitcherController mUserSwitcherController;
NextAlarmController mNextAlarmController;
- KeyguardMonitor mKeyguardMonitor;
+ protected KeyguardMonitor mKeyguardMonitor;
BrightnessMirrorController mBrightnessMirrorController;
AccessibilityController mAccessibilityController;
FullscreenUserSwitcher mFullscreenUserSwitcher;
@@ -295,7 +296,7 @@
StatusBarWindowView mStatusBarWindow;
PhoneStatusBarView mStatusBarView;
private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
- private StatusBarWindowManager mStatusBarWindowManager;
+ protected StatusBarWindowManager mStatusBarWindowManager;
private UnlockMethodCache mUnlockMethodCache;
private DozeServiceHost mDozeServiceHost;
private boolean mWakeUpComingFromTouch;
@@ -422,8 +423,8 @@
private int mMaxKeyguardNotifications;
private ViewMediatorCallback mKeyguardViewMediatorCallback;
- private ScrimController mScrimController;
- private DozeScrimController mDozeScrimController;
+ protected ScrimController mScrimController;
+ protected DozeScrimController mDozeScrimController;
private final Runnable mAutohide = new Runnable() {
@Override
@@ -437,7 +438,7 @@
private boolean mWaitingForKeyguardExit;
private boolean mDozing;
private boolean mDozingRequested;
- private boolean mScrimSrcModeEnabled;
+ protected boolean mScrimSrcModeEnabled;
private Interpolator mLinearInterpolator = new LinearInterpolator();
private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
@@ -866,7 +867,7 @@
mKeyguardMonitor = new KeyguardMonitor(mContext);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor,
- mHandler);
+ mHandler, this);
if (mUserSwitcherController.useFullscreenUserSwitcher()) {
mFullscreenUserSwitcher = new FullscreenUserSwitcher(this, mUserSwitcherController,
(ViewStub) mStatusBarWindow.findViewById(
@@ -1072,13 +1073,13 @@
}
}
- private void startKeyguard() {
+ protected void startKeyguard() {
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
mFingerprintUnlockController = new FingerprintUnlockController(mContext,
mStatusBarWindowManager, mDozeScrimController, keyguardViewMediator,
mScrimController, this);
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
- mStatusBarWindow, mStatusBarWindowManager, mScrimController,
+ getBouncerContainer(), mStatusBarWindowManager, mScrimController,
mFingerprintUnlockController);
mKeyguardIndicationController.setStatusBarKeyguardViewManager(
mStatusBarKeyguardViewManager);
@@ -1096,6 +1097,10 @@
return mStatusBarWindow;
}
+ protected ViewGroup getBouncerContainer() {
+ return mStatusBarWindow;
+ }
+
public int getStatusBarHeight() {
if (mNaturalBarHeight < 0) {
final Resources res = mContext.getResources();
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 336b208..29ad5d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -27,6 +27,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.view.View;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.external.CustomTile;
@@ -99,6 +100,7 @@
private final TileServices mServices;
private final List<Callback> mCallbacks = new ArrayList<>();
+ private View mHeader;
public QSTileHost(Context context, PhoneStatusBar statusBar,
BluetoothController bluetooth, LocationController location,
@@ -135,6 +137,10 @@
TunerService.get(mContext).addTunable(this, TILES_SETTING);
}
+ public void setHeaderView(View view) {
+ mHeader = view;
+ }
+
public PhoneStatusBar getPhoneStatusBar() {
return mStatusBar;
}
@@ -173,6 +179,11 @@
// already logged
}
+ public void animateExpandQS() {
+ // TODO: Better path to animated panel expansion.
+ mHeader.performClick();
+ }
+
@Override
public void collapsePanels() {
mStatusBar.postAnimateCollapsePanels();
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 3692aee..5b44f0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -197,23 +197,16 @@
}
}
- public void setupHost(QSTileHost host) {
- final QSTileHost myHost = new QSTileHost(host.getContext(), host.getPhoneStatusBar(),
- host.getBluetoothController(), host.getLocationController(),
- host.getRotationLockController(), host.getNetworkController(),
- host.getZenModeController(), host.getHotspotController(),
- host.getCastController(), host.getFlashlightController(),
- host.getUserSwitcherController(), host.getUserInfoController(),
- host.getKeyguardMonitor(), host.getSecurityController(),
- host.getBatteryController(), host.getIconController());
+ public void setupHost(final QSTileHost host) {
+ host.setHeaderView(this);
mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
- mHeaderQsPanel.setHost(myHost);
+ mHeaderQsPanel.setHost(host);
mHeaderQsPanel.setMaxTiles(5);
- mHeaderQsPanel.setTiles(myHost.getTiles());
- myHost.addCallback(new QSTile.Host.Callback() {
+ mHeaderQsPanel.setTiles(host.getTiles());
+ host.addCallback(new QSTile.Host.Callback() {
@Override
public void onTilesChanged() {
- mHeaderQsPanel.setTiles(myHost.getTiles());
+ mHeaderQsPanel.setTiles(host.getTiles());
}
});
}
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 f14f0d4..7a05b8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -30,6 +30,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.RemoteInputController;
@@ -54,11 +55,11 @@
private static String TAG = "StatusBarKeyguardViewManager";
- private final Context mContext;
+ protected final Context mContext;
- private LockPatternUtils mLockPatternUtils;
- private ViewMediatorCallback mViewMediatorCallback;
- private PhoneStatusBar mPhoneStatusBar;
+ protected LockPatternUtils mLockPatternUtils;
+ protected ViewMediatorCallback mViewMediatorCallback;
+ protected PhoneStatusBar mPhoneStatusBar;
private ScrimController mScrimController;
private FingerprintUnlockController mFingerprintUnlockController;
@@ -67,17 +68,17 @@
private boolean mDeviceInteractive = false;
private boolean mScreenTurnedOn;
- private KeyguardBouncer mBouncer;
- private boolean mShowing;
- private boolean mOccluded;
- private boolean mRemoteInputActive;
+ protected KeyguardBouncer mBouncer;
+ protected boolean mShowing;
+ protected boolean mOccluded;
+ protected boolean mRemoteInputActive;
- private boolean mFirstUpdate = true;
- private boolean mLastShowing;
- private boolean mLastOccluded;
+ protected boolean mFirstUpdate = true;
+ protected boolean mLastShowing;
+ protected boolean mLastOccluded;
private boolean mLastBouncerShowing;
private boolean mLastBouncerDismissible;
- private boolean mLastRemoteInputActive;
+ protected boolean mLastRemoteInputActive;
private OnDismissAction mAfterKeyguardGoneAction;
private boolean mDeviceWillWakeUp;
@@ -99,8 +100,8 @@
mStatusBarWindowManager = statusBarWindowManager;
mScrimController = scrimController;
mFingerprintUnlockController = fingerprintUnlockController;
- mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
- mStatusBarWindowManager, container);
+ mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
+ mViewMediatorCallback, mLockPatternUtils, mStatusBarWindowManager, container);
}
/**
@@ -118,7 +119,7 @@
* Shows the notification keyguard or the bouncer depending on
* {@link KeyguardBouncer#needsFullscreenBouncer()}.
*/
- private void showBouncerOrKeyguard() {
+ protected void showBouncerOrKeyguard() {
if (mBouncer.needsFullscreenBouncer()) {
// The keyguard might be showing (already). So we need to hide it.
@@ -433,7 +434,7 @@
}
};
- private void updateStates() {
+ protected void updateStates() {
int vis = mContainer.getSystemUiVisibility();
boolean showing = mShowing;
boolean occluded = mOccluded;
@@ -451,9 +452,8 @@
}
}
- boolean navBarVisible = (!(showing && !occluded) || bouncerShowing || remoteInputActive);
- boolean lastNavBarVisible = (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing
- || mLastRemoteInputActive);
+ boolean navBarVisible = isNavBarVisible();
+ boolean lastNavBarVisible = getLastNavBarVisible();
if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
if (mPhoneStatusBar.getNavigationBarView() != null) {
if (navBarVisible) {
@@ -495,6 +495,20 @@
mPhoneStatusBar.onKeyguardViewManagerStatesUpdated();
}
+ /**
+ * @return Whether the navigation bar should be made visible based on the current state.
+ */
+ protected boolean isNavBarVisible() {
+ return !(mShowing && !mOccluded) || mBouncer.isShowing() || mRemoteInputActive;
+ }
+
+ /**
+ * @return Whether the navigation bar was made visible based on the last known state.
+ */
+ protected boolean getLastNavBarVisible() {
+ return !(mLastShowing && !mLastOccluded) || mLastBouncerShowing || mLastRemoteInputActive;
+ }
+
public boolean onMenuPressed() {
return mBouncer.onMenuPressed();
}
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 9d2f0de..382de19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -134,6 +134,12 @@
mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
+
+ if (state.remoteInputActive) {
+ mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
+ } else {
+ mLpChanged.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ }
}
private void applyHeight(State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
index 101a5f3..4f33d82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -23,6 +23,9 @@
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
@@ -41,6 +44,7 @@
private float mFramePadding;
private Bitmap mBitmap;
private Drawable mDrawable;
+ private boolean mIsDisabled;
private final Paint mFramePaint = new Paint();
private final Paint mBitmapPaint = new Paint();
@@ -239,4 +243,28 @@
mDrawable.setState(getDrawableState());
}
}
+
+ public void setDisabled(boolean disabled) {
+ if (mIsDisabled == disabled) {
+ return;
+ }
+ mIsDisabled = disabled;
+ int disabledColor = getContext().getColor(R.color.qs_tile_disabled_color);
+ PorterDuffColorFilter filter = new PorterDuffColorFilter(disabledColor,
+ PorterDuff.Mode.SRC_ATOP);
+ if (mBitmap != null) {
+ if (disabled) {
+ mBitmapPaint.setColorFilter(filter);
+ } else {
+ mBitmapPaint.setColorFilter(null);
+ }
+ } else if (mDrawable != null) {
+ if (disabled) {
+ mDrawable.setColorFilter(filter);
+ } else {
+ mDrawable.setColorFilter(null);
+ }
+ }
+ invalidate();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index b65bf43..ad8e3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -440,6 +440,7 @@
+ " dataState=" + state.getDataRegState());
}
mServiceState = state;
+ mDataNetType = state.getDataNetworkType();
updateTelephony();
}
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 65053f3..61a9851 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -26,6 +26,7 @@
import android.app.RemoteInput;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Editable;
@@ -174,8 +175,8 @@
}
public void focus() {
- mEditText.setInnerFocusable(true);
mController.addRemoteInput(mEntry);
+ mEditText.setInnerFocusable(true);
mEditText.mShowImeOnInputConnection = true;
mEditText.setText(mEntry.remoteInputText);
mEditText.setSelection(mEditText.getText().length());
@@ -257,6 +258,20 @@
}
@Override
+ public boolean requestRectangleOnScreen(Rect r) {
+ r.top = mScrollY;
+ r.bottom = mScrollY + (mBottom - mTop);
+ return super.requestRectangleOnScreen(r);
+ }
+
+ @Override
+ public void getFocusedRect(Rect r) {
+ super.getFocusedRect(r);
+ r.top = mScrollY;
+ r.bottom = mScrollY + (mBottom - mTop);
+ }
+
+ @Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
defocusIfNeeded();
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 f3a3554..05d9626 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -22,7 +22,9 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -33,6 +35,7 @@
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -48,11 +51,13 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.UserIcons;
+import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.BitmapHelper;
import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.statusbar.phone.ActivityStarter;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.io.FileDescriptor;
@@ -61,6 +66,8 @@
import java.util.ArrayList;
import java.util.List;
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
/**
* Keeps a list of all users on the device for user switching.
*/
@@ -88,6 +95,7 @@
= new GuestResumeSessionReceiver();
private final KeyguardMonitor mKeyguardMonitor;
private final Handler mHandler;
+ private final ActivityStarter mActivityStarter;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
private Dialog mExitGuestDialog;
@@ -99,11 +107,12 @@
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
- Handler handler) {
+ Handler handler, ActivityStarter activityStarter) {
mContext = context;
mGuestResumeSessionReceiver.register(context);
mKeyguardMonitor = keyguardMonitor;
mHandler = handler;
+ mActivityStarter = activityStarter;
mUserManager = UserManager.get(context);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
@@ -206,25 +215,23 @@
}
}
- boolean systemCanCreateUsers = !mUserManager.hasUserRestriction(
- UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
boolean currentUserCanCreateUsers = currentUserInfo != null
&& (currentUserInfo.isAdmin()
- || currentUserInfo.id == UserHandle.USER_SYSTEM)
- && systemCanCreateUsers;
- boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked;
- boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
+ || currentUserInfo.id == UserHandle.USER_SYSTEM);
+ boolean canCreateGuest = (currentUserCanCreateUsers || addUsersWhenLocked)
&& guestRecord == null;
- boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
+ boolean canCreateUser = (currentUserCanCreateUsers || addUsersWhenLocked)
&& mUserManager.canAddMoreUsers();
boolean createIsRestricted = !addUsersWhenLocked;
if (!mSimpleUserSwitcher) {
if (guestRecord == null) {
if (canCreateGuest) {
- records.add(new UserRecord(null /* info */, null /* picture */,
+ guestRecord = new UserRecord(null /* info */, null /* picture */,
true /* isGuest */, false /* isCurrent */,
- false /* isAddUser */, createIsRestricted));
+ false /* isAddUser */, createIsRestricted);
+ checkIfAddUserDisallowed(guestRecord);
+ records.add(guestRecord);
}
} else {
int index = guestRecord.isCurrent ? 0 : records.size();
@@ -233,9 +240,11 @@
}
if (!mSimpleUserSwitcher && canCreateUser) {
- records.add(new UserRecord(null /* info */, null /* picture */,
+ UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
- createIsRestricted));
+ createIsRestricted);
+ checkIfAddUserDisallowed(addUserRecord);
+ records.add(addUserRecord);
}
return records;
@@ -289,6 +298,14 @@
return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher);
}
+ public void logoutCurrentUser() {
+ int currentUser = ActivityManager.getCurrentUser();
+ if (currentUser != UserHandle.USER_SYSTEM) {
+ switchToUserId(UserHandle.USER_SYSTEM);
+ stopUserId(currentUser);
+ }
+ }
+
public void removeUserId(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
Log.w(TAG, "User " + userId + " could not removed.");
@@ -331,6 +348,19 @@
switchToUserId(id);
}
+ public void switchTo(int userId) {
+ final int count = mUsers.size();
+ for (int i = 0; i < count; ++i) {
+ UserRecord record = mUsers.get(i);
+ if (record.info != null && record.info.id == userId) {
+ switchTo(record);
+ return;
+ }
+ }
+
+ Log.e(TAG, "Couldn't switch to user, id=" + userId);
+ }
+
private void switchToUserId(int id) {
try {
pauseRefreshUsers();
@@ -395,11 +425,7 @@
}
return;
} else if (ACTION_LOGOUT_USER.equals(intent.getAction())) {
- int currentUser = ActivityManager.getCurrentUser();
- if (currentUser != UserHandle.USER_SYSTEM) {
- switchToUserId(UserHandle.USER_SYSTEM);
- stopUserId(currentUser);
- }
+ logoutCurrentUser();
} else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
mExitGuestDialog.cancel();
@@ -594,6 +620,22 @@
}
}
+ private void checkIfAddUserDisallowed(UserRecord record) {
+ EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+ UserManager.DISALLOW_ADD_USER, UserHandle.myUserId());
+ if (admin != null) {
+ record.isDisabledByAdmin = true;
+ record.enforcedAdmin = admin;
+ } else {
+ record.isDisabledByAdmin = false;
+ record.enforcedAdmin = null;
+ }
+ }
+
+ public void startActivity(Intent intent) {
+ mActivityStarter.startActivity(intent, true);
+ }
+
public static final class UserRecord {
public final UserInfo info;
public final Bitmap picture;
@@ -602,6 +644,8 @@
public final boolean isAddUser;
/** If true, the record is only visible to the owner and only when unlocked. */
public final boolean isRestricted;
+ public boolean isDisabledByAdmin;
+ public EnforcedAdmin enforcedAdmin;
public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
boolean isAddUser, boolean isRestricted) {
@@ -634,6 +678,10 @@
if (isCurrent) sb.append(" <isCurrent>");
if (picture != null) sb.append(" <hasPicture>");
if (isRestricted) sb.append(" <isRestricted>");
+ if (isDisabledByAdmin) {
+ sb.append(" <isDisabledByAdmin>");
+ sb.append(" enforcedAdmin=" + enforcedAdmin);
+ }
sb.append(')');
return sb.toString();
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 3c63aae..0901015 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -27,6 +27,7 @@
import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
@@ -559,7 +560,13 @@
: R.drawable.ic_volume_expand_animation;
if (res == mExpandButtonRes) return;
mExpandButtonRes = res;
- mExpandButton.setImageResource(res);
+ if (hasTouchFeature()) {
+ mExpandButton.setImageResource(res);
+ } else {
+ // if there is no touch feature, show the volume ringer instead
+ mExpandButton.setImageResource(R.drawable.ic_volume_ringer);
+ mExpandButton.setBackgroundResource(0); // remove gray background emphasis
+ }
mExpandButton.setContentDescription(mContext.getString(mExpanded ?
R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
}
@@ -837,6 +844,11 @@
rescheduleTimeoutH();
}
+ private boolean hasTouchFeature() {
+ final PackageManager pm = mContext.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
+ }
+
private final VolumeDialogController.Callbacks mControllerCallbackH
= new VolumeDialogController.Callbacks() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
index 6ebf488..f86c6a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
@@ -283,6 +283,11 @@
public void onClick(IBinder iBinder) throws RemoteException {
sendCallback("onClick");
}
+
+ @Override
+ public void onUnlockComplete() throws RemoteException {
+ sendCallback("onUnlockComplete");
+ }
};
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 13fc47d..b64fbea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -60,8 +60,8 @@
protected NetworkControllerImpl mNetworkController;
protected MobileSignalController mMobileSignalController;
protected PhoneStateListener mPhoneStateListener;
- private SignalStrength mSignalStrength;
- private ServiceState mServiceState;
+ protected SignalStrength mSignalStrength;
+ protected ServiceState mServiceState;
protected ConnectivityManager mMockCm;
protected WifiManager mMockWm;
protected SubscriptionManager mMockSm;
@@ -235,7 +235,7 @@
mPhoneStateListener.onSignalStrengthsChanged(mSignalStrength);
}
- private void updateServiceState() {
+ protected void updateServiceState() {
Log.d(TAG, "Sending Service State: " + mServiceState);
mPhoneStateListener.onServiceStateChanged(mServiceState);
}
@@ -246,6 +246,7 @@
}
public void updateDataConnectionState(int dataState, int dataNetType) {
+ when(mServiceState.getDataNetworkType()).thenReturn(dataNetType);
mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 587e2b5..e4f858b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -115,6 +115,21 @@
TelephonyIcons.QS_DATA_4G);
}
+ public void testDataChangeWithoutConnectionState() {
+ setupDefaultSignal();
+ updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+ TelephonyManager.NETWORK_TYPE_LTE);
+
+ verifyDataIndicators(TelephonyIcons.DATA_LTE[1][0 /* No direction */],
+ TelephonyIcons.QS_DATA_LTE);
+
+ Mockito.when(mServiceState.getDataNetworkType())
+ .thenReturn(TelephonyManager.NETWORK_TYPE_HSPA);
+ updateServiceState();
+ verifyDataIndicators(TelephonyIcons.DATA_H[1][0 /* No direction */],
+ TelephonyIcons.QS_DATA_H);
+ }
+
public void testDataActivity() {
setupDefaultSignal();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
index 28121b4..e310801 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java
@@ -64,6 +64,14 @@
// Indicates that motion events are being collected to match a gesture.
private boolean mRecognizingGesture;
+ // Indicates that motion events from the second pointer are being checked
+ // for a double tap.
+ private boolean mSecondFingerDoubleTap;
+
+ // Tracks the most recent time where ACTION_POINTER_DOWN was sent for the
+ // second pointer.
+ private long mSecondPointerDownTime;
+
// Policy flags of the previous event.
private int mPolicyFlags;
@@ -102,6 +110,7 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDoubleTapDetected = false;
+ mSecondFingerDoubleTap = false;
mRecognizingGesture = true;
mPreviousX = x;
mPreviousY = y;
@@ -133,6 +142,43 @@
}
}
break;
+
+ case MotionEvent.ACTION_POINTER_DOWN:
+ // Once a second finger is used, we're definitely not
+ // recognizing a gesture.
+ cancelGesture();
+
+ if (event.getPointerCount() == 2) {
+ // If this was the second finger, attempt to recognize double
+ // taps on it.
+ mSecondFingerDoubleTap = true;
+ mSecondPointerDownTime = event.getEventTime();
+ } else {
+ // If there are more than two fingers down, stop watching
+ // for a double tap.
+ mSecondFingerDoubleTap = false;
+ }
+ break;
+
+ 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;
+ }
+ break;
+ }
+
+ // If we're detecting taps on the second finger, map events from the
+ // finger to the first finger.
+ if (mSecondFingerDoubleTap) {
+ MotionEvent newEvent = mapSecondPointerToFirstPointer(event);
+ if (newEvent == null) {
+ return false;
+ }
+ boolean handled = mGestureDetector.onTouchEvent(newEvent);
+ newEvent.recycle();
+ return handled;
}
if (!mRecognizingGesture) {
@@ -146,6 +192,7 @@
public void clear() {
mFirstTapDetected = false;
mDoubleTapDetected = false;
+ mSecondFingerDoubleTap = false;
cancelGesture();
mStrokeBuffer.clear();
}
@@ -229,4 +276,28 @@
return false;
}
+
+ private MotionEvent mapSecondPointerToFirstPointer(MotionEvent event) {
+ // Only map basic events when two fingers are down.
+ if (event.getPointerCount() != 2 ||
+ (event.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN &&
+ event.getActionMasked() != MotionEvent.ACTION_POINTER_UP &&
+ event.getActionMasked() != MotionEvent.ACTION_MOVE)) {
+ return null;
+ }
+
+ int action = event.getActionMasked();
+
+ if (action == MotionEvent.ACTION_POINTER_DOWN) {
+ action = MotionEvent.ACTION_DOWN;
+ } else if (action == MotionEvent.ACTION_POINTER_UP) {
+ action = MotionEvent.ACTION_UP;
+ }
+
+ // Map the information from the second pointer to the first.
+ return MotionEvent.obtain(mSecondPointerDownTime, event.getEventTime(), action,
+ event.getX(1), event.getY(1), event.getPressure(1), event.getSize(1),
+ event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
+ event.getDeviceId(), event.getEdgeFlags());
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 39d5952..28aeef7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -715,7 +715,7 @@
}
@Override
- public IBinder getWindowToken(int windowId) {
+ public IBinder getWindowToken(int windowId, int userId) {
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.RETRIEVE_WINDOW_TOKEN,
GET_WINDOW_TOKEN);
@@ -724,8 +724,7 @@
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
- .resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.getCallingUserId());
+ .resolveCallingUserIdEnforcingPermissionsLocked(userId);
if (resolvedUserId != mCurrentUserId) {
return null;
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 2264c69..aa15373 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -239,6 +239,11 @@
// How long between attempts to perform a full-data backup of any given app
static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day
+ // If an app is busy when we want to do a full-data backup, how long to defer the retry.
+ // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
+ static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60; // one hour
+ static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2; // two hours
+
Context mContext;
private PackageManager mPackageManager;
IPackageManager mPackageManagerBinder;
@@ -4496,35 +4501,72 @@
return false;
}
- // At this point we know that we have work to do, just not right now. Any
- // exit without actually running backups will also require that we
+ // At this point we know that we have work to do, but possibly not right now.
+ // Any exit without actually running backups will also require that we
// reschedule the job.
boolean runBackup = true;
+ boolean headBusy;
- if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
- if (MORE_DEBUG) {
- Slog.i(TAG, "Preconditions not met; not running full backup");
- }
- runBackup = false;
- // Typically this means we haven't run a key/value backup yet. Back off
- // full-backup operations by the key/value job's run interval so that
- // next time we run, we are likely to be able to make progress.
- latency = KeyValueBackupJob.BATCH_INTERVAL;
- }
+ do {
+ headBusy = false;
- if (runBackup) {
- entry = mFullBackupQueue.get(0);
- long timeSinceRun = now - entry.lastBackup;
- runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
- if (!runBackup) {
- // It's too early to back up the next thing in the queue, so bow out
+ if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
if (MORE_DEBUG) {
- Slog.i(TAG, "Device ready but too early to back up next app");
+ Slog.i(TAG, "Preconditions not met; not running full backup");
}
- // Wait until the next app in the queue falls due for a full data backup
- latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+ runBackup = false;
+ // Typically this means we haven't run a key/value backup yet. Back off
+ // full-backup operations by the key/value job's run interval so that
+ // next time we run, we are likely to be able to make progress.
+ latency = KeyValueBackupJob.BATCH_INTERVAL;
}
- }
+
+ if (runBackup) {
+ entry = mFullBackupQueue.get(0);
+ long timeSinceRun = now - entry.lastBackup;
+ runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
+ if (!runBackup) {
+ // It's too early to back up the next thing in the queue, so bow out
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Device ready but too early to back up next app");
+ }
+ // Wait until the next app in the queue falls due for a full data backup
+ latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+ break; // we know we aren't doing work yet, so bail.
+ }
+
+ try {
+ PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+ headBusy = mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
+
+ if (headBusy) {
+ final long nextEligible = System.currentTimeMillis()
+ + BUSY_BACKOFF_MIN_MILLIS
+ + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
+ if (DEBUG_SCHEDULING) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ Slog.i(TAG, "Full backup time but " + entry.packageName
+ + " is busy; deferring to "
+ + sdf.format(new Date(nextEligible)));
+ }
+ // This relocates the app's entry from the head of the queue to
+ // its order-appropriate position further down, so upon looping
+ // a new candidate will be considered at the head.
+ enqueueFullBackup(entry.packageName,
+ nextEligible - MIN_FULL_BACKUP_INTERVAL);
+ }
+
+ } catch (NameNotFoundException nnf) {
+ // So, we think we want to back this up, but it turns out the package
+ // in question is no longer installed. We want to drop it from the
+ // queue entirely and move on, but if there's nothing else in the queue
+ // we should bail entirely. headBusy cannot have been set to true yet.
+ runBackup = (mFullBackupQueue.size() > 1);
+ } catch (RemoteException e) {
+ // Cannot happen; the Activity Manager is in the same process
+ }
+ }
+ } while (headBusy);
if (!runBackup) {
if (DEBUG_SCHEDULING) {
@@ -4539,7 +4581,7 @@
return false;
}
- // Okay, the top thing is runnable now. Pop it off and get going.
+ // Okay, the top thing is ready for backup now. Do it.
mFullBackupQueue.remove(0);
CountDownLatch latch = new CountDownLatch(1);
String[] pkg = new String[] {entry.packageName};
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 65a27c8..2de5324 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1568,12 +1568,11 @@
// load the global proxy at startup
mHandler.sendMessage(mHandler.obtainMessage(EVENT_APPLY_GLOBAL_HTTP_PROXY));
- // Try bringing up tracker, but if KeyStore isn't ready yet, wait
- // for user to unlock device.
- if (!updateLockdownVpn()) {
- final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
- mContext.registerReceiver(mUserPresentReceiver, filter);
- }
+ // Try bringing up tracker, but KeyStore won't be ready yet for secondary users so wait
+ // for user to unlock device too.
+ updateLockdownVpn();
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_PRESENT);
+ mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.ALL, filter, null, null);
// Configure whether mobile data is always on.
mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON));
@@ -1586,10 +1585,16 @@
private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ // User that sent this intent = user that was just unlocked
+ final int unlockedUser = getSendingUserId();
+
// Try creating lockdown tracker, since user present usually means
// unlocked keystore.
- if (updateLockdownVpn()) {
- mContext.unregisterReceiver(this);
+ if (mUserManager.getUserInfo(unlockedUser).isPrimary() &&
+ LockdownVpnTracker.isEnabled()) {
+ updateLockdownVpn();
+ } else {
+ updateAlwaysOnVpn(unlockedUser);
}
}
};
@@ -3258,6 +3263,76 @@
}
}
+ /**
+ * Sets up or tears down the always-on VPN for user {@param user} as appropriate.
+ *
+ * @return {@code false} in case of errors; {@code true} otherwise.
+ */
+ private boolean updateAlwaysOnVpn(int user) {
+ final String lockdownPackage = getAlwaysOnVpnPackage(user);
+ if (lockdownPackage == null) {
+ return true;
+ }
+
+ // Create an intent to start the VPN service declared in the app's manifest.
+ Intent serviceIntent = new Intent(VpnConfig.SERVICE_INTERFACE);
+ serviceIntent.setPackage(lockdownPackage);
+
+ try {
+ return mContext.startServiceAsUser(serviceIntent, UserHandle.of(user)) != null;
+ } catch (RuntimeException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean setAlwaysOnVpnPackage(int userId, String packageName) {
+ enforceConnectivityInternalPermission();
+ enforceCrossUserPermission(userId);
+
+ // Can't set always-on VPN if legacy VPN is already in lockdown mode.
+ if (LockdownVpnTracker.isEnabled()) {
+ return false;
+ }
+
+ // If the current VPN package is the same as the new one, this is a no-op
+ final String oldPackage = getAlwaysOnVpnPackage(userId);
+ if (TextUtils.equals(oldPackage, packageName)) {
+ return true;
+ }
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ return false;
+ }
+ if (!vpn.setAlwaysOnPackage(packageName)) {
+ return false;
+ }
+ if (!updateAlwaysOnVpn(userId)) {
+ vpn.setAlwaysOnPackage(null);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String getAlwaysOnVpnPackage(int userId) {
+ enforceConnectivityInternalPermission();
+ enforceCrossUserPermission(userId);
+
+ synchronized (mVpns) {
+ Vpn vpn = mVpns.get(userId);
+ if (vpn == null) {
+ Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ return null;
+ }
+ return vpn.getAlwaysOnPackage();
+ }
+ }
+
@Override
public int checkMobileProvisioning(int suggestedTimeOutMs) {
// TODO: Remove? Any reason to trigger a provisioning check?
@@ -4848,6 +4923,11 @@
}
@Override
+ public String getCaptivePortalServerUrl() {
+ return NetworkMonitor.getCaptivePortalServerUrl(mContext);
+ }
+
+ @Override
public void startNattKeepalive(Network network, int intervalSeconds, Messenger messenger,
IBinder binder, String srcAddr, int srcPort, String dstAddr) {
enforceKeepalivePermission();
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 1a21f5c..377d52f 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -695,7 +695,7 @@
unlockUser(userId, token);
UserInfo info = UserManager.get(mContext).getUserInfo(userId);
- if (LockPatternUtils.isSeparateWorkChallengeEnabled() && info.isManagedProfile()) {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
TrustManager trustManager =
(TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
trustManager.setDeviceLockedForUser(userId, false);
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index 137fa27..816c791 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -71,6 +71,7 @@
private final Object mFileWriteLock = new Object();
private int mStoredCredentialType;
+ private LockPatternUtils mLockPatternUtils;
class CredentialHash {
static final int TYPE_NONE = -1;
@@ -100,6 +101,7 @@
public LockSettingsStorage(Context context, Callback callback) {
mContext = context;
mOpenHelper = new DatabaseHelper(context, callback);
+ mLockPatternUtils = new LockPatternUtils(context);
}
public void writeKeyValue(String key, String value, int userId) {
@@ -388,7 +390,7 @@
private int getUserParentOrSelfId(int userId) {
// Device supports per user encryption, so lock is applied to the given user.
- if (LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
return userId;
}
// Device uses Block Based Encryption, and the parent user's lock is used for the whole
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 11f9e2d..f345d7e 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2312,6 +2312,14 @@
}
final int uid = Binder.getCallingUid();
+ // Only allow system to start session
+ if (!isSystemUid(uid)) {
+ String msg = String.format(
+ "uid %s cannot stat add account session.",
+ uid);
+ throw new SecurityException(msg);
+ }
+
final int userId = UserHandle.getUserId(uid);
if (!canUserModifyAccounts(userId, uid)) {
try {
@@ -2499,6 +2507,14 @@
}
final int uid = Binder.getCallingUid();
+ // Only allow system to finish session
+ if (!isSystemUid(uid)) {
+ String msg = String.format(
+ "uid %s cannot finish session.",
+ uid);
+ throw new SecurityException(msg);
+ }
+
final int userId = UserHandle.getUserId(uid);
if (!canUserModifyAccounts(userId, uid)) {
sendErrorResponse(response,
@@ -2717,6 +2733,16 @@
if (account == null) {
throw new IllegalArgumentException("account is null");
}
+
+ final int uid = Binder.getCallingUid();
+ // Only allow system to start session
+ if (!isSystemUid(uid)) {
+ String msg = String.format(
+ "uid %s cannot start update credentials session.",
+ uid);
+ throw new SecurityException(msg);
+ }
+
int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d12eadb..7ba6338 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -61,6 +61,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -326,7 +327,7 @@
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
- callingPid, callingUid, userId, true, callerFg);
+ callingPid, callingUid, userId, true, callerFg, false);
if (res == null) {
return null;
}
@@ -549,7 +550,7 @@
// If this service is active, make sure it is stopped.
ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, null,
- Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false);
+ Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false);
if (r != null) {
if (r.record != null) {
final long origId = Binder.clearCallingIdentity();
@@ -598,7 +599,7 @@
IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
ServiceLookupResult r = retrieveServiceLocked(service, resolvedType, callingPackage,
Binder.getCallingPid(), Binder.getCallingUid(),
- UserHandle.getCallingUserId(), false, false);
+ UserHandle.getCallingUserId(), false, false, false);
IBinder ret = null;
if (r != null) {
@@ -831,10 +832,11 @@
}
final boolean callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
ServiceLookupResult res =
- retrieveServiceLocked(service, resolvedType, callingPackage,
- Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
+ retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
if (res == null) {
return 0;
}
@@ -1192,7 +1194,7 @@
private ServiceLookupResult retrieveServiceLocked(Intent service,
String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,
- boolean createIfNeeded, boolean callingFromFg) {
+ boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal) {
ServiceRecord r = null;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service
+ " type=" + resolvedType + " callingUid=" + callingUid);
@@ -1205,10 +1207,16 @@
if (comp != null) {
r = smap.mServicesByName.get(comp);
}
- if (r == null) {
+ if (r == null && !isBindExternal) {
Intent.FilterComparison filter = new Intent.FilterComparison(service);
r = smap.mServicesByIntent.get(filter);
}
+ if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
+ && !callingPackage.equals(r.packageName)) {
+ // If an external service is running within its own package, other packages
+ // should not bind to that instance.
+ r = null;
+ }
if (r == null) {
try {
// TODO: come back and remove this assumption to triage all services
@@ -1225,6 +1233,37 @@
}
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
+ if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
+ if (isBindExternal) {
+ if (!sInfo.exported) {
+ throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
+ " is not exported");
+ }
+ if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
+ throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
+ " is not an isolatedProcess");
+ }
+ // Run the service under the calling package's application.
+ ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
+ callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
+ if (aInfo == null) {
+ throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
+ "could not resolve client package " + callingPackage);
+ }
+ sInfo = new ServiceInfo(sInfo);
+ sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
+ sInfo.applicationInfo.packageName = aInfo.packageName;
+ sInfo.applicationInfo.uid = aInfo.uid;
+ name = new ComponentName(aInfo.packageName, name.getClassName());
+ service.setComponent(name);
+ } else {
+ throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
+ name);
+ }
+ } else if (isBindExternal) {
+ throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
+ " is not an externalService");
+ }
if (userId > 0) {
if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
sInfo.name, sInfo.flags)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a4b13ed..ca38b71 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18,7 +18,6 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.AssistUtils;
@@ -35,6 +34,7 @@
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
+import com.android.internal.os.InstallerConnection.InstallerException;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
@@ -239,6 +239,7 @@
import java.util.concurrent.atomic.AtomicLong;
import dalvik.system.VMRuntime;
+
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
@@ -255,7 +256,9 @@
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -1306,7 +1309,7 @@
int mMemWatchDumpUid;
String mTrackAllocationApp = null;
- final long[] mTmpLong = new long[1];
+ final long[] mTmpLong = new long[2];
static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
@@ -1954,8 +1957,10 @@
break;
}
case SYSTEM_USER_UNLOCK_MSG: {
- mSystemServiceManager.unlockUser(msg.arg1);
- mRecentTasks.cleanupLocked(msg.arg1);
+ final int userId = msg.arg1;
+ mSystemServiceManager.unlockUser(userId);
+ mRecentTasks.cleanupLocked(userId);
+ installEncryptionUnawareProviders(userId);
break;
}
case SYSTEM_USER_CURRENT_MSG: {
@@ -2237,7 +2242,7 @@
}
int num = 0;
- long[] tmp = new long[1];
+ long[] tmp = new long[2];
do {
ProcessRecord proc;
int procState;
@@ -2269,7 +2274,7 @@
if (pss != 0 && proc.thread != null && proc.setProcState == procState
&& proc.pid == pid && proc.lastPssTime == lastPssTime) {
num++;
- recordPssSampleLocked(proc, procState, pss, tmp[0],
+ recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1],
SystemClock.uptimeMillis());
}
}
@@ -6586,8 +6591,10 @@
Process.establishZygoteConnectionForAbi(abi);
final String instructionSet = VMRuntime.getInstructionSet(abi);
if (!completedIsas.contains(instructionSet)) {
- if (mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi)) != 0) {
- Slog.e(TAG, "Unable to mark boot complete for abi: " + abi);
+ try {
+ mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi));
+ } catch (InstallerException e) {
+ Slog.e(TAG, "Unable to mark boot complete for abi: " + abi, e);
}
completedIsas.add(instructionSet);
}
@@ -7249,6 +7256,17 @@
}
@Override
+ public boolean isAppForeground(int uid) throws RemoteException {
+ synchronized (this) {
+ UidRecord uidRec = mActiveUids.get(uid);
+ if (uidRec == null || uidRec.idle) {
+ return false;
+ }
+ return uidRec.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
+ }
+
+ @Override
public boolean inMultiWindowMode(IBinder token) {
final long origId = Binder.clearCallingIdentity();
try {
@@ -10824,6 +10842,49 @@
}
/**
+ * When a user is unlocked, we need to install encryption-unaware providers
+ * belonging to any running apps.
+ */
+ private void installEncryptionUnawareProviders(int userId) {
+ if (!StorageManager.isFileBasedEncryptionEnabled()) {
+ // 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
+ // have already been spun up.
+ return;
+ }
+
+ synchronized (this) {
+ final int NP = mProcessNames.getMap().size();
+ for (int ip = 0; ip < NP; ip++) {
+ final SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
+ final int NA = apps.size();
+ for (int ia = 0; ia < NA; ia++) {
+ final ProcessRecord app = apps.valueAt(ia);
+ if (app.userId != userId || app.thread == null) continue;
+
+ final int NG = app.pkgList.size();
+ for (int ig = 0; ig < NG; ig++) {
+ try {
+ final String pkgName = app.pkgList.keyAt(ig);
+ final PackageInfo pkgInfo = AppGlobals.getPackageManager()
+ .getPackageInfo(pkgName,
+ GET_PROVIDERS | MATCH_ENCRYPTION_UNAWARE, userId);
+ if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
+ for (ProviderInfo provInfo : pkgInfo.providers) {
+ Log.v(TAG, "Installing " + provInfo);
+ app.thread.scheduleInstallProvider(provInfo);
+ }
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Allows apps to retrieve the MIME type of a URI.
* If an app is in the same user as the ContentProvider, or if it is allowed to interact across
* users, then it does not need permission to access the ContentProvider.
@@ -12220,6 +12281,8 @@
sb.append(proc.processName);
sb.append(" in idle maint: pss=");
sb.append(proc.lastPss);
+ sb.append(", swapPss=");
+ sb.append(proc.lastSwapPss);
sb.append(", initialPss=");
sb.append(proc.initialIdlePss);
sb.append(", period=");
@@ -15127,6 +15190,7 @@
pw.print("state: cur="); pw.print(ProcessList.makeProcStateString(r.curProcState));
pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState));
pw.print(" lastPss="); DebugUtils.printSizeValue(pw, r.lastPss*1024);
+ pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, r.lastSwapPss*1024);
pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, r.lastCachedPss*1024);
pw.println();
pw.print(prefix);
@@ -15280,32 +15344,35 @@
final String label;
final String shortLabel;
final long pss;
+ final long swapPss;
final int id;
final boolean hasActivities;
ArrayList<MemItem> subitems;
- public MemItem(String _label, String _shortLabel, long _pss, int _id,
+ public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id,
boolean _hasActivities) {
isProc = true;
label = _label;
shortLabel = _shortLabel;
pss = _pss;
+ swapPss = _swapPss;
id = _id;
hasActivities = _hasActivities;
}
- public MemItem(String _label, String _shortLabel, long _pss, int _id) {
+ public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id) {
isProc = false;
label = _label;
shortLabel = _shortLabel;
pss = _pss;
+ swapPss = _swapPss;
id = _id;
hasActivities = false;
}
}
static final void dumpMemItems(PrintWriter pw, String prefix, String tag,
- ArrayList<MemItem> items, boolean sort, boolean isCompact) {
+ ArrayList<MemItem> items, boolean sort, boolean isCompact, boolean dumpSwapPss) {
if (sort && !isCompact) {
Collections.sort(items, new Comparator<MemItem>() {
@Override
@@ -15323,18 +15390,24 @@
for (int i=0; i<items.size(); i++) {
MemItem mi = items.get(i);
if (!isCompact) {
- pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label);
+ if (dumpSwapPss) {
+ pw.printf("%s%s: %-60s (%s in swap)\n", prefix, stringifyKBSize(mi.pss),
+ mi.label, stringifyKBSize(mi.swapPss));
+ } else {
+ pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label);
+ }
} else if (mi.isProc) {
pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel);
- pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss);
+ pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss); pw.print(",");
+ pw.print(dumpSwapPss ? mi.swapPss : "N/A");
pw.println(mi.hasActivities ? ",a" : ",e");
} else {
pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(",");
- pw.println(mi.pss);
+ pw.println(mi.pss); pw.print(dumpSwapPss ? mi.swapPss : "N/A");
}
if (mi.subitems != null) {
- dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems,
- true, isCompact);
+ dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems,
+ true, isCompact, dumpSwapPss);
}
}
}
@@ -15463,6 +15536,8 @@
boolean isCompact = false;
boolean localOnly = false;
boolean packages = false;
+ boolean isCheckinRequest = false;
+ boolean dumpSwapPss = false;
int opti = 0;
while (opti < args.length) {
@@ -15475,6 +15550,7 @@
dumpDetails = true;
dumpFullDetails = true;
dumpDalvik = true;
+ dumpSwapPss = true;
} else if ("-d".equals(opt)) {
dumpDalvik = true;
} else if ("-c".equals(opt)) {
@@ -15482,22 +15558,29 @@
} else if ("-s".equals(opt)) {
dumpDetails = true;
dumpSummaryOnly = true;
+ } else if ("-S".equals(opt)) {
+ dumpSwapPss = true;
} else if ("--oom".equals(opt)) {
oomOnly = true;
} else if ("--local".equals(opt)) {
localOnly = true;
} else if ("--package".equals(opt)) {
packages = true;
+ } else if ("--checkin".equals(opt)) {
+ isCheckinRequest = true;
+
} else if ("-h".equals(opt)) {
pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]");
pw.println(" -a: include all available information for each process.");
pw.println(" -d: include dalvik details.");
pw.println(" -c: dump in a compact machine-parseable representation.");
pw.println(" -s: dump only summary of application memory usage.");
+ pw.println(" -S: dump also SwapPss.");
pw.println(" --oom: only show processes organized by oom adj.");
pw.println(" --local: only collect details locally, don't call process.");
pw.println(" --package: interpret process arg as package, dumping all");
pw.println(" processes that have loaded that package.");
+ pw.println(" --checkin: dump data for a checkin");
pw.println("If [process] is specified it can be the name or ");
pw.println("pid of a specific process to dump.");
return;
@@ -15506,7 +15589,6 @@
}
}
- final boolean isCheckinRequest = scanArgs(args, "--checkin");
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
final long[] tmpLong = new long[1];
@@ -15578,18 +15660,28 @@
ArrayList<MemItem> procMems = new ArrayList<MemItem>();
final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
long nativePss = 0;
+ long nativeSwapPss = 0;
long dalvikPss = 0;
+ long dalvikSwapPss = 0;
long[] dalvikSubitemPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
+ long[] dalvikSubitemSwapPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
+ EmptyArray.LONG;
long otherPss = 0;
+ long otherSwapPss = 0;
long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
+ long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
+ long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length];
ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
new ArrayList[DUMP_MEM_OOM_LABEL.length];
long totalPss = 0;
+ long totalSwapPss = 0;
long cachedPss = 0;
+ long cachedSwapPss = 0;
+ boolean hasSwapPss = false;
Debug.MemoryInfo mi = null;
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
@@ -15613,6 +15705,7 @@
}
if (dumpDetails || (!brief && !oomOnly)) {
Debug.getMemoryInfo(pid, mi);
+ hasSwapPss = mi.hasSwappedOutPss;
} else {
mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
mi.dalvikPrivateDirty = (int)tmpLong[0];
@@ -15640,6 +15733,7 @@
final long myTotalPss = mi.getTotalPss();
final long myTotalUss = mi.getTotalUss();
+ final long myTotalSwapPss = mi.getTotalSwappedOutPss();
synchronized (this) {
if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
@@ -15650,32 +15744,43 @@
if (!isCheckinRequest && mi != null) {
totalPss += myTotalPss;
+ totalSwapPss += myTotalSwapPss;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
- (hasActivities ? " / activities)" : ")"),
- r.processName, myTotalPss, pid, hasActivities);
+ (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
+ myTotalSwapPss, pid, hasActivities);
procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
nativePss += mi.nativePss;
+ nativeSwapPss += mi.nativeSwappedOutPss;
dalvikPss += mi.dalvikPss;
+ dalvikSwapPss += mi.dalvikSwappedOutPss;
for (int j=0; j<dalvikSubitemPss.length; j++) {
dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemSwapPss[j] +=
+ mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
}
otherPss += mi.otherPss;
+ otherSwapPss += mi.otherSwappedOutPss;
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
long mem = mi.getOtherPss(j);
miscPss[j] += mem;
otherPss -= mem;
+ mem = mi.getOtherSwappedOutPss(j);
+ miscSwapPss[j] += mem;
+ otherSwapPss -= mem;
}
if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
cachedPss += myTotalPss;
+ cachedSwapPss += myTotalSwapPss;
}
for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
if (oomAdj <= DUMP_MEM_OOM_ADJ[oomIndex]
|| oomIndex == (oomPss.length-1)) {
oomPss[oomIndex] += myTotalPss;
+ oomSwapPss[oomIndex] += myTotalSwapPss;
if (oomProcs[oomIndex] == null) {
oomProcs[oomIndex] = new ArrayList<MemItem>();
}
@@ -15710,26 +15815,35 @@
}
final long myTotalPss = mi.getTotalPss();
+ final long myTotalSwapPss = mi.getTotalSwappedOutPss();
totalPss += myTotalPss;
nativeProcTotalPss += myTotalPss;
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
- st.name, myTotalPss, st.pid, false);
+ st.name, myTotalPss, mi.getSummaryTotalSwapPss(), st.pid, false);
procMems.add(pssItem);
nativePss += mi.nativePss;
+ nativeSwapPss += mi.nativeSwappedOutPss;
dalvikPss += mi.dalvikPss;
+ dalvikSwapPss += mi.dalvikSwappedOutPss;
for (int j=0; j<dalvikSubitemPss.length; j++) {
- dalvikSubitemPss[j] += mi.getOtherPss(
- Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemSwapPss[j] +=
+ mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
}
otherPss += mi.otherPss;
+ otherSwapPss += mi.otherSwappedOutPss;
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
long mem = mi.getOtherPss(j);
miscPss[j] += mem;
otherPss -= mem;
+ mem = mi.getOtherSwappedOutPss(j);
+ miscSwapPss[j] += mem;
+ otherSwapPss -= mem;
}
oomPss[0] += myTotalPss;
+ oomSwapPss[0] += myTotalSwapPss;
if (oomProcs[0] == null) {
oomProcs[0] = new ArrayList<MemItem>();
}
@@ -15740,21 +15854,23 @@
ArrayList<MemItem> catMems = new ArrayList<MemItem>();
- catMems.add(new MemItem("Native", "Native", nativePss, -1));
- final MemItem dalvikItem = new MemItem("Dalvik", "Dalvik", dalvikPss, -2);
+ catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, -1));
+ final MemItem dalvikItem =
+ new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, -2);
if (dalvikSubitemPss.length > 0) {
dalvikItem.subitems = new ArrayList<MemItem>();
for (int j=0; j<dalvikSubitemPss.length; j++) {
final String name = Debug.MemoryInfo.getOtherLabel(
Debug.MemoryInfo.NUM_OTHER_STATS + j);
- dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], j));
+ dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j],
+ dalvikSubitemSwapPss[j], j));
}
}
catMems.add(dalvikItem);
- catMems.add(new MemItem("Unknown", "Unknown", otherPss, -3));
+ catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, -3));
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
String label = Debug.MemoryInfo.getOtherLabel(j);
- catMems.add(new MemItem(label, label, miscPss[j], j));
+ catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], j));
}
ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
@@ -15762,30 +15878,31 @@
if (oomPss[j] != 0) {
String label = isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
: DUMP_MEM_OOM_LABEL[j];
- MemItem item = new MemItem(label, label, oomPss[j],
+ MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j],
DUMP_MEM_OOM_ADJ[j]);
item.subitems = oomProcs[j];
oomMems.add(item);
}
}
+ dumpSwapPss = dumpSwapPss && hasSwapPss && totalSwapPss != 0;
if (!brief && !oomOnly && !isCompact) {
pw.println();
pw.println("Total PSS by process:");
- dumpMemItems(pw, " ", "proc", procMems, true, isCompact);
+ dumpMemItems(pw, " ", "proc", procMems, true, isCompact, dumpSwapPss);
pw.println();
}
if (!isCompact) {
pw.println("Total PSS by OOM adjustment:");
}
- dumpMemItems(pw, " ", "oom", oomMems, false, isCompact);
+ dumpMemItems(pw, " ", "oom", oomMems, false, isCompact, dumpSwapPss);
if (!brief && !oomOnly) {
PrintWriter out = categoryPw != null ? categoryPw : pw;
if (!isCompact) {
out.println();
out.println("Total PSS by category:");
}
- dumpMemItems(out, " ", "cat", catMems, true, isCompact);
+ dumpMemItems(out, " ", "cat", catMems, true, isCompact, dumpSwapPss);
}
if (!isCompact) {
pw.println();
@@ -18291,9 +18408,12 @@
* dialog / global actions also might want different behaviors.
*/
private static final boolean shouldShowDialogs(Configuration config) {
- return !(config.keyboard == Configuration.KEYBOARD_NOKEYS
- && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
- && config.navigation == Configuration.NAVIGATION_NONAV);
+ final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
+ && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
+ && config.navigation == Configuration.NAVIGATION_NONAV);
+ final boolean uiIsNotCarType = !((config.uiMode & Configuration.UI_MODE_TYPE_MASK)
+ == Configuration.UI_MODE_TYPE_CAR);
+ return inputMethodExists && uiIsNotCarType;
}
@Override
@@ -19105,8 +19225,10 @@
/**
* Record new PSS sample for a process.
*/
- void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long now) {
- EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024);
+ void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long swapPss,
+ long now) {
+ EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
+ swapPss * 1024);
proc.lastPssTime = now;
proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList);
if (DEBUG_PSS) Slog.d(TAG_PSS,
@@ -19116,8 +19238,10 @@
proc.initialIdlePss = pss;
}
proc.lastPss = pss;
+ proc.lastSwapPss = swapPss;
if (procState >= ActivityManager.PROCESS_STATE_HOME) {
proc.lastCachedPss = pss;
+ proc.lastCachedSwapPss = swapPss;
}
final SparseArray<Pair<Long, String>> watchUids
@@ -19553,7 +19677,7 @@
// states, which well tend to give noisy data.
long start = SystemClock.uptimeMillis();
long pss = Debug.getPss(app.pid, mTmpLong, null);
- recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], now);
+ recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], mTmpLong[1], now);
mPendingPssProcesses.remove(app);
Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState
+ " to " + app.curProcState + ": "
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 4101dde..ffb2fc4 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -5,6 +5,7 @@
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.PINNED_STACK_ID;
+import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import android.app.ActivityManager.StackId;
import android.content.Context;
@@ -51,7 +52,7 @@
mLastLogTimeSecs = now;
ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
- if (stack != null && stack.isStackVisibleLocked()) {
+ if (stack != null && stack.getStackVisibilityLocked() != 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 4fb87c3..eb0945bf 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -171,7 +171,9 @@
boolean stopped; // is activity pause finished?
boolean delayedResume; // not yet resumed because of stopped app switches?
boolean finishing; // activity in pending finish list?
- boolean configDestroy; // need to destroy due to config change?
+ boolean deferRelaunchUntilPaused; // relaunch of activity is being deferred until pause is
+ // completed
+ boolean preserveWindowOnDeferredRelaunch; // activity windows are preserved on deferred relaunch
int configChangeFlags; // which config values have changed
boolean keysPaused; // has key dispatching been paused for it?
int launchMode; // the launch mode activity attribute.
@@ -184,9 +186,9 @@
boolean immersive; // immersive mode (don't interrupt if possible)
boolean forceNewConfig; // force re-create with new config next time
int launchCount; // count of launches since last state
- long lastLaunchTime; // time of last lauch of this activity
+ long lastLaunchTime; // time of last launch of this activity
boolean isVrActivity; // is the activity running in VR mode?
- ArrayList<ActivityContainer> mChildContainers = new ArrayList<ActivityContainer>();
+ ArrayList<ActivityContainer> mChildContainers = new ArrayList<>();
String stringName; // for caching of toString().
@@ -341,8 +343,8 @@
else TimeUtils.formatDuration(lastVisibleTime, now, pw);
pw.println();
}
- if (configDestroy || configChangeFlags != 0) {
- pw.print(prefix); pw.print("configDestroy="); pw.print(configDestroy);
+ if (deferRelaunchUntilPaused || configChangeFlags != 0) {
+ pw.print(prefix); pw.print("deferRelaunchUntilPaused="); pw.print(deferRelaunchUntilPaused);
pw.print(" configChangeFlags=");
pw.println(Integer.toHexString(configChangeFlags));
}
@@ -551,7 +553,7 @@
stopped = false;
delayedResume = false;
finishing = false;
- configDestroy = false;
+ deferRelaunchUntilPaused = false;
keysPaused = false;
inHistory = false;
visible = false;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index c44b4cf..98319fd 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -166,6 +166,14 @@
DESTROYED
}
+ // Stack is not considered visible.
+ static final int STACK_INVISIBLE = 0;
+ // Stack is considered visible
+ static final int STACK_VISIBLE = 1;
+ // Stack is considered visible, but only becuase it has activity that is visible behind other
+ // activities and there is a specific combination of stacks.
+ static final int STACK_VISIBLE_ACTIVITY_BEHIND = 2;
+
final ActivityManagerService mService;
final WindowManagerService mWindowManager;
private final RecentTasks mRecentTasks;
@@ -1088,7 +1096,7 @@
if (r.finishing) {
r.clearOptionsLocked();
} else {
- if (r.configDestroy) {
+ if (r.deferRelaunchUntilPaused) {
destroyActivityLocked(r, true, "stop-config");
mStackSupervisor.resumeFocusedStackTopActivityLocked();
} else {
@@ -1114,14 +1122,11 @@
if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE,
"Complete pause, no longer waiting: " + prev);
}
- if (prev.configDestroy) {
- // The previous is being paused because the configuration
- // is changing, which means it is actually stopping...
- // To juggle the fact that we are also starting a new
- // instance right now, we need to first completely stop
- // the current instance before starting the new one.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Destroying after pause: " + prev);
- destroyActivityLocked(prev, true, "pause-config");
+ if (prev.deferRelaunchUntilPaused) {
+ // Complete the deferred relaunch that was waiting for pause to complete.
+ if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Re-launching after pause: " + prev);
+ relaunchActivityLocked(prev, prev.configChangeFlags, false,
+ prev.preserveWindowOnDeferredRelaunch);
} else if (wasStopping) {
// We are also stopping, the stop request must have gone soon after the pause.
// We can't clobber it, because the stop confirmation will not be handled.
@@ -1182,8 +1187,12 @@
prev.cpuTimeAtResume = 0; // reset it
}
- // Notfiy when the task stack has changed
- mService.notifyTaskStackChangedLocked();
+ // Notify when the task stack has changed, but only if visibilities changed (not just
+ // focus).
+ if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause) {
+ mService.notifyTaskStackChangedLocked();
+ mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
+ }
}
private void addToStopping(ActivityRecord r) {
@@ -1260,6 +1269,7 @@
ActivityContainer container = containers.get(containerNdx);
container.setVisible(visible);
}
+ mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
}
// Find the first visible activity above the passed activity and if it is translucent return it
@@ -1319,7 +1329,8 @@
if (stacks != null) {
for (int i = stacks.size() - 1; i >= 0; --i) {
ActivityStack stack = stacks.get(i);
- if (stack != this && stack.isFocusable() && stack.isStackVisibleLocked()) {
+ if (stack != this && stack.isFocusable()
+ && stack.getStackVisibilityLocked() != STACK_INVISIBLE) {
return stack;
}
}
@@ -1361,14 +1372,17 @@
return true;
}
- /** Returns true if the stack is considered visible. */
- boolean isStackVisibleLocked() {
+ /**
+ * Returns stack's visibility: {@link #STACK_INVISIBLE}, {@link #STACK_VISIBLE} or
+ * {@link #STACK_VISIBLE_ACTIVITY_BEHIND}.
+ */
+ int getStackVisibilityLocked() {
if (!isAttached()) {
- return false;
+ return STACK_INVISIBLE;
}
if (mStackSupervisor.isFrontStack(this) || mStackSupervisor.isFocusedStack(this)) {
- return true;
+ return STACK_VISIBLE;
}
final int stackIndex = mStacks.indexOf(this);
@@ -1376,32 +1390,34 @@
if (stackIndex == mStacks.size() - 1) {
Slog.wtf(TAG,
"Stack=" + this + " isn't front stack but is at the top of the stack list");
- return false;
+ return STACK_INVISIBLE;
}
final boolean isLockscreenShown = mService.mLockScreenShown == LOCK_SCREEN_SHOWN;
if (isLockscreenShown && !StackId.isAllowedOverLockscreen(mStackId)) {
- return false;
+ return STACK_INVISIBLE;
}
final ActivityStack focusedStack = mStackSupervisor.getFocusedStack();
final int focusedStackId = focusedStack.mStackId;
if (mStackId == FULLSCREEN_WORKSPACE_STACK_ID
- && hasVisibleBehindActivity() && focusedStackId == HOME_STACK_ID) {
+ && hasVisibleBehindActivity() && focusedStackId == HOME_STACK_ID
+ && !focusedStack.topActivity().fullscreen) {
// The fullscreen stack should be visible if it has a visible behind activity behind
- // the home stack that will be translucent.
- return true;
+ // the home stack that is translucent.
+ return STACK_VISIBLE_ACTIVITY_BEHIND;
}
if (mStackId == DOCKED_STACK_ID) {
// Docked stack is always visible, except in the case where the home activity
// is the top running activity in the focused home stack.
if (focusedStackId != HOME_STACK_ID) {
- return true;
+ return STACK_VISIBLE;
}
ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked();
- return topHomeActivity == null || !topHomeActivity.isHomeActivity();
+ return topHomeActivity == null || !topHomeActivity.isHomeActivity() ?
+ STACK_VISIBLE : STACK_INVISIBLE;
}
// Find the first stack below focused stack that actually got something visible.
@@ -1413,7 +1429,7 @@
if ((focusedStackId == DOCKED_STACK_ID || focusedStackId == PINNED_STACK_ID)
&& stackIndex == belowFocusedIndex) {
// Stacks directly behind the docked or pinned stack are always visible.
- return true;
+ return STACK_VISIBLE;
}
if (focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
@@ -1422,7 +1438,7 @@
// visible so they can act as a backdrop to the translucent activity.
// For example, dialog activities
if (stackIndex == belowFocusedIndex) {
- return true;
+ return STACK_VISIBLE;
}
if (belowFocusedIndex >= 0) {
final ActivityStack stack = mStacks.get(belowFocusedIndex);
@@ -1430,14 +1446,14 @@
&& stackIndex == (belowFocusedIndex - 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 true;
+ return STACK_VISIBLE;
}
}
}
if (StackId.isStaticStack(mStackId)) {
// Visibility of any static stack should have been determined by the conditions above.
- return false;
+ return STACK_INVISIBLE;
}
for (int i = stackIndex + 1; i < mStacks.size(); i++) {
@@ -1449,15 +1465,15 @@
if (!StackId.isDynamicStacksVisibleBehindAllowed(stack.mStackId)) {
// These stacks can't have any dynamic stacks visible behind them.
- return false;
+ return STACK_INVISIBLE;
}
if (!hasTranslucentActivity(stack)) {
- return false;
+ return STACK_INVISIBLE;
}
}
- return true;
+ return STACK_VISIBLE;
}
final int rankTaskLayers(int baseLayer) {
@@ -1490,14 +1506,16 @@
// 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 boolean stackInvisible = !isStackVisibleLocked();
+ final int stackVisibility = getStackVisibilityLocked();
+ final boolean stackInvisible = stackVisibility != STACK_VISIBLE;
+ final boolean stackVisibleBehind = stackVisibility == STACK_VISIBLE_ACTIVITY_BEHIND;
boolean behindFullscreenActivity = stackInvisible;
boolean resumeNextActivity = isFocusable() && (isInStackLocked(starting) == null);
-
+ boolean behindTranslucentActivity = false;
+ final ActivityRecord visibleBehind = getVisibleBehindActivity();
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);
if (r.finishing) {
@@ -1510,11 +1528,13 @@
aboveTop = false;
// mLaunchingBehind: Activities launching behind are at the back of the task stack
// but must be drawn initially for the animation as though they were visible.
- if ((!behindFullscreenActivity || r.mLaunchTaskBehind)
- && okToShowLocked(r)) {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
- "Make visible? " + r + " finishing=" + r.finishing
- + " state=" + r.state);
+ final boolean activityVisibleBehind =
+ (behindTranslucentActivity || stackVisibleBehind) && visibleBehind == r;
+ final boolean isVisible = (!behindFullscreenActivity || r.mLaunchTaskBehind
+ || activityVisibleBehind) && okToShowLocked(r);
+ if (isVisible) {
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
+ + " finishing=" + r.finishing + " state=" + r.state);
// First: if this is not the current activity being started, make
// sure it matches the current configuration.
if (r != starting) {
@@ -1543,15 +1563,23 @@
configChanges |= r.configChangeFlags;
behindFullscreenActivity = updateBehindFullscreen(stackInvisible,
behindFullscreenActivity, task, r);
+ if (behindFullscreenActivity && !r.fullscreen) {
+ behindTranslucentActivity = true;
+ }
} else {
- makeInvisible(stackInvisible, behindFullscreenActivity, r);
+ if (DEBUG_VISIBILITY || true) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+ + " finishing=" + r.finishing + " state=" + r.state + " stackInvisible="
+ + stackInvisible + " behindFullscreenActivity="
+ + behindFullscreenActivity + " mLaunchTaskBehind="
+ + r.mLaunchTaskBehind);
+ makeInvisible(r, visibleBehind);
}
}
if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
// The visibility of tasks and the activities they contain in freeform stack are
// determined individually unlike other stacks where the visibility or fullscreen
// status of an activity in a previous task affects other.
- behindFullscreenActivity = stackInvisible;
+ behindFullscreenActivity = stackVisibility == STACK_INVISIBLE;
}
}
@@ -1599,18 +1627,14 @@
return false;
}
- private void makeInvisible(boolean stackInvisible, boolean behindFullscreenActivity,
- ActivityRecord r) {
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r + " finishing="
- + r.finishing + " state=" + r.state + " stackInvisible=" + stackInvisible
- + " behindFullscreenActivity=" + behindFullscreenActivity);
+ private void makeInvisible(ActivityRecord r, ActivityRecord visibleBehind) {
if (!r.visible) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
return;
}
// Now for any activities that aren't visible to the user, make sure they no longer are
// keeping the screen frozen.
- if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r);
+ if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.state);
try {
setVisible(r, false);
switch (r.state) {
@@ -1629,7 +1653,7 @@
case PAUSED:
// This case created for transitioning activities from
// translucent to opaque {@link Activity#convertToOpaque}.
- if (getVisibleBehindActivity() == r) {
+ if (visibleBehind == r) {
releaseBackgroundResources(r);
} else {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
@@ -1651,16 +1675,16 @@
private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
TaskRecord task, ActivityRecord r) {
if (r.fullscreen) {
- // At this point, nothing else needs to be shown in this task.
- behindFullscreenActivity = true;
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+ " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
- } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
+ // At this point, nothing else needs to be shown in this task.
behindFullscreenActivity = true;
+ } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r
+ " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
+ behindFullscreenActivity = true;
}
return behindFullscreenActivity;
}
@@ -2318,11 +2342,12 @@
ActivityStack lastStack = mStackSupervisor.getLastStack();
final boolean fromHome = lastStack.isHomeStack();
if (!isHomeStack() && (fromHome || topTask() != task)) {
- task.setTaskToReturnTo(fromHome
- ? lastStack.topTask() == null
- ? HOME_ACTIVITY_TYPE
- : lastStack.topTask().taskType
- : APPLICATION_ACTIVITY_TYPE);
+ int returnToType = APPLICATION_ACTIVITY_TYPE;
+ if (fromHome && StackId.allowTopTaskToReturnHome(mStackId)) {
+ returnToType = lastStack.topTask() == null
+ ? HOME_ACTIVITY_TYPE : lastStack.topTask().taskType;
+ }
+ task.setTaskToReturnTo(returnToType);
}
} else {
task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
@@ -2972,7 +2997,7 @@
r.stopped = true;
if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
r.state = ActivityState.STOPPED;
- if (r.configDestroy) {
+ if (r.deferRelaunchUntilPaused) {
destroyActivityLocked(r, true, "stop-except");
}
}
@@ -3403,7 +3428,7 @@
}
mService.resetFocusedActivityIfNeededLocked(r);
- r.configDestroy = false;
+ r.deferRelaunchUntilPaused = false;
r.frozenBeforeDestroy = false;
if (setState) {
@@ -3715,9 +3740,9 @@
void releaseBackgroundResources(ActivityRecord r) {
if (hasVisibleBehindActivity() &&
!mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) {
- if (r == topRunningActivityLocked()) {
+ if (r == topRunningActivityLocked() && getStackVisibilityLocked() == STACK_VISIBLE) {
// Don't release the top activity if it has requested to run behind the next
- // activity.
+ // activity and the stack is currently visible.
return;
}
if (DEBUG_STATES) Slog.d(TAG_STATES, "releaseBackgroundResources activtyDisplay=" +
@@ -4175,38 +4200,33 @@
r.configChangeFlags |= changes;
r.startFreezingScreenLocked(r.app, globalChanges);
r.forceNewConfig = false;
+ preserveWindow &= isResizeOnlyChange(changes);
if (r.app == null || r.app.thread == null) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is destroying non-running " + r);
destroyActivityLocked(r, true, "config");
} else if (r.state == ActivityState.PAUSING) {
- // A little annoying: we are waiting for this activity to
- // finish pausing. Let's not do anything now, but just
- // flag that it needs to be restarted when done pausing.
+ // A little annoying: we are waiting for this activity to finish pausing. Let's not
+ // do anything now, but just flag that it needs to be restarted when done pausing.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is skipping already pausing " + r);
- r.configDestroy = true;
+ r.deferRelaunchUntilPaused = true;
+ r.preserveWindowOnDeferredRelaunch = preserveWindow;
return true;
} else if (r.state == ActivityState.RESUMED) {
- // Try to optimize this case: the configuration is changing
- // and we need to restart the top, resumed activity.
- // Instead of doing the normal handshaking, just say
+ // Try to optimize this case: the configuration is changing and we need to restart
+ // the top, resumed activity. Instead of doing the normal handshaking, just say
// "restart!".
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, true,
- preserveWindow && isResizeOnlyChange(changes));
- r.configChangeFlags = 0;
+ relaunchActivityLocked(r, r.configChangeFlags, true, preserveWindow);
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching non-resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, false,
- preserveWindow && isResizeOnlyChange(changes));
- r.configChangeFlags = 0;
+ relaunchActivityLocked(r, r.configChangeFlags, false, preserveWindow);
}
- // All done... tell the caller we weren't able to keep this
- // activity around.
+ // All done... tell the caller we weren't able to keep this activity around.
return false;
}
@@ -4298,6 +4318,7 @@
private void relaunchActivityLocked(
ActivityRecord r, int changes, boolean andResume, boolean preserveWindow) {
if (mService.mSuppressResizeConfigChanges && preserveWindow) {
+ r.configChangeFlags = 0;
return;
}
@@ -4341,6 +4362,8 @@
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
r.state = ActivityState.PAUSED;
}
+
+ r.configChangeFlags = 0;
}
boolean willActivityBeVisibleLocked(IBinder token) {
@@ -4757,8 +4780,7 @@
}
void addConfigOverride(ActivityRecord r, TaskRecord task) {
- final Rect bounds = task.getLaunchBounds();
- task.updateOverrideConfiguration(bounds);
+ final Rect bounds = task.updateOverrideConfigurationFromLaunchBounds();
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
@@ -4814,10 +4836,9 @@
}
private void setAppTask(ActivityRecord r, TaskRecord task) {
- final Rect bounds = task.getLaunchBounds();
- task.updateOverrideConfiguration(bounds);
+ final Rect bounds = task.updateOverrideConfigurationFromLaunchBounds();
mWindowManager.setAppTask(
- r.appToken, task.taskId, mStackId, task.getLaunchBounds(), task.mOverrideConfig);
+ r.appToken, task.taskId, mStackId, bounds, task.mOverrideConfig);
mWindowManager.setTaskResizeable(task.taskId, task.mResizeable);
r.taskConfigOverride = task.mOverrideConfig;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4672023..e837d9a 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -399,6 +399,12 @@
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
/**
+ * Used to keep track whether app visibilities got changed since the last pause. Useful to
+ * determine whether to invoke the task stack change listener after pausing.
+ */
+ boolean mAppVisibilitiesChangedSinceLastPause;
+
+ /**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
*/
@@ -1673,7 +1679,7 @@
if (task.mResizeable && options != null) {
int stackId = options.getLaunchStackId();
if (canUseActivityOptionsLaunchBounds(options, stackId)) {
- Rect bounds = options.getLaunchBounds();
+ final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds());
task.updateOverrideConfiguration(bounds);
if (stackId == INVALID_STACK_ID) {
stackId = task.getLaunchStackId();
@@ -1841,6 +1847,7 @@
// can have the right fullscreen state.
bounds = null;
}
+ bounds = TaskRecord.validateBounds(bounds);
mTmpBounds.clear();
mTmpConfigs.clear();
@@ -1857,8 +1864,8 @@
fitWithinBounds(tempRect2, bounds);
task.updateOverrideConfiguration(tempRect2);
} else {
- task.updateOverrideConfiguration(tempTaskBounds != null
- ? tempTaskBounds : bounds);
+ task.updateOverrideConfiguration(
+ tempTaskBounds != null ? tempTaskBounds : bounds);
}
}
@@ -1973,6 +1980,7 @@
// Nothing to do here...
return true;
}
+ bounds = TaskRecord.validateBounds(bounds);
if (!mWindowManager.isValidTaskId(task.taskId)) {
// Task doesn't exist in window manager yet (e.g. was restored from recents).
@@ -2722,7 +2730,7 @@
for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord s = mStoppingActivities.get(activityNdx);
final boolean waitingVisible = mWaitingVisibleActivities.contains(s);
- if (DEBUG_ALL) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+ if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+ " waitingVisible=" + waitingVisible + " finishing=" + s.finishing);
if (waitingVisible && nowVisible) {
mWaitingVisibleActivities.remove(s);
@@ -2732,12 +2740,12 @@
// so get rid of it. Otherwise, we need to go through the
// normal flow and hide it once we determine that it is
// hidden by the activities in front of it.
- if (DEBUG_ALL) Slog.v(TAG, "Before stopping, can hide: " + s);
+ if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
mWindowManager.setAppVisibility(s.appToken, false);
}
}
if ((!waitingVisible || mService.isSleepingOrShuttingDown()) && remove) {
- if (DEBUG_ALL) Slog.v(TAG, "Ready to stop: " + s);
+ if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
if (stops == null) {
stops = new ArrayList<>();
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index cfa4433..f144e0c 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -60,6 +60,7 @@
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
@@ -1701,7 +1702,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.isStackVisibleLocked()) {
+ if (stack != null && stack.getStackVisibilityLocked() == STACK_INVISIBLE) {
// There is a docked stack, but it isn't visible, so we can't launch into that.
return null;
} else {
@@ -1739,7 +1740,7 @@
if (options != null && (r.isResizeable() || (inTask != null && inTask.mResizeable))) {
if (mSupervisor.canUseActivityOptionsLaunchBounds(
options, options.getLaunchStackId())) {
- newBounds = options.getLaunchBounds();
+ newBounds = TaskRecord.validateBounds(options.getLaunchBounds());
}
}
return newBounds;
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 0397553..f2e8d09 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -100,6 +100,6 @@
30045 am_pre_boot (User|1|5),(Package|3)
# Report collection of global memory state
-30046 am_meminfo (CachedKb|2|2),(FreeKb|2|2),(ZramKb|2|2),(KernelKb|2|2),(NativeKb|2|2)
+30046 am_meminfo (Cached|2|2),(Free|2|2),(Zram|2|2),(Kernel|2|2),(Native|2|2)
# Report collection of memory used by a process
-30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(PssKb|2|2),(UssKb|2|2)
+30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(Pss|2|2),(Uss|2|2),(SwapPss|2|2)
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 4bfe300..b4aa4cf 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -82,7 +82,9 @@
long lastStateTime; // Last time setProcState changed
long initialIdlePss; // Initial memory pss of process for idle maintenance.
long lastPss; // Last computed memory pss.
+ long lastSwapPss; // Last computed SwapPss.
long lastCachedPss; // Last computed pss when in cached state.
+ long lastCachedSwapPss; // Last computed SwapPss when in cached state.
int maxAdj; // Maximum OOM adjustment for this process
int curRawAdj; // Current OOM unlimited adjustment for this process
int setRawAdj; // Last set OOM unlimited adjustment for this process
@@ -257,7 +259,9 @@
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.print(lruSeq);
pw.print(" lastPss="); DebugUtils.printSizeValue(pw, lastPss*1024);
+ pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, lastSwapPss*1024);
pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, lastCachedPss*1024);
+ pw.print(" lastCachedSwapPss="); DebugUtils.printSizeValue(pw, lastCachedSwapPss*1024);
pw.println();
pw.print(prefix); pw.print("cached="); pw.print(cached);
pw.print(" empty="); pw.println(empty);
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index ae987e6..f7e30c0 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
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;
@@ -1310,6 +1311,20 @@
return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
}
+ Rect updateOverrideConfigurationFromLaunchBounds() {
+ final Rect bounds = validateBounds(getLaunchBounds());
+ updateOverrideConfiguration(bounds);
+ return bounds;
+ }
+
+ static Rect validateBounds(Rect bounds) {
+ if (bounds != null && bounds.isEmpty()) {
+ Slog.wtf(TAG, "Received strange task bounds: " + bounds, new Throwable());
+ return null;
+ }
+ return bounds;
+ }
+
private void reportMultiWindowModeChange() {
for (int i = mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mActivities.get(i);
@@ -1386,17 +1401,20 @@
/** Returns the bounds that should be used to launch this task. */
Rect getLaunchBounds() {
- final int stackId = stack.mStackId;
-
// If we're over lockscreen, forget about stack bounds and use fullscreen.
if (mService.mLockScreenShown == LOCK_SCREEN_SHOWN) {
return null;
}
- if (stack == null
- || stackId == HOME_STACK_ID
- || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
- return (mResizeable && stack != null) ? stack.mBounds : null;
+ if (stack == null) {
+ return null;
+ }
+
+ final int stackId = stack.mStackId;
+ if (stackId == HOME_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID
+ || (stackId == DOCKED_STACK_ID && !mResizeable)) {
+ return mResizeable ? stack.mBounds : null;
} else if (!StackId.persistTaskBounds(stackId)) {
return stack.mBounds;
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 7172859..56757ca 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -143,6 +143,8 @@
private volatile UserManagerService mUserManager;
+ private final LockPatternUtils mLockPatternUtils;
+
UserController(ActivityManagerService service) {
mService = service;
mHandler = mService.mHandler;
@@ -150,6 +152,7 @@
final UserState uss = new UserState(UserHandle.SYSTEM);
mStartedUsers.put(UserHandle.USER_SYSTEM, uss);
mUserLru.add(UserHandle.USER_SYSTEM);
+ mLockPatternUtils = new LockPatternUtils(mService.mContext);
updateStartedUserArrayLocked();
}
@@ -1294,13 +1297,12 @@
* intercept activity launches for work apps when the Work Challenge is present.
*/
boolean shouldConfirmCredentials(int userId) {
- final UserInfo user = getUserInfo(userId);
- if (!user.isManagedProfile() || !LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+ if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
return false;
}
final KeyguardManager km = (KeyguardManager) mService.mContext
.getSystemService(KEYGUARD_SERVICE);
- return km.isDeviceLocked(user.id);
+ return km.isDeviceLocked(userId);
}
void dump(PrintWriter pw, boolean dumpAll) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b8cbecb..9331dd8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -58,10 +58,12 @@
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioPort;
+import android.media.AudioRecordConfiguration;
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
+import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
import android.media.IVolumeController;
import android.media.MediaPlayer;
@@ -706,6 +708,8 @@
LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
+
+ mRecordMonitor.initMonitor();
}
public void systemReady() {
@@ -6165,7 +6169,7 @@
}
//======================
- // Audio policy callback from AudioSystem
+ // Audio policy callbacks from AudioSystem for dynamic policies
//======================
private final AudioSystem.DynamicPolicyCallback mDynPolicyCallback =
new AudioSystem.DynamicPolicyCallback() {
@@ -6194,7 +6198,23 @@
}
}
}
+ }
+ //======================
+ // Audio policy callbacks from AudioSystem for recording configuration updates
+ //======================
+ private final RecordingActivityMonitor mRecordMonitor = new RecordingActivityMonitor();
+
+ public void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
+ mRecordMonitor.registerRecordingCallback(rcdb);
+ }
+
+ public void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) {
+ mRecordMonitor.unregisterRecordingCallback(rcdb);
+ }
+
+ public AudioRecordConfiguration[] getActiveRecordConfigurations() {
+ return mRecordMonitor.getActiveRecordConfigurations();
}
//======================
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
new file mode 100644
index 0000000..5806f3f
--- /dev/null
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.audio;
+
+import android.media.AudioManager;
+import android.media.AudioRecordConfiguration;
+import android.media.AudioSystem;
+import android.media.IRecordingConfigDispatcher;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Class to receive and dispatch updates from AudioSystem about recording configurations.
+ */
+public final class RecordingActivityMonitor implements AudioSystem.AudioRecordingCallback {
+
+ public final static String TAG = "AudioService.RecordingActivityMonitor";
+
+ private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>();
+
+ private HashMap<Integer, AudioRecordConfiguration> mRecordConfigs =
+ new HashMap<Integer, AudioRecordConfiguration>();
+
+ RecordingActivityMonitor() {
+ RecMonitorClient.sMonitor = this;
+ }
+
+ /**
+ * Implementation of android.media.AudioSystem.AudioRecordingCallback
+ */
+ public void onRecordingConfigurationChanged(int event, int session, int source) {
+ if (updateSnapshot(event, session, source)) {
+ final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
+ synchronized(mClients) {
+ while (clientIterator.hasNext()) {
+ try {
+ clientIterator.next().mDispatcherCb.dispatchRecordingConfigChange();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e);
+ }
+ }
+ }
+ }
+ }
+
+ void initMonitor() {
+ AudioSystem.setRecordingCallback(this);
+ }
+
+ void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
+ if (rcdb == null) {
+ return;
+ }
+ synchronized(mClients) {
+ final RecMonitorClient rmc = new RecMonitorClient(rcdb);
+ if (rmc.init()) {
+ mClients.add(rmc);
+ }
+ }
+ }
+
+ void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) {
+ if (rcdb == null) {
+ return;
+ }
+ synchronized(mClients) {
+ final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
+ while (clientIterator.hasNext()) {
+ RecMonitorClient rmc = clientIterator.next();
+ if (rcdb.equals(rmc.mDispatcherCb)) {
+ rmc.release();
+ clientIterator.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ AudioRecordConfiguration[] getActiveRecordConfigurations() {
+ synchronized(mRecordConfigs) {
+ return mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]);
+ }
+ }
+
+ /**
+ * Update the internal "view" of the active recording sessions
+ * @param event
+ * @param session
+ * @param source
+ * @return true if the list of active recording sessions has been modified, false otherwise.
+ */
+ private boolean updateSnapshot(int event, int session, int source) {
+ 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);
+ case AudioManager.RECORD_CONFIG_EVENT_START:
+ if (mRecordConfigs.containsKey(new Integer(session))) {
+ // start of session that's already tracked, not worth an update
+ // TO DO in the future when tracking record format: there might be a record
+ // format change during a recording that requires reporting
+ return false;
+ } else {
+ mRecordConfigs.put(new Integer(session),
+ new AudioRecordConfiguration(session, source));
+ return true;
+ }
+ default:
+ Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
+ event, session, source));
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Inner class to track clients that want to be notified of recording updates
+ */
+ private final static class RecMonitorClient implements IBinder.DeathRecipient {
+
+ // can afford to be static because only one RecordingActivityMonitor ever instantiated
+ static RecordingActivityMonitor sMonitor;
+
+ final IRecordingConfigDispatcher mDispatcherCb;
+
+ RecMonitorClient(IRecordingConfigDispatcher rcdb) {
+ mDispatcherCb = rcdb;
+ }
+
+ public void binderDied() {
+ Log.w(TAG, "client died");
+ sMonitor.unregisterRecordingCallback(mDispatcherCb);
+ }
+
+ boolean init() {
+ try {
+ mDispatcherCb.asBinder().linkToDeath(this, 0);
+ return true;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not link to client death", e);
+ return false;
+ }
+ }
+
+ void release() {
+ mDispatcherCb.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 3a10dbe..4504bdb 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -223,7 +223,6 @@
private final AlarmManager mAlarmManager;
private final NetworkRequest mDefaultRequest;
- private String mServer;
private boolean mIsCaptivePortalCheckEnabled = false;
// Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
@@ -265,10 +264,6 @@
addState(mLingeringState, mDefaultState);
setInitialState(mDefaultState);
- mServer = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.CAPTIVE_PORTAL_SERVER);
- if (mServer == null) mServer = DEFAULT_SERVER;
-
mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
@@ -622,6 +617,13 @@
}
}
+ public static String getCaptivePortalServerUrl(Context context) {
+ String server = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.CAPTIVE_PORTAL_SERVER);
+ if (server == null) server = DEFAULT_SERVER;
+ return "http://" + server + "/generate_204";
+ }
+
/**
* Do a URL fetch on a known server to see if we get the data we expect.
* Returns HTTP response code.
@@ -633,9 +635,9 @@
HttpURLConnection urlConnection = null;
int httpResponseCode = 599;
try {
- URL url = new URL("http", mServer, "/generate_204");
+ URL url = new URL(getCaptivePortalServerUrl(mContext));
// On networks with a PAC instead of fetching a URL that should result in a 204
- // reponse, we instead simply fetch the PAC script. This is done for a few reasons:
+ // response, we instead simply fetch the PAC script. This is done for a few reasons:
// 1. At present our PAC code does not yet handle multiple PACs on multiple networks
// until something like https://android-review.googlesource.com/#/c/115180/ lands.
// Network.openConnection() will ignore network-specific PACs and instead fetch
@@ -644,7 +646,8 @@
// 2. To proxy the generate_204 fetch through a PAC would require a number of things
// happen before the fetch can commence, namely:
// a) the PAC script be fetched
- // b) a PAC script resolver service be fired up and resolve mServer
+ // b) a PAC script resolver service be fired up and resolve the captive portal
+ // server.
// Network validation could be delayed until these prerequisities are satisifed or
// could simply be left to race them. Neither is an optimal solution.
// 3. PAC scripts are sometimes used to block or restrict Internet access and may in
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 5bd4f98..e957fc6 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -63,6 +63,7 @@
import android.os.SystemService;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
import android.text.TextUtils;
@@ -169,6 +170,58 @@
}
/**
+ * Configures an always-on VPN connection through a specific application.
+ * This connection is automatically granted and persisted after a reboot.
+ *
+ * <p>The designated package should exist and declare a {@link VpnService} in its
+ * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
+ * otherwise the call will fail.
+ *
+ * @param newPackage the package to designate as always-on VPN supplier.
+ */
+ public synchronized boolean setAlwaysOnPackage(String packageName) {
+ enforceControlPermissionOrInternalCaller();
+
+ // Disconnect current VPN.
+ prepareInternal(VpnConfig.LEGACY_VPN);
+
+ // Pre-authorize new always-on VPN package.
+ if (packageName != null) {
+ if (!setPackageAuthorization(packageName, true)) {
+ return false;
+ }
+ }
+
+ // Save the new package name in Settings.Secure.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ALWAYS_ON_VPN_APP, packageName, mUserHandle);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return true;
+ }
+
+ /**
+ * @return the package name of the VPN controller responsible for always-on VPN,
+ * or {@code null} if none is set or always-on VPN is controlled through
+ * lockdown instead.
+ * @hide
+ */
+ public synchronized String getAlwaysOnPackage() {
+ enforceControlPermissionOrInternalCaller();
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ALWAYS_ON_VPN_APP, mUserHandle);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
* Prepare for a VPN application. This method is designed to solve
* race conditions. It first compares the current prepared package
* with {@code oldPackage}. If they are the same, the prepared
@@ -270,14 +323,14 @@
/**
* Set whether a package has the ability to launch VPNs without user intervention.
*/
- public void setPackageAuthorization(String packageName, boolean authorized) {
+ public boolean setPackageAuthorization(String packageName, boolean authorized) {
// Check if the caller is authorized.
- enforceControlPermission();
+ enforceControlPermissionOrInternalCaller();
int uid = getAppUid(packageName, mUserHandle);
if (uid == -1 || VpnConfig.LEGACY_VPN.equals(packageName)) {
// Authorization for nonexistent packages (or fake ones) can't be updated.
- return;
+ return false;
}
long token = Binder.clearCallingIdentity();
@@ -286,11 +339,13 @@
(AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
appOps.setMode(AppOpsManager.OP_ACTIVATE_VPN, uid, packageName,
authorized ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
+ return true;
} catch (Exception e) {
Log.wtf(TAG, "Failed to set app ops for package " + packageName + ", uid " + uid, e);
} finally {
Binder.restoreCallingIdentity(token);
}
+ return false;
}
private boolean isVpnUserPreConsented(String packageName) {
@@ -743,6 +798,13 @@
mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
}
+ private void enforceControlPermissionOrInternalCaller() {
+ // Require caller to be either an application with CONTROL_VPN permission or a process
+ // in the system server.
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.CONTROL_VPN,
+ "Unauthorized Caller");
+ }
+
private class Connection implements ServiceConnection {
private IBinder mService;
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 78618ce..2eb9095 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2682,6 +2682,31 @@
}
continue;
}
+ String packageName = getPackageName(op.target);
+ ApplicationInfo ai = null;
+ if (packageName != null) {
+ try {
+ ai = mContext.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES
+ | PackageManager.GET_DISABLED_COMPONENTS);
+ } catch (NameNotFoundException e) {
+ operationIterator.remove();
+ mSyncStorageEngine.deleteFromPending(op.pendingOperation);
+ continue;
+ }
+ }
+ // If app is considered idle, then skip for now and backoff
+ if (ai != null
+ && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) {
+ increaseBackoffSetting(op);
+ op.appIdle = true;
+ if (isLoggable) {
+ Log.v(TAG, "Sync backing off idle app " + packageName);
+ }
+ continue;
+ } else {
+ op.appIdle = false;
+ }
if (!isOperationValidLocked(op)) {
operationIterator.remove();
mSyncStorageEngine.deleteFromPending(op.pendingOperation);
@@ -2700,28 +2725,6 @@
}
continue;
}
- String packageName = getPackageName(op.target);
- ApplicationInfo ai = null;
- if (packageName != null) {
- try {
- ai = mContext.getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES
- | PackageManager.GET_DISABLED_COMPONENTS);
- } catch (NameNotFoundException e) {
- }
- }
- // If app is considered idle, then skip for now and backoff
- if (ai != null
- && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) {
- increaseBackoffSetting(op);
- op.appIdle = true;
- if (isLoggable) {
- Log.v(TAG, "Sync backing off idle app " + packageName);
- }
- continue;
- } else {
- op.appIdle = false;
- }
// Add this sync to be run.
operations.add(op);
}
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 835ba17..a16fcd2 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -587,16 +587,16 @@
Slog.e(TAG, "Unable to create surface.", ex);
return false;
}
+
+ mSurfaceControl.setLayerStack(mDisplayLayerStack);
+ mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
+ mSurface = new Surface();
+ mSurface.copyFrom(mSurfaceControl);
+
+ mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
+ mDisplayId, mSurfaceControl);
+ mSurfaceLayout.onDisplayTransaction();
}
-
- mSurfaceControl.setLayerStack(mDisplayLayerStack);
- mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
- mSurface = new Surface();
- mSurface.copyFrom(mSurfaceControl);
-
- mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
- mDisplayId, mSurfaceControl);
- mSurfaceLayout.onDisplayTransaction();
} finally {
SurfaceControl.closeTransaction();
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 433d887..b74b0f2 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -673,6 +673,9 @@
slowChange = false;
}
mAppliedDimming = true;
+ } else if (mAppliedDimming) {
+ slowChange = false;
+ mAppliedDimming = false;
}
// If low power mode is enabled, cut the brightness level by half
@@ -685,6 +688,9 @@
slowChange = false;
}
mAppliedLowPower = true;
+ } else if (mAppliedLowPower) {
+ slowChange = false;
+ mAppliedLowPower = false;
}
// Animate the screen brightness when the screen is on or dozing.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 3530d80..a6db613 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -16,14 +16,21 @@
package com.android.server.job;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.IUidObserver;
-import android.app.job.IJobScheduler;
import android.app.job.JobInfo;
+import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
+import android.app.job.IJobScheduler;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -47,7 +54,6 @@
import android.util.SparseArray;
import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.ArrayUtils;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
import com.android.server.job.controllers.AppIdleController;
@@ -58,15 +64,6 @@
import com.android.server.job.controllers.StateController;
import com.android.server.job.controllers.TimeController;
-import libcore.util.EmptyArray;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
/**
* Responsible for taking jobs representing work to be performed by a client app, and determining
* based on the criteria specified when that job should be run against the client application's
@@ -130,7 +127,7 @@
*/
final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
- int[] mStartedUsers = EmptyArray.INT;
+ final ArrayList<Integer> mStartedUsers = new ArrayList<>();
final JobHandler mHandler;
final JobSchedulerStub mJobSchedulerStub;
@@ -161,9 +158,8 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- Slog.d(TAG, "Receieved: " + action);
- if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ Slog.d(TAG, "Receieved: " + intent.getAction());
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
// If this is an outright uninstall rather than the first half of an
// app update sequence, cancel the jobs associated with the app.
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
@@ -173,21 +169,18 @@
}
cancelJobsForUid(uidRemoved, true);
}
- } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
if (DEBUG) {
Slog.d(TAG, "Removing jobs for user: " + userId);
}
cancelJobsForUser(userId);
- } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)
- || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())
+ || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) {
updateIdleMode(mPowerManager != null
? (mPowerManager.isDeviceIdleMode()
- || mPowerManager.isLightDeviceIdleMode())
+ || mPowerManager.isLightDeviceIdleMode())
: false);
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- // Kick off pending jobs for any apps that re-appeared
- mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
}
};
@@ -209,20 +202,14 @@
@Override
public void onStartUser(int userHandle) {
- mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
- // Let's kick any outstanding jobs for this user.
- mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
- }
-
- @Override
- public void onUnlockUser(int userHandle) {
+ mStartedUsers.add(userHandle);
// Let's kick any outstanding jobs for this user.
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
@Override
public void onStopUser(int userHandle) {
- mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
+ mStartedUsers.remove(Integer.valueOf(userHandle));
}
/**
@@ -329,7 +316,7 @@
// Remove from pending queue.
mPendingJobs.remove(cancelled);
// Cancel if running.
- stopJobOnServiceContextLocked(cancelled);
+ stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
reportActive();
}
}
@@ -357,7 +344,7 @@
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus executing = jsc.getRunningJob();
if (executing != null) {
- jsc.cancelExecutingJob();
+ jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
}
}
} else {
@@ -382,7 +369,7 @@
if (mPendingJobs.size() <= 0) {
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
- if (!jsc.isAvailable()) {
+ if (jsc.getRunningJob() != null) {
active = true;
break;
}
@@ -429,24 +416,17 @@
@Override
public void onBootPhase(int phase) {
if (PHASE_SYSTEM_SERVICES_READY == phase) {
- // Register for package removals and user removals.
+ // Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
-
final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
userFilter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
-
- final IntentFilter storageFilter = new IntentFilter();
- storageFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- getContext().registerReceiverAsUser(
- mBroadcastReceiver, UserHandle.ALL, storageFilter, null, null);
-
- mPowerManager = getContext().getSystemService(PowerManager.class);
+ mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
try {
ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_IDLE);
@@ -526,12 +506,12 @@
return removed;
}
- private boolean stopJobOnServiceContextLocked(JobStatus job) {
+ private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus executing = jsc.getRunningJob();
if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
- jsc.cancelExecutingJob();
+ jsc.cancelExecutingJob(reason);
return true;
}
}
@@ -731,6 +711,7 @@
*/
private void queueReadyJobsForExecutionLockedH() {
ArraySet<JobStatus> jobs = mJobs.getJobs();
+ mPendingJobs.clear();
if (DEBUG) {
Slog.d(TAG, "queuing all ready jobs for execution:");
}
@@ -741,8 +722,9 @@
Slog.d(TAG, " queued " + job.toShortString());
}
mPendingJobs.add(job);
- } else if (isReadyToBeCancelledLocked(job)) {
- stopJobOnServiceContextLocked(job);
+ } else if (areJobConstraintsNotSatisfied(job)) {
+ stopJobOnServiceContextLocked(job,
+ JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
}
}
if (DEBUG) {
@@ -765,8 +747,9 @@
* TODO: It would be nice to consolidate these sort of high-level policies somewhere.
*/
private void maybeQueueReadyJobsForExecutionLockedH() {
+ mPendingJobs.clear();
int chargingCount = 0;
- int idleCount = 0;
+ int idleCount = 0;
int backoffCount = 0;
int connectivityCount = 0;
List<JobStatus> runnableJobs = null;
@@ -801,8 +784,9 @@
runnableJobs = new ArrayList<>();
}
runnableJobs.add(job);
- } else if (isReadyToBeCancelledLocked(job)) {
- stopJobOnServiceContextLocked(job);
+ } else if (areJobConstraintsNotSatisfied(job)) {
+ stopJobOnServiceContextLocked(job,
+ JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
}
}
if (backoffCount > 0 ||
@@ -821,11 +805,6 @@
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
}
}
- if (DEBUG) {
- Slog.d(TAG, "idle=" + idleCount + " connectivity=" +
- connectivityCount + " charging=" + chargingCount + " tot=" +
- runnableJobs.size());
- }
}
/**
@@ -834,31 +813,18 @@
* - It's not pending.
* - It's not already running on a JSC.
* - The user that requested the job is running.
- * - The component is enabled and runnable.
*/
private boolean isReadyToBeExecutedLocked(JobStatus job) {
final boolean jobReady = job.isReady();
final boolean jobPending = mPendingJobs.contains(job);
final boolean jobActive = isCurrentlyActiveLocked(job);
-
- final int userId = job.getUserId();
- final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
- final boolean componentPresent;
- try {
- componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
- job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
- userId) != null);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
-
+ final boolean userRunning = mStartedUsers.contains(job.getUserId());
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ " ready=" + jobReady + " pending=" + jobPending
- + " active=" + jobActive + " userStarted=" + userStarted
- + " componentPresent=" + componentPresent);
+ + " active=" + jobActive + " userRunning=" + userRunning);
}
- return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
+ return userRunning && jobReady && !jobPending && !jobActive;
}
/**
@@ -866,7 +832,7 @@
* - It's not ready
* - It's running on a JSC.
*/
- private boolean isReadyToBeCancelledLocked(JobStatus job) {
+ private boolean areJobConstraintsNotSatisfied(JobStatus job) {
return !job.isReady() && isCurrentlyActiveLocked(job);
}
@@ -881,46 +847,121 @@
// If device is idle, we will not schedule jobs to run.
return;
}
- Iterator<JobStatus> it = mPendingJobs.iterator();
if (DEBUG) {
Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
}
- while (it.hasNext()) {
- JobStatus nextPending = it.next();
- JobServiceContext availableContext = null;
- for (int i=0; i<mActiveServices.size(); i++) {
- JobServiceContext jsc = mActiveServices.get(i);
- final JobStatus running = jsc.getRunningJob();
- if (running != null && running.matches(nextPending.getUid(),
- nextPending.getJobId())) {
- // Already running this job for this uId, skip.
- availableContext = null;
- break;
- }
- if (jsc.isAvailable()) {
- availableContext = jsc;
- }
- }
- if (availableContext != null) {
- if (DEBUG) {
- Slog.d(TAG, "About to run job "
- + nextPending.getJob().getService().toString());
- }
- if (!availableContext.executeRunnableJob(nextPending)) {
- if (DEBUG) {
- Slog.d(TAG, "Error executing " + nextPending);
- }
- mJobs.remove(nextPending);
- }
- it.remove();
- }
- }
+ assignJobsToContextsH();
reportActive();
}
}
}
/**
+ * Takes jobs from pending queue and runs them on available contexts.
+ * If no contexts are available, preempts lower priority jobs to
+ * run higher priority ones.
+ * Lock on mJobs before calling this function.
+ */
+ private void assignJobsToContextsH() {
+ if (DEBUG) {
+ Slog.d(TAG, printPendingQueue());
+ }
+
+ // This array essentially stores the state of mActiveServices array.
+ // ith index stores the job present on the ith JobServiceContext.
+ // We manipulate this array until we arrive at what jobs should be running on
+ // what JobServiceContext.
+ JobStatus[] contextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
+ // Indicates whether we need to act on this jobContext id
+ boolean[] act = new boolean[MAX_JOB_CONTEXTS_COUNT];
+ int[] preferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
+ for (int i=0; i<mActiveServices.size(); i++) {
+ contextIdToJobMap[i] = mActiveServices.get(i).getRunningJob();
+ preferredUidForContext[i] = mActiveServices.get(i).getPreferredUid();
+ }
+ if (DEBUG) {
+ Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
+ }
+ Iterator<JobStatus> it = mPendingJobs.iterator();
+ while (it.hasNext()) {
+ JobStatus nextPending = it.next();
+
+ // If job is already running, go to next job.
+ int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
+ if (jobRunningContext != -1) {
+ continue;
+ }
+
+ // Find a context for nextPending. The context should be available OR
+ // it should have lowest priority among all running jobs
+ // (sharing the same Uid as nextPending)
+ int minPriority = Integer.MAX_VALUE;
+ int minPriorityContextId = -1;
+ for (int i=0; i<mActiveServices.size(); i++) {
+ JobStatus job = contextIdToJobMap[i];
+ int preferredUid = preferredUidForContext[i];
+ if (job == null && (preferredUid == nextPending.getUid() ||
+ preferredUid == JobServiceContext.NO_PREFERRED_UID) ) {
+ minPriorityContextId = i;
+ break;
+ }
+ if (job.getUid() != nextPending.getUid()) {
+ continue;
+ }
+ if (job.getPriority() >= nextPending.getPriority()) {
+ continue;
+ }
+ if (minPriority > nextPending.getPriority()) {
+ minPriority = nextPending.getPriority();
+ minPriorityContextId = i;
+ }
+ }
+ if (minPriorityContextId != -1) {
+ contextIdToJobMap[minPriorityContextId] = nextPending;
+ act[minPriorityContextId] = true;
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
+ }
+ for (int i=0; i<mActiveServices.size(); i++) {
+ boolean preservePreferredUid = false;
+ if (act[i]) {
+ JobStatus js = mActiveServices.get(i).getRunningJob();
+ if (js != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
+ }
+ // preferredUid will be set to uid of currently running job.
+ mActiveServices.get(i).preemptExecutingJob();
+ preservePreferredUid = true;
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "About to run job on context "
+ + String.valueOf(i) + ", job: " + contextIdToJobMap[i]);
+ }
+ if (!mActiveServices.get(i).executeRunnableJob(contextIdToJobMap[i])) {
+ Slog.d(TAG, "Error executing " + contextIdToJobMap[i]);
+ }
+ mPendingJobs.remove(contextIdToJobMap[i]);
+ }
+ }
+ if (!preservePreferredUid) {
+ mActiveServices.get(i).clearPreferredUid();
+ }
+ }
+ }
+
+ int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
+ for (int i=0; i<map.length; i++) {
+ if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
* Binder stub trampoline implementation
*/
final class JobSchedulerStub extends IJobScheduler.Stub {
@@ -936,8 +977,7 @@
final IPackageManager pm = AppGlobals.getPackageManager();
final ComponentName service = job.getService();
try {
- ServiceInfo si = pm.getServiceInfo(service,
- PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(uid));
+ ServiceInfo si = pm.getServiceInfo(service, 0, UserHandle.getUserId(uid));
if (si == null) {
throw new IllegalArgumentException("No such service " + service);
}
@@ -1050,12 +1090,41 @@
Binder.restoreCallingIdentity(identityToken);
}
}
+ };
+
+ private String printContextIdToJobMap(JobStatus[] map, String initial) {
+ StringBuilder s = new StringBuilder(initial + ": ");
+ for (int i=0; i<map.length; i++) {
+ s.append("(")
+ .append(map[i] == null? -1: map[i].getJobId())
+ .append(map[i] == null? -1: map[i].getUid())
+ .append(")" );
+ }
+ return s.toString();
+ }
+
+ private String printPendingQueue() {
+ StringBuilder s = new StringBuilder("Pending queue: ");
+ Iterator<JobStatus> it = mPendingJobs.iterator();
+ while (it.hasNext()) {
+ JobStatus js = it.next();
+ s.append("(")
+ .append(js.getJob().getId())
+ .append(", ")
+ .append(js.getUid())
+ .append(") ");
+ }
+ return s.toString();
}
void dumpInternal(PrintWriter pw) {
final long now = SystemClock.elapsedRealtime();
synchronized (mJobs) {
- pw.println("Started users: " + Arrays.toString(mStartedUsers));
+ pw.print("Started users: ");
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ pw.print("u" + mStartedUsers.get(i) + " ");
+ }
+ pw.println();
pw.println("Registered jobs:");
if (mJobs.size() > 0) {
ArraySet<JobStatus> jobs = mJobs.getJobs();
@@ -1071,15 +1140,12 @@
mControllers.get(i).dumpControllerState(pw);
}
pw.println();
- pw.println("Pending:");
- for (int i=0; i<mPendingJobs.size(); i++) {
- pw.println(mPendingJobs.get(i).hashCode());
- }
+ pw.println(printPendingQueue());
pw.println();
pw.println("Active jobs:");
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext jsc = mActiveServices.get(i);
- if (jsc.isAvailable()) {
+ if (jsc.getRunningJob() == null) {
continue;
} else {
final long timeout = jsc.getTimeoutElapsed();
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 5376043..5935319e 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -59,7 +59,6 @@
* To mitigate this, tearing down the context removes all messages from the handler, including any
* tardy {@link #MSG_CANCEL}s. Additionally, we avoid sending duplicate onStopJob()
* calls to the client after they've specified jobFinished().
- *
*/
public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
private static final boolean DEBUG = JobSchedulerService.DEBUG;
@@ -95,6 +94,8 @@
/** Shutdown the job. Used when the client crashes and we can't die gracefully.*/
private static final int MSG_SHUTDOWN_EXECUTION = 4;
+ public static final int NO_PREFERRED_UID = -1;
+
private final Handler mCallbackHandler;
/** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
private final JobCompletedListener mCompletedListener;
@@ -117,7 +118,8 @@
* Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}.
*/
private JobStatus mRunningJob;
- /** Binder to the client service. */
+ /** Used to store next job to run when current job is to be preempted. */
+ private int mPreferredUid;
IJobService service;
private final Object mLock = new Object();
@@ -138,17 +140,19 @@
@VisibleForTesting
JobServiceContext(Context context, IBatteryStats batteryStats,
- JobCompletedListener completedListener, Looper looper) {
+ JobCompletedListener completedListener, Looper looper) {
mContext = context;
mBatteryStats = batteryStats;
mCallbackHandler = new JobServiceHandler(looper);
mCompletedListener = completedListener;
mAvailable = true;
+ mVerb = VERB_FINISHED;
+ mPreferredUid = NO_PREFERRED_UID;
}
/**
- * Give a job to this context for execution. Callers must first check {@link #isAvailable()}
- * to make sure this is a valid context.
+ * Give a job to this context for execution. Callers must first check {@link #getRunningJob()}
+ * and ensure it is null to make sure this is a valid context.
* @param job The status of the job that we are going to run.
* @return True if the job is valid and is running. False if the job cannot be executed.
*/
@@ -159,6 +163,8 @@
return false;
}
+ mPreferredUid = NO_PREFERRED_UID;
+
mRunningJob = job;
final boolean isDeadlineExpired =
job.hasDeadlineConstraint() &&
@@ -206,17 +212,22 @@
}
/** Called externally when a job that was scheduled for execution should be cancelled. */
- void cancelExecutingJob() {
- mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget();
+ void cancelExecutingJob(int reason) {
+ mCallbackHandler.obtainMessage(MSG_CANCEL, reason, 0 /* unused */).sendToTarget();
}
- /**
- * @return Whether this context is available to handle incoming work.
- */
- boolean isAvailable() {
- synchronized (mLock) {
- return mAvailable;
- }
+ void preemptExecutingJob() {
+ Message m = mCallbackHandler.obtainMessage(MSG_CANCEL);
+ m.arg1 = JobParameters.REASON_PREEMPT;
+ m.sendToTarget();
+ }
+
+ int getPreferredUid() {
+ return mPreferredUid;
+ }
+
+ void clearPreferredUid() {
+ mPreferredUid = NO_PREFERRED_UID;
}
long getExecutionStartTimeElapsed() {
@@ -344,6 +355,11 @@
}
break;
case MSG_CANCEL:
+ mParams.setStopReason(message.arg1);
+ if (message.arg1 == JobParameters.REASON_PREEMPT) {
+ mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
+ NO_PREFERRED_UID;
+ }
handleCancelH();
break;
case MSG_TIMEOUT:
@@ -481,6 +497,7 @@
/** Process MSG_TIMEOUT here. */
private void handleOpTimeoutH() {
+ mParams.setStopReason(JobParameters.REASON_TIMEOUT);
switch (mVerb) {
case VERB_BINDING:
Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index b8aa9dd..a8cb19f 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -312,7 +312,7 @@
Slog.d(TAG, "Saving job " + jobStatus.getJobId());
}
out.startTag(null, "job");
- addIdentifierAttributesToJobTag(out, jobStatus);
+ addAttributesToJobTag(out, jobStatus);
writeConstraintsToXml(out, jobStatus);
writeExecutionCriteriaToXml(out, jobStatus);
writeBundleToXml(jobStatus.getExtras(), out);
@@ -337,13 +337,16 @@
}
}
- /** Write out a tag with data comprising the required fields of this job and its client. */
- private void addIdentifierAttributesToJobTag(XmlSerializer out, JobStatus jobStatus)
+ /** Write out a tag with data comprising the required fields and priority of this job and
+ * its client.
+ */
+ private void addAttributesToJobTag(XmlSerializer out, JobStatus jobStatus)
throws IOException {
out.attribute(null, "jobid", Integer.toString(jobStatus.getJobId()));
out.attribute(null, "package", jobStatus.getServiceComponent().getPackageName());
out.attribute(null, "class", jobStatus.getServiceComponent().getClassName());
out.attribute(null, "uid", Integer.toString(jobStatus.getUid()));
+ out.attribute(null, "priority", String.valueOf(jobStatus.getPriority()));
}
private void writeBundleToXml(PersistableBundle extras, XmlSerializer out)
@@ -361,9 +364,9 @@
PersistableBundle copy = (PersistableBundle) bundle.clone();
Set<String> keySet = bundle.keySet();
for (String key: keySet) {
- PersistableBundle b = copy.getPersistableBundle(key);
- if (b != null) {
- PersistableBundle bCopy = deepCopyBundle(b, maxDepth-1);
+ Object o = copy.get(key);
+ if (o instanceof PersistableBundle) {
+ PersistableBundle bCopy = deepCopyBundle((PersistableBundle) o, maxDepth-1);
copy.putPersistableBundle(key, bCopy);
}
}
@@ -541,11 +544,16 @@
JobInfo.Builder jobBuilder;
int uid;
- // Read out job identifier attributes.
+ // Read out job identifier attributes and priority.
try {
jobBuilder = buildBuilderFromXml(parser);
jobBuilder.setPersisted(true);
uid = Integer.valueOf(parser.getAttributeValue(null, "uid"));
+
+ String priority = parser.getAttributeValue(null, "priority");
+ if (priority != null) {
+ jobBuilder.setPriority(Integer.valueOf(priority));
+ }
} catch (NumberFormatException e) {
Slog.e(TAG, "Error parsing job's required fields, skipping");
return null;
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 060a93e..56d92d5 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -165,6 +165,10 @@
public PersistableBundle getExtras() {
return job.getExtras();
}
+
+ public int getPriority() {
+ return job.getPriority();
+ }
public boolean hasConnectivityConstraint() {
return job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY;
diff --git a/services/core/java/com/android/server/media/MediaResourceMonitorService.java b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
new file mode 100644
index 0000000..50dd607
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaResourceMonitorService.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 com.android.server.media;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.Intent;
+import android.media.IMediaResourceMonitor;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.SystemService;
+
+import java.util.List;
+
+/** This class provides a system service that monitors media resource usage. */
+public class MediaResourceMonitorService extends SystemService {
+ private static final String TAG = "MediaResourceMonitor";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final String SERVICE_NAME = "media_resource_monitor";
+
+ /*
+ * Resource types. Should be in sync with:
+ * frameworks/av/media/libmedia/MediaResource.cpp
+ */
+ private static final String RESOURCE_AUDIO_CODEC = "audio-codec";
+ private static final String RESOURCE_VIDEO_CODEC = "video-codec";
+
+ private final MediaResourceMonitorImpl mMediaResourceMonitorImpl;
+
+ public MediaResourceMonitorService(Context context) {
+ super(context);
+ mMediaResourceMonitorImpl = new MediaResourceMonitorImpl();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(SERVICE_NAME, mMediaResourceMonitorImpl);
+ }
+
+ class MediaResourceMonitorImpl extends IMediaResourceMonitor.Stub {
+ @Override
+ public void notifyResourceGranted(int pid, String type, String subType, long value)
+ throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyResourceGranted(pid=" + pid + ", type=" + type + ", subType="
+ + subType + ", value=" + value + ")");
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ String pkgNames[] = getPackageNamesFromPid(pid);
+ Integer resourceType = null;
+ if (RESOURCE_AUDIO_CODEC.equals(subType)) {
+ resourceType = Intent.EXTRA_MEDIA_RESOURCE_TYPE_AUDIO_CODEC;
+ } else if (RESOURCE_VIDEO_CODEC.equals(subType)) {
+ resourceType = Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC;
+ }
+ if (pkgNames != null && resourceType != null) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
+ intent.putExtra(Intent.EXTRA_PACKAGES, pkgNames);
+ intent.putExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE, resourceType);
+ getContext().sendBroadcastAsUser(intent,
+ new UserHandle(ActivityManager.getCurrentUser()),
+ android.Manifest.permission.RECEIVE_MEDIA_RESOURCE_USAGE);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private String[] getPackageNamesFromPid(int pid) {
+ try {
+ for (ActivityManager.RunningAppProcessInfo proc :
+ ActivityManagerNative.getDefault().getRunningAppProcesses()) {
+ if (proc.pid == pid) {
+ return proc.pkgList;
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "ActivityManager.getRunningAppProcesses() failed");
+ }
+ return null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 018bf2d..b1fe68c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1727,6 +1727,14 @@
}
@Override
+ public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
+ Preconditions.checkNotNull(owner, "Owner is null");
+ enforceSystemOrSystemUI("getRuleInstanceCount");
+
+ return mZenModeHelper.getCurrentInstanceCount(owner);
+ }
+
+ @Override
public void setInterruptionFilter(String pkg, int filter) throws RemoteException {
enforcePolicyAccess(pkg, "setInterruptionFilter");
final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f7043a6..1d91fb7 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -27,7 +27,10 @@
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.res.Resources;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
@@ -45,7 +48,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings.Global;
-import android.service.notification.IConditionListener;
+import android.service.notification.ConditionProviderService;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.EventInfo;
import android.service.notification.ZenModeConfig.ScheduleInfo;
@@ -91,6 +94,7 @@
private final ZenModeConditions mConditions;
private final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private final Metrics mMetrics = new Metrics();
+ private final ConditionProviders.Config mServiceConfig;
private int mZenMode;
private int mUser = UserHandle.USER_SYSTEM;
@@ -113,6 +117,7 @@
mSettingsObserver.observe();
mFiltering = new ZenModeFiltering(mContext);
mConditions = new ZenModeConditions(this, conditionProviders);
+ mServiceConfig = conditionProviders.getConfig();
}
public Looper getLooper() {
@@ -197,7 +202,7 @@
config.user = user;
}
synchronized (mConfig) {
- setConfig(config, "onUserSwitched");
+ setConfigLocked(config, "onUserSwitched");
}
cleanUpZenRules();
}
@@ -257,22 +262,34 @@
}
public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+ if (!TextUtils.isEmpty(automaticZenRule.getId())) {
+ throw new IllegalArgumentException("Rule already exists");
+ }
+ if (!isSystemRule(automaticZenRule)) {
+ ServiceInfo owner = getServiceInfo(automaticZenRule.getOwner());
+ if (owner == null) {
+ throw new IllegalArgumentException("Owner is not a condition provider service");
+ }
+
+ final int ruleInstanceLimit = owner.metaData.getInt(
+ ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
+ if (ruleInstanceLimit > 0 && ruleInstanceLimit
+ < (getCurrentInstanceCount(automaticZenRule.getOwner()) + 1)) {
+ throw new IllegalArgumentException("Rule instance limit exceeded");
+ }
+ }
+
ZenModeConfig newConfig;
synchronized (mConfig) {
if (mConfig == null) return null;
if (DEBUG) {
- Log.d(TAG,
- "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" + reason);
- }
- if (!TextUtils.isEmpty(automaticZenRule.getId())) {
- throw new IllegalArgumentException("Rule already exists");
+ Log.d(TAG, "addAutomaticZenRule rule= " + automaticZenRule + " reason=" + reason);
}
newConfig = mConfig.copy();
-
ZenRule rule = new ZenRule();
populateZenRule(automaticZenRule, rule, true);
newConfig.automaticRules.put(rule.id, rule);
- if (setConfig(newConfig, reason, true)) {
+ if (setConfigLocked(newConfig, reason, true)) {
return createAutomaticZenRule(rule);
} else {
return null;
@@ -302,7 +319,7 @@
}
populateZenRule(automaticZenRule, rule, false);
newConfig.automaticRules.put(ruleId, rule);
- return setConfig(newConfig, reason, true);
+ return setConfigLocked(newConfig, reason, true);
}
}
@@ -320,7 +337,7 @@
throw new SecurityException(
"Cannot delete rules not owned by your condition provider");
}
- return setConfig(newConfig, reason, true);
+ return setConfigLocked(newConfig, reason, true);
}
}
@@ -336,10 +353,22 @@
newConfig.automaticRules.removeAt(i);
}
}
- return setConfig(newConfig, reason, true);
+ return setConfigLocked(newConfig, reason, true);
}
}
+ public int getCurrentInstanceCount(ComponentName owner) {
+ int count = 0;
+ synchronized (mConfig) {
+ for (ZenRule rule : mConfig.automaticRules.values()) {
+ if (rule.component != null && rule.component.equals(owner)) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
public boolean canManageAutomaticZenRule(ZenRule rule) {
final int callingUid = Binder.getCallingUid();
if (callingUid == 0 || callingUid == Process.SYSTEM_UID) {
@@ -361,6 +390,29 @@
}
}
+ private boolean isSystemRule(AutomaticZenRule rule) {
+ return ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
+ }
+
+ private ServiceInfo getServiceInfo(ComponentName owner) {
+ Intent queryIntent = new Intent();
+ queryIntent.setComponent(owner);
+ List<ResolveInfo> installedServices = mPm.queryIntentServicesAsUser(
+ queryIntent,
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+ UserHandle.getCallingUserId());
+ if (installedServices != null) {
+ for (int i = 0, count = installedServices.size(); i < count; i++) {
+ ResolveInfo resolveInfo = installedServices.get(i);
+ ServiceInfo info = resolveInfo.serviceInfo;
+ if (mServiceConfig.bindPermission.equals(info.permission)) {
+ return info;
+ }
+ }
+ }
+ return null;
+ }
+
private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
if (isNew) {
rule.id = ZenModeConfig.newRuleId();
@@ -413,7 +465,7 @@
newRule.conditionId = conditionId;
newConfig.manualRule = newRule;
}
- setConfig(newConfig, reason, setRingerMode);
+ setConfigLocked(newConfig, reason, setRingerMode);
}
}
@@ -478,7 +530,7 @@
}
if (DEBUG) Log.d(TAG, "readXml");
synchronized (mConfig) {
- setConfig(config, "readXml");
+ setConfigLocked(config, "readXml");
}
}
}
@@ -507,7 +559,7 @@
synchronized (mConfig) {
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
- setConfig(newConfig, "setNotificationPolicy");
+ setConfigLocked(newConfig, "setNotificationPolicy");
}
}
@@ -530,7 +582,7 @@
}
}
}
- setConfig(newConfig, "cleanUpZenRules");
+ setConfigLocked(newConfig, "cleanUpZenRules");
}
}
@@ -543,30 +595,30 @@
}
}
- public boolean setConfig(ZenModeConfig config, String reason) {
- return setConfig(config, reason, true /*setRingerMode*/);
+ public boolean setConfigLocked(ZenModeConfig config, String reason) {
+ return setConfigLocked(config, reason, true /*setRingerMode*/);
}
public void setConfigAsync(ZenModeConfig config, String reason) {
mHandler.postSetConfig(config, reason);
}
- private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
+ private boolean setConfigLocked(ZenModeConfig config, String reason, boolean setRingerMode) {
final long identity = Binder.clearCallingIdentity();
try {
if (config == null || !config.isValid()) {
- Log.w(TAG, "Invalid config in setConfig; " + config);
+ Log.w(TAG, "Invalid config in setConfigLocked; " + config);
return false;
}
if (config.user != mUser) {
// simply store away for background users
mConfigs.put(config.user, config);
- if (DEBUG) Log.d(TAG, "setConfig: store config for user " + config.user);
+ if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
return true;
}
mConditions.evaluateConfig(config, false /*processSubscriptions*/); // may modify config
mConfigs.put(config.user, config);
- if (DEBUG) Log.d(TAG, "setConfig reason=" + reason, new Throwable());
+ if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
ZenLog.traceConfig(reason, mConfig, config);
final boolean policyChanged = !Objects.equals(getNotificationPolicy(mConfig),
getNotificationPolicy(config));
@@ -1041,7 +1093,7 @@
case MSG_SET_CONFIG:
ConfigMessageData configData = (ConfigMessageData)msg.obj;
synchronized (mConfig) {
- setConfig(configData.config, configData.reason);
+ setConfigLocked(configData.config, configData.reason);
}
break;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 99a051a..190eca6 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -20,14 +20,14 @@
import android.content.Context;
import android.content.pm.PackageStats;
import android.os.Build;
-import android.text.TextUtils;
import android.util.Slog;
-import dalvik.system.VMRuntime;
-
import com.android.internal.os.InstallerConnection;
+import com.android.internal.os.InstallerConnection.InstallerException;
import com.android.server.SystemService;
+import dalvik.system.VMRuntime;
+
public final class Installer extends SystemService {
private static final String TAG = "Installer";
@@ -46,6 +46,11 @@
/** Run the application with the JIT compiler */
public static final int DEXOPT_USEJIT = 1 << 5;
+ public static final int FLAG_DE_STORAGE = 1 << 0;
+ public static final int FLAG_CE_STORAGE = 1 << 1;
+ public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 2;
+ public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 3;
+
private final InstallerConnection mInstaller;
public Installer(Context context) {
@@ -67,423 +72,137 @@
mInstaller.waitForConnection();
}
- private static String escapeNull(String arg) {
- if (TextUtils.isEmpty(arg)) {
- return "!";
- } else {
- if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) {
- throw new IllegalArgumentException(arg);
- }
- return arg;
- }
+ public void createAppData(String uuid, String pkgname, int userid, int flags, int appid,
+ String seinfo) throws InstallerException {
+ mInstaller.execute("create_app_data", uuid, pkgname, userid, flags, appid, seinfo);
}
- @Deprecated
- public int install(String name, int uid, int gid, String seinfo) {
- return install(null, name, uid, gid, seinfo);
+ public void restoreconAppData(String uuid, String pkgname, int userid, int flags, int appid,
+ String seinfo) throws InstallerException {
+ mInstaller.execute("restorecon_app_data", uuid, pkgname, userid, flags, appid,
+ seinfo);
}
- public int install(String uuid, String name, int uid, int gid, String seinfo) {
- StringBuilder builder = new StringBuilder("install");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(name);
- builder.append(' ');
- builder.append(uid);
- builder.append(' ');
- builder.append(gid);
- builder.append(' ');
- builder.append(seinfo != null ? seinfo : "!");
- return mInstaller.execute(builder.toString());
+ public void clearAppData(String uuid, String pkgname, int userid, int flags)
+ throws InstallerException {
+ mInstaller.execute("clear_app_data", uuid, pkgname, userid, flags);
}
- public int dexopt(String apkPath, int uid, String instructionSet,
- int dexoptNeeded, int dexFlags) {
- if (!isValidInstructionSet(instructionSet)) {
- Slog.e(TAG, "Invalid instruction set: " + instructionSet);
- return -1;
- }
-
- return mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
+ public void destroyAppData(String uuid, String pkgname, int userid, int flags)
+ throws InstallerException {
+ mInstaller.execute("destroy_app_data", uuid, pkgname, userid, flags);
}
- public int dexopt(String apkPath, int uid, String pkgName, String instructionSet,
- int dexoptNeeded, @Nullable String outputPath, int dexFlags) {
- if (!isValidInstructionSet(instructionSet)) {
- Slog.e(TAG, "Invalid instruction set: " + instructionSet);
- return -1;
- }
- return mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
- outputPath, dexFlags);
+ public void moveCompleteApp(String from_uuid, String to_uuid, String package_name,
+ String data_app_name, int appid, String seinfo) throws InstallerException {
+ mInstaller.execute("move_complete_app", from_uuid, to_uuid, package_name,
+ data_app_name, appid, seinfo);
}
- public int idmap(String targetApkPath, String overlayApkPath, int uid) {
- StringBuilder builder = new StringBuilder("idmap");
- builder.append(' ');
- builder.append(targetApkPath);
- builder.append(' ');
- builder.append(overlayApkPath);
- builder.append(' ');
- builder.append(uid);
- return mInstaller.execute(builder.toString());
- }
-
- public int movedex(String srcPath, String dstPath, String instructionSet) {
- if (!isValidInstructionSet(instructionSet)) {
- Slog.e(TAG, "Invalid instruction set: " + instructionSet);
- return -1;
- }
-
- StringBuilder builder = new StringBuilder("movedex");
- builder.append(' ');
- builder.append(srcPath);
- builder.append(' ');
- builder.append(dstPath);
- builder.append(' ');
- builder.append(instructionSet);
- return mInstaller.execute(builder.toString());
- }
-
- public int rmdex(String codePath, String instructionSet) {
- if (!isValidInstructionSet(instructionSet)) {
- Slog.e(TAG, "Invalid instruction set: " + instructionSet);
- return -1;
- }
-
- StringBuilder builder = new StringBuilder("rmdex");
- builder.append(' ');
- builder.append(codePath);
- builder.append(' ');
- builder.append(instructionSet);
- return mInstaller.execute(builder.toString());
- }
-
- /**
- * Removes packageDir or its subdirectory
- */
- public int rmPackageDir(String packageDir) {
- StringBuilder builder = new StringBuilder("rmpackagedir");
- builder.append(' ');
- builder.append(packageDir);
- return mInstaller.execute(builder.toString());
- }
-
- @Deprecated
- public int remove(String name, int userId) {
- return remove(null, name, userId);
- }
-
- public int remove(String uuid, String name, int userId) {
- StringBuilder builder = new StringBuilder("remove");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(name);
- builder.append(' ');
- builder.append(userId);
- return mInstaller.execute(builder.toString());
- }
-
- @Deprecated
- public int fixUid(String name, int uid, int gid) {
- return fixUid(null, name, uid, gid);
- }
-
- public int fixUid(String uuid, String name, int uid, int gid) {
- StringBuilder builder = new StringBuilder("fixuid");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(name);
- builder.append(' ');
- builder.append(uid);
- builder.append(' ');
- builder.append(gid);
- return mInstaller.execute(builder.toString());
- }
-
- @Deprecated
- public int deleteCacheFiles(String name, int userId) {
- return deleteCacheFiles(null, name, userId);
- }
-
- public int deleteCacheFiles(String uuid, String name, int userId) {
- StringBuilder builder = new StringBuilder("rmcache");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(name);
- builder.append(' ');
- builder.append(userId);
- return mInstaller.execute(builder.toString());
- }
-
- @Deprecated
- public int deleteCodeCacheFiles(String name, int userId) {
- return deleteCodeCacheFiles(null, name, userId);
- }
-
- public int deleteCodeCacheFiles(String uuid, String name, int userId) {
- StringBuilder builder = new StringBuilder("rmcodecache");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(name);
- builder.append(' ');
- builder.append(userId);
- return mInstaller.execute(builder.toString());
- }
-
- @Deprecated
- public int createUserData(String name, int uid, int userId, String seinfo) {
- return createUserData(null, name, uid, userId, seinfo);
- }
-
- public int createUserData(String uuid, String name, int uid, int userId, String seinfo) {
- StringBuilder builder = new StringBuilder("mkuserdata");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(name);
- builder.append(' ');
- builder.append(uid);
- builder.append(' ');
- builder.append(userId);
- builder.append(' ');
- builder.append(seinfo != null ? seinfo : "!");
- return mInstaller.execute(builder.toString());
- }
-
- public int createUserConfig(int userId) {
- StringBuilder builder = new StringBuilder("mkuserconfig");
- builder.append(' ');
- builder.append(userId);
- return mInstaller.execute(builder.toString());
- }
-
- @Deprecated
- public int removeUserDataDirs(int userId) {
- return removeUserDataDirs(null, userId);
- }
-
- public int removeUserDataDirs(String uuid, int userId) {
- StringBuilder builder = new StringBuilder("rmuser");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(userId);
- return mInstaller.execute(builder.toString());
- }
-
- public int copyCompleteApp(String fromUuid, String toUuid, String packageName,
- String dataAppName, int appId, String seinfo) {
- StringBuilder builder = new StringBuilder("cpcompleteapp");
- builder.append(' ');
- builder.append(escapeNull(fromUuid));
- builder.append(' ');
- builder.append(escapeNull(toUuid));
- builder.append(' ');
- builder.append(packageName);
- builder.append(' ');
- builder.append(dataAppName);
- builder.append(' ');
- builder.append(appId);
- builder.append(' ');
- builder.append(seinfo);
- return mInstaller.execute(builder.toString());
- }
-
- @Deprecated
- public int clearUserData(String name, int userId) {
- return clearUserData(null, name, userId);
- }
-
- public int clearUserData(String uuid, String name, int userId) {
- StringBuilder builder = new StringBuilder("rmuserdata");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(name);
- builder.append(' ');
- builder.append(userId);
- return mInstaller.execute(builder.toString());
- }
-
- public int markBootComplete(String instructionSet) {
- if (!isValidInstructionSet(instructionSet)) {
- Slog.e(TAG, "Invalid instruction set: " + instructionSet);
- return -1;
- }
-
- StringBuilder builder = new StringBuilder("markbootcomplete");
- builder.append(' ');
- builder.append(instructionSet);
- return mInstaller.execute(builder.toString());
- }
-
- @Deprecated
- public int freeCache(long freeStorageSize) {
- return freeCache(null, freeStorageSize);
- }
-
- public int freeCache(String uuid, long freeStorageSize) {
- StringBuilder builder = new StringBuilder("freecache");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(String.valueOf(freeStorageSize));
- return mInstaller.execute(builder.toString());
- }
-
- @Deprecated
- public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
- String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) {
- return getSizeInfo(null, pkgName, persona, apkPath, libDirPath, fwdLockApkPath, asecPath,
- instructionSets, pStats);
- }
-
- public int getSizeInfo(String uuid, String pkgName, int persona, String apkPath,
+ public void getAppSize(String uuid, String pkgname, int userid, int flags, String apkPath,
String libDirPath, String fwdLockApkPath, String asecPath, String[] instructionSets,
- PackageStats pStats) {
+ PackageStats pStats) throws InstallerException {
for (String instructionSet : instructionSets) {
- if (!isValidInstructionSet(instructionSet)) {
- Slog.e(TAG, "Invalid instruction set: " + instructionSet);
- return -1;
- }
+ assertValidInstructionSet(instructionSet);
}
- StringBuilder builder = new StringBuilder("getsize");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(pkgName);
- builder.append(' ');
- builder.append(persona);
- builder.append(' ');
- builder.append(apkPath);
- builder.append(' ');
// TODO: Extend getSizeInfo to look at the full subdirectory tree,
// not just the first level.
- builder.append(libDirPath != null ? libDirPath : "!");
- builder.append(' ');
- builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
- builder.append(' ');
- builder.append(asecPath != null ? asecPath : "!");
- builder.append(' ');
// TODO: Extend getSizeInfo to look at *all* instrution sets, not
// just the primary.
- builder.append(instructionSets[0]);
-
- String s = mInstaller.transact(builder.toString());
- String res[] = s.split(" ");
+ final String rawRes = mInstaller.executeForResult("get_app_size", uuid, pkgname, userid,
+ flags, apkPath, libDirPath, fwdLockApkPath, asecPath, instructionSets[0]);
+ final String res[] = rawRes.split(" ");
if ((res == null) || (res.length != 5)) {
- return -1;
+ throw new InstallerException("Invalid size result: " + rawRes);
}
try {
pStats.codeSize = Long.parseLong(res[1]);
pStats.dataSize = Long.parseLong(res[2]);
pStats.cacheSize = Long.parseLong(res[3]);
pStats.externalCodeSize = Long.parseLong(res[4]);
- return Integer.parseInt(res[0]);
} catch (NumberFormatException e) {
- return -1;
+ throw new InstallerException("Invalid size result: " + rawRes);
}
}
- public int moveFiles() {
- return mInstaller.execute("movefiles");
+ public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
+ int dexFlags) throws InstallerException {
+ assertValidInstructionSet(instructionSet);
+ mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags);
}
- @Deprecated
- public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) {
- return linkNativeLibraryDirectory(null, dataPath, nativeLibPath32, userId);
+ public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
+ int dexoptNeeded, @Nullable String outputPath, int dexFlags)
+ throws InstallerException {
+ assertValidInstructionSet(instructionSet);
+ mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
+ outputPath, dexFlags);
+ }
+
+ public void idmap(String targetApkPath, String overlayApkPath, int uid)
+ throws InstallerException {
+ mInstaller.execute("idmap", targetApkPath, overlayApkPath, uid);
+ }
+
+ public void rmdex(String codePath, String instructionSet) throws InstallerException {
+ assertValidInstructionSet(instructionSet);
+ mInstaller.execute("rmdex", codePath, instructionSet);
+ }
+
+ public void rmPackageDir(String packageDir) throws InstallerException {
+ mInstaller.execute("rmpackagedir", packageDir);
+ }
+
+ public void createUserConfig(int userid) throws InstallerException {
+ mInstaller.execute("mkuserconfig", userid);
+ }
+
+ public void removeUserDataDirs(String uuid, int userid) throws InstallerException {
+ mInstaller.execute("rmuser", uuid, userid);
+ }
+
+ public void markBootComplete(String instructionSet) throws InstallerException {
+ assertValidInstructionSet(instructionSet);
+ mInstaller.execute("markbootcomplete", instructionSet);
+ }
+
+ public void freeCache(String uuid, long freeStorageSize) throws InstallerException {
+ mInstaller.execute("freecache", uuid, freeStorageSize);
+ }
+
+ public void moveFiles() throws InstallerException {
+ mInstaller.execute("movefiles");
}
/**
- * Links the 32 bit native library directory in an application's data directory to the
- * real location for backward compatibility. Note that no such symlink is created for
- * 64 bit shared libraries.
- *
- * @return -1 on error
+ * Links the 32 bit native library directory in an application's data
+ * directory to the real location for backward compatibility. Note that no
+ * such symlink is created for 64 bit shared libraries.
*/
- public int linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32,
- int userId) {
- if (dataPath == null) {
- Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
- return -1;
- } else if (nativeLibPath32 == null) {
- Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
- return -1;
- }
-
- StringBuilder builder = new StringBuilder("linklib");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(dataPath);
- builder.append(' ');
- builder.append(nativeLibPath32);
- builder.append(' ');
- builder.append(userId);
-
- return mInstaller.execute(builder.toString());
+ public void linkNativeLibraryDirectory(String uuid, String dataPath, String nativeLibPath32,
+ int userId) throws InstallerException {
+ mInstaller.execute("linklib", uuid, dataPath, nativeLibPath32, userId);
}
- @Deprecated
- public boolean restoreconData(String pkgName, String seinfo, int uid) {
- return restoreconData(null, pkgName, seinfo, uid);
+ public void createOatDir(String oatDir, String dexInstructionSet)
+ throws InstallerException {
+ mInstaller.execute("createoatdir", oatDir, dexInstructionSet);
}
- public boolean restoreconData(String uuid, String pkgName, String seinfo, int uid) {
- StringBuilder builder = new StringBuilder("restorecondata");
- builder.append(' ');
- builder.append(escapeNull(uuid));
- builder.append(' ');
- builder.append(pkgName);
- builder.append(' ');
- builder.append(seinfo != null ? seinfo : "!");
- builder.append(' ');
- builder.append(uid);
- return (mInstaller.execute(builder.toString()) == 0);
+ public void linkFile(String relativePath, String fromBase, String toBase)
+ throws InstallerException {
+ mInstaller.execute("linkfile", relativePath, fromBase, toBase);
}
- public int createOatDir(String oatDir, String dexInstructionSet) {
- StringBuilder builder = new StringBuilder("createoatdir");
- builder.append(' ');
- builder.append(oatDir);
- builder.append(' ');
- builder.append(dexInstructionSet);
- return mInstaller.execute(builder.toString());
- }
-
-
- public int linkFile(String relativePath, String fromBase, String toBase) {
- StringBuilder builder = new StringBuilder("linkfile");
- builder.append(' ');
- builder.append(relativePath);
- builder.append(' ');
- builder.append(fromBase);
- builder.append(' ');
- builder.append(toBase);
- return mInstaller.execute(builder.toString());
- }
-
- /**
- * Returns true iff. {@code instructionSet} is a valid instruction set.
- */
- private static boolean isValidInstructionSet(String instructionSet) {
- if (instructionSet == null) {
- return false;
- }
-
+ private static void assertValidInstructionSet(String instructionSet)
+ throws InstallerException {
for (String abi : Build.SUPPORTED_ABIS) {
- if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) {
- return true;
+ if (VMRuntime.getInstructionSet(abi).equals(instructionSet)) {
+ return;
}
}
-
- return false;
+ throw new InstallerException("Invalid instruction set: " + instructionSet);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index d29a623..b45a922 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -27,6 +27,8 @@
import android.util.Log;
import android.util.Slog;
+import com.android.internal.os.InstallerConnection.InstallerException;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -166,12 +168,13 @@
| (vmSafeMode ? DEXOPT_SAFEMODE : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
| DEXOPT_BOOTCOMPLETE;
- final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
- pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir, dexFlags);
-
- // Dex2oat might fail due to compiler / verifier errors.
- if (ret == 0) {
+ try {
+ mPackageManagerService.mInstaller.dexopt(path, sharedGid,
+ pkg.packageName, dexCodeInstructionSet, dexoptNeeded, oatDir,
+ dexFlags);
performedDexOpt = true;
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to dexopt", e);
}
}
}
@@ -210,8 +213,13 @@
File codePath = new File(pkg.codePath);
if (codePath.isDirectory()) {
File oatDir = getOatDir(codePath);
- mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
- dexInstructionSet);
+ try {
+ mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
+ dexInstructionSet);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to create oat dir", e);
+ return null;
+ }
return oatDir.getAbsolutePath();
}
return null;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 55b8bf2..23a58d0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -258,11 +258,7 @@
for (File stage : unclaimedStages) {
Slog.w(TAG, "Deleting orphan stage " + stage);
synchronized (mPm.mInstallLock) {
- if (stage.isDirectory()) {
- mPm.mInstaller.rmPackageDir(stage.getAbsolutePath());
- } else {
- stage.delete();
- }
+ mPm.removeCodePathLI(stage);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1655cb6..b84ffa3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -69,6 +69,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
+import com.android.internal.os.InstallerConnection.InstallerException;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -832,9 +833,14 @@
throw new IOException("File: " + pathStr + " outside base: " + baseStr);
}
- private void createOatDirs(List<String> instructionSets, File fromDir) {
+ private void createOatDirs(List<String> instructionSets, File fromDir)
+ throws PackageManagerException {
for (String instructionSet : instructionSets) {
- mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+ try {
+ mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
+ } catch (InstallerException e) {
+ throw PackageManagerException.from(e);
+ }
}
}
@@ -842,13 +848,12 @@
throws IOException {
for (File fromFile : fromFiles) {
final String relativePath = getRelativePath(fromFile, fromDir);
- final int ret = mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
- toDir.getAbsolutePath());
-
- if (ret < 0) {
- // installd will log failure details.
+ try {
+ mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
+ toDir.getAbsolutePath());
+ } catch (InstallerException e) {
throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
- + fromDir + ", " + toDir + ")");
+ + fromDir + ", " + toDir + ")", e);
}
}
@@ -1041,7 +1046,10 @@
}
}
if (stageDir != null) {
- mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+ try {
+ mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+ } catch (InstallerException ignored) {
+ }
}
if (stageCid != null) {
PackageHelper.destroySdDir(stageCid);
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index a41636e..d04eedc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -16,8 +16,11 @@
package com.android.server.pm;
+import android.content.pm.PackageManager;
import android.content.pm.PackageParser.PackageParserException;
+import com.android.internal.os.InstallerConnection.InstallerException;
+
/** {@hide} */
public class PackageManagerException extends Exception {
public final int error;
@@ -36,4 +39,10 @@
throws PackageManagerException {
throw new PackageManagerException(e.error, e.getMessage(), e.getCause());
}
+
+ public static PackageManagerException from(InstallerException e)
+ throws PackageManagerException {
+ throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
+ e.getMessage(), e.getCause());
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 41627fd..c1cba97 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -219,6 +219,7 @@
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.os.IParcelFileDescriptorFactory;
+import com.android.internal.os.InstallerConnection.InstallerException;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
@@ -2067,7 +2068,7 @@
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
- } catch (IOException e) {
+ } catch (IOException | InstallerException e) {
Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
+ e.getMessage());
}
@@ -2136,7 +2137,11 @@
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
- mInstaller.moveFiles();
+ try {
+ mInstaller.moveFiles();
+ } catch (InstallerException e) {
+ logCriticalInfo(Log.WARN, "Update commands failed: " + e);
+ }
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
@@ -2710,11 +2715,7 @@
removeDataDirsLI(ps.volumeUuid, ps.name);
if (ps.codePath != null) {
- if (ps.codePath.isDirectory()) {
- mInstaller.rmPackageDir(ps.codePath.getAbsolutePath());
- } else {
- ps.codePath.delete();
- }
+ removeCodePathLI(ps.codePath);
}
if (ps.resourcePath != null && !ps.resourcePath.equals(ps.codePath)) {
if (ps.resourcePath.isDirectory()) {
@@ -3038,16 +3039,18 @@
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
- int retCode = -1;
+ boolean success = true;
synchronized (mInstallLock) {
- retCode = mInstaller.freeCache(volumeUuid, freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
+ try {
+ mInstaller.freeCache(volumeUuid, freeStorageSize);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Couldn't clear application caches: " + e);
+ success = false;
}
}
if (observer != null) {
try {
- observer.onRemoveCompleted(null, (retCode >= 0));
+ observer.onRemoveCompleted(null, success);
} catch (RemoteException e) {
Slog.w(TAG, "RemoveException when invoking call back");
}
@@ -3065,17 +3068,19 @@
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
- int retCode = -1;
+ boolean success = true;
synchronized (mInstallLock) {
- retCode = mInstaller.freeCache(volumeUuid, freeStorageSize);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't clear application caches");
+ try {
+ mInstaller.freeCache(volumeUuid, freeStorageSize);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Couldn't clear application caches: " + e);
+ success = false;
}
}
if(pi != null) {
try {
// Callback via pending intent
- int code = (retCode >= 0) ? 1 : 0;
+ int code = success ? 1 : 0;
pi.sendIntent(null, code, null,
null, null);
} catch (SendIntentException e1) {
@@ -3088,8 +3093,10 @@
void freeStorage(String volumeUuid, long freeStorageSize) throws IOException {
synchronized (mInstallLock) {
- if (mInstaller.freeCache(volumeUuid, freeStorageSize) < 0) {
- throw new IOException("Failed to free enough space");
+ try {
+ mInstaller.freeCache(volumeUuid, freeStorageSize);
+ } catch (InstallerException e) {
+ throw new IOException("Failed to free enough space", e);
}
}
}
@@ -3121,7 +3128,7 @@
/**
* Update given flags based on encryption status of current user.
*/
- private int updateFlagsForEncryption(int flags, int userId) {
+ private int updateFlags(int flags, int userId) {
if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE
| PackageManager.MATCH_ENCRYPTION_AWARE)) != 0) {
// Caller expressed an explicit opinion about what encryption
@@ -3135,6 +3142,12 @@
flags |= PackageManager.MATCH_ENCRYPTION_AWARE;
}
}
+
+ // Safe mode means we should ignore any third-party apps
+ if (mSafeMode) {
+ flags |= PackageManager.MATCH_SYSTEM_ONLY;
+ }
+
return flags;
}
@@ -3162,7 +3175,7 @@
Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
+ " with flags 0x" + Integer.toHexString(flags), new Throwable());
}
- return updateFlagsForEncryption(flags, userId);
+ return updateFlags(flags, userId);
}
/**
@@ -3194,7 +3207,7 @@
Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie
+ " with flags 0x" + Integer.toHexString(flags), new Throwable());
}
- return updateFlagsForEncryption(flags, userId);
+ return updateFlags(flags, userId);
}
/**
@@ -5941,8 +5954,6 @@
: null;
return ps != null
&& mSettings.isEnabledAndMatchLPr(provider.info, flags, userId)
- && (!mSafeMode || (provider.info.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) != 0)
? PackageParser.generateProviderInfo(provider, flags,
ps.readUserState(userId), userId)
: null;
@@ -5997,9 +6008,7 @@
&& (processName == null
|| (p.info.processName.equals(processName)
&& UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
- && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)
- && (!mSafeMode
- || (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+ && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
@@ -6084,7 +6093,9 @@
}
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
// TODO: generate idmap for split APKs
- if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) {
+ try {
+ mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid);
+ } catch (InstallerException e) {
Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and "
+ opkg.baseCodePath);
return false;
@@ -6144,11 +6155,7 @@
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
- if (file.isDirectory()) {
- mInstaller.rmPackageDir(file.getAbsolutePath());
- } else {
- file.delete();
- }
+ removeCodePathLI(file);
}
}
}
@@ -6714,50 +6721,65 @@
private void createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo)
throws PackageManagerException {
- int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
- if (res != 0) {
+ // TODO: triage flags as part of 26466827
+ final int appId = UserHandle.getAppId(uid);
+ final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+
+ try {
+ final int[] users = sUserManager.getUserIds();
+ for (int user : users) {
+ mInstaller.createAppData(volumeUuid, packageName, user, flags, appId, seinfo);
+ }
+ } catch (InstallerException e) {
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Failed to install " + packageName + ": " + res);
+ "Failed to prepare data directory", e);
}
+ }
+
+ private boolean removeDataDirsLI(String volumeUuid, String packageName) {
+ // TODO: triage flags as part of 26466827
+ final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+
+ boolean res = true;
+ final int[] users = sUserManager.getUserIds();
+ for (int user : users) {
+ try {
+ mInstaller.destroyAppData(volumeUuid, packageName, user, flags);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to delete data directory", e);
+ res = false;
+ }
+ }
+ return res;
+ }
+
+ void removeCodePathLI(File codePath) {
+ if (codePath.isDirectory()) {
+ try {
+ mInstaller.rmPackageDir(codePath.getAbsolutePath());
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to remove code path", e);
+ }
+ } else {
+ codePath.delete();
+ }
+ }
+
+ private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
+ // TODO: triage flags as part of 26466827
+ final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
final int[] users = sUserManager.getUserIds();
for (int user : users) {
- if (user != 0) {
- res = mInstaller.createUserData(volumeUuid, packageName,
- UserHandle.getUid(user, uid), user, seinfo);
- if (res != 0) {
- throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Failed to createUserData " + packageName + ": " + res);
- }
+ try {
+ mInstaller.clearAppData(volumeUuid, packageName, user,
+ flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to delete code cache directory", e);
}
}
}
- private int removeDataDirsLI(String volumeUuid, String packageName) {
- int[] users = sUserManager.getUserIds();
- int res = 0;
- for (int user : users) {
- int resInner = mInstaller.remove(volumeUuid, packageName, user);
- if (resInner < 0) {
- res = resInner;
- }
- }
-
- return res;
- }
-
- private int deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
- int[] users = sUserManager.getUserIds();
- int res = 0;
- for (int user : users) {
- int resInner = mInstaller.deleteCodeCacheFiles(volumeUuid, packageName, user);
- if (resInner < 0) {
- res = resInner;
- }
- }
- return res;
- }
-
private void addSharedLibraryLPw(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
PackageParser.Package changingLib) {
if (file.path != null) {
@@ -7256,6 +7278,8 @@
final File dataPath = Environment.getDataUserCredentialEncryptedPackageDirectory(
pkg.volumeUuid, UserHandle.USER_SYSTEM, pkg.packageName);
+ // TOOD: switch to ensure various directories
+
boolean uidError = false;
if (dataPath.exists()) {
int currentUid = 0;
@@ -7269,27 +7293,12 @@
// If we have mismatched owners for the data path, we have a problem.
if (currentUid != pkg.applicationInfo.uid) {
boolean recovered = false;
- if (currentUid == 0) {
- // The directory somehow became owned by root. Wow.
- // This is probably because the system was stopped while
- // installd was in the middle of messing with its libs
- // directory. Ask installd to fix that.
- int ret = mInstaller.fixUid(pkg.volumeUuid, pkgName,
- pkg.applicationInfo.uid, pkg.applicationInfo.uid);
- if (ret >= 0) {
- recovered = true;
- String msg = "Package " + pkg.packageName
- + " unexpectedly changed to uid 0; recovered to " +
- + pkg.applicationInfo.uid;
- reportSettingsProblem(Log.WARN, msg);
- }
- }
- if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
- || (scanFlags&SCAN_BOOTING) != 0)) {
+ if (((parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0
+ || (scanFlags & SCAN_BOOTING) != 0)) {
// If this is a system app, we can at least delete its
// current data so the application will still work.
- int ret = removeDataDirsLI(pkg.volumeUuid, pkgName);
- if (ret >= 0) {
+ boolean res = removeDataDirsLI(pkg.volumeUuid, pkgName);
+ if (res) {
// TODO: Kill the processes first
// Old data gone!
String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
@@ -7304,11 +7313,12 @@
if (!recovered) {
mHasSystemUidErrors = true;
}
- } else if (!recovered) {
+ } else {
// If we allow this install to proceed, we will be broken.
// Abort, abort!
throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
- "scanPackageLI");
+ "Expected data to be owned by UID " + pkg.applicationInfo.uid
+ + " but found " + currentUid);
}
if (!recovered) {
pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
@@ -7338,8 +7348,16 @@
if (mShouldRestoreconData) {
Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued.");
- mInstaller.restoreconData(pkg.volumeUuid, pkg.packageName,
- pkg.applicationInfo.seinfo, pkg.applicationInfo.uid);
+ // TODO: extend this to restorecon over all users
+ final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
+ // TODO: triage flags as part of 26466827
+ final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+ try {
+ mInstaller.restoreconAppData(pkg.volumeUuid, pkg.packageName,
+ UserHandle.USER_SYSTEM, flags, appId, pkg.applicationInfo.seinfo);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to restorecon " + pkg.packageName, e);
+ }
}
} else {
if (DEBUG_PACKAGE_SCANNING) {
@@ -7395,9 +7413,15 @@
if (!TextUtils.isEmpty(pkg.volumeUuid)) {
for (int userId : userIds) {
if (userId != UserHandle.USER_SYSTEM) {
- mInstaller.createUserData(pkg.volumeUuid, pkg.packageName,
- UserHandle.getUid(userId, pkg.applicationInfo.uid), userId,
- pkg.applicationInfo.seinfo);
+ // TODO: triage flags as part of 26466827
+ final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+ final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
+ try {
+ mInstaller.createAppData(pkg.volumeUuid, pkg.packageName, userId,
+ flags, appId, pkg.applicationInfo.seinfo);
+ } catch (InstallerException e) {
+ throw PackageManagerException.from(e);
+ }
}
}
}
@@ -7411,10 +7435,11 @@
try {
final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
for (int userId : userIds) {
- if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
- nativeLibPath, userId) < 0) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Failed linking native library dir (user=" + userId + ")");
+ try {
+ mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+ nativeLibPath, userId);
+ } catch (InstallerException e) {
+ throw PackageManagerException.from(e);
}
}
} finally {
@@ -8147,8 +8172,11 @@
if (ps.pkg != null && ps.pkg.applicationInfo != null) {
ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi);
- mInstaller.rmdex(ps.codePathString,
- getDexCodeInstructionSet(getPreferredInstructionSet()));
+ try {
+ mInstaller.rmdex(ps.codePathString,
+ getDexCodeInstructionSet(getPreferredInstructionSet()));
+ } catch (InstallerException ignored) {
+ }
}
}
}
@@ -9265,10 +9293,6 @@
return null;
}
final PackageParser.Activity activity = info.activity;
- if (mSafeMode && (activity.info.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) == 0) {
- return null;
- }
PackageSetting ps = (PackageSetting) activity.owner.mExtras;
if (ps == null) {
return null;
@@ -9489,10 +9513,6 @@
return null;
}
final PackageParser.Service service = info.service;
- if (mSafeMode && (service.info.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) == 0) {
- return null;
- }
PackageSetting ps = (PackageSetting) service.owner.mExtras;
if (ps == null) {
return null;
@@ -9712,10 +9732,6 @@
return null;
}
final PackageParser.Provider provider = info.provider;
- if (mSafeMode && (provider.info.applicationInfo.flags
- & ApplicationInfo.FLAG_SYSTEM) == 0) {
- return null;
- }
PackageSetting ps = (PackageSetting) provider.owner.mExtras;
if (ps == null) {
return null;
@@ -10200,7 +10216,7 @@
long callingId = Binder.clearCallingIdentity();
try {
- boolean sendAdded = false;
+ boolean installed = false;
// writer
synchronized (mPackages) {
@@ -10212,11 +10228,21 @@
pkgSetting.setInstalled(true, userId);
pkgSetting.setHidden(false, userId);
mSettings.writePackageRestrictionsLPr(userId);
- sendAdded = true;
+ installed = true;
}
}
- if (sendAdded) {
+ if (installed) {
+ synchronized (mInstallLock) {
+ final int flags = Installer.FLAG_DE_STORAGE | Installer.FLAG_CE_STORAGE;
+ try {
+ mInstaller.createAppData(pkgSetting.volumeUuid, packageName, userId, flags,
+ pkgSetting.appId, pkgSetting.pkg.applicationInfo.seinfo);
+ } catch (InstallerException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
sendPackageAddedForUser(packageName, pkgSetting, userId);
}
} finally {
@@ -11143,9 +11169,12 @@
final long sizeBytes = mContainerService.calculateInstalledSize(
origin.resolvedPath, isForwardLocked(), packageAbiOverride);
- if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) {
+ try {
+ mInstaller.freeCache(null, sizeBytes + lowThreshold);
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
}
/*
@@ -11543,11 +11572,9 @@
String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String codePath : allCodePaths) {
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- int retCode = mInstaller.rmdex(codePath, dexCodeInstructionSet);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove dex file for package at location " + codePath
- + ", retcode=" + retCode);
- // we don't consider this to be a failure of the core package deletion
+ try {
+ mInstaller.rmdex(codePath, dexCodeInstructionSet);
+ } catch (InstallerException ignored) {
}
}
}
@@ -11733,11 +11760,7 @@
return false;
}
- if (codeFile.isDirectory()) {
- mInstaller.rmPackageDir(codeFile.getAbsolutePath());
- } else {
- codeFile.delete();
- }
+ removeCodePathLI(codeFile);
if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
resourceFile.delete();
@@ -12114,8 +12137,11 @@
if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from "
+ move.fromUuid + " to " + move.toUuid);
synchronized (mInstaller) {
- if (mInstaller.copyCompleteApp(move.fromUuid, move.toUuid, move.packageName,
- move.dataAppName, move.appId, move.seinfo) != 0) {
+ try {
+ mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName,
+ move.dataAppName, move.appId, move.seinfo);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to move app", e);
return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
}
}
@@ -12178,11 +12204,7 @@
synchronized (mInstallLock) {
// Clean up both app data and code
removeDataDirsLI(volumeUuid, move.packageName);
- if (codeFile.isDirectory()) {
- mInstaller.rmPackageDir(codeFile.getAbsolutePath());
- } else {
- codeFile.delete();
- }
+ removeCodePathLI(codeFile);
}
return true;
}
@@ -13863,7 +13885,13 @@
outInfo.removedAppId = appId;
outInfo.removedUsers = new int[] {removeUser};
}
- mInstaller.clearUserData(ps.volumeUuid, packageName, removeUser);
+ // TODO: triage flags as part of 26466827
+ final int installerFlags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+ try {
+ mInstaller.destroyAppData(ps.volumeUuid, packageName, removeUser, installerFlags);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to delete app data", e);
+ }
removeKeystoreDataIfNeeded(removeUser, appId);
schedulePackageCleaning(packageName, removeUser, false);
synchronized (mPackages) {
@@ -14037,13 +14065,16 @@
// Always delete data directories for package, even if we found no other
// record of app. This helps users recover from UID mismatches without
// resorting to a full data wipe.
- int retCode = mInstaller.clearUserData(pkg.volumeUuid, packageName, userId);
- if (retCode < 0) {
- Slog.w(TAG, "Couldn't remove cache files for package " + packageName);
+ // TODO: triage flags as part of 26466827
+ final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+ try {
+ mInstaller.clearAppData(pkg.volumeUuid, packageName, userId, flags);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Couldn't remove cache files for package " + packageName, e);
return false;
}
- final int appId = pkg.applicationInfo.uid;
+ final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
removeKeystoreDataIfNeeded(userId, appId);
// Create a native library symlink only if we have native libraries
@@ -14052,9 +14083,11 @@
if (pkg.applicationInfo.primaryCpuAbi != null &&
!VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
- if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
- nativeLibPath, userId) < 0) {
- Slog.w(TAG, "Failed linking native library dir");
+ try {
+ mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+ nativeLibPath, userId);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed linking native library dir", e);
return false;
}
}
@@ -14267,10 +14300,14 @@
Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
return false;
}
- int retCode = mInstaller.deleteCacheFiles(p.volumeUuid, packageName, userId);
- if (retCode < 0) {
+ // TODO: triage flags as part of 26466827
+ final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+ try {
+ mInstaller.clearAppData(p.volumeUuid, packageName, userId,
+ flags | Installer.FLAG_CLEAR_CACHE_ONLY);
+ } catch (InstallerException e) {
Slog.w(TAG, "Couldn't remove cache files for package "
- + packageName + " u" + userId);
+ + packageName + " u" + userId, e);
return false;
}
return true;
@@ -14364,9 +14401,12 @@
apkPath = p.baseCodePath;
}
- int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, apkPath,
- libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
- if (res < 0) {
+ // TODO: triage flags as part of 26466827
+ final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+ try {
+ mInstaller.getAppSize(p.volumeUuid, packageName, userHandle, flags, apkPath,
+ libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
+ } catch (InstallerException e) {
return false;
}
@@ -16581,7 +16621,11 @@
if (destroyUser) {
synchronized (mInstallLock) {
- mInstaller.removeUserDataDirs(volumeUuid, userId);
+ try {
+ mInstaller.removeUserDataDirs(volumeUuid, userId);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to clean up user dirs", e);
+ }
}
}
}
@@ -16647,11 +16691,7 @@
if (packageName != null) {
removeDataDirsLI(volumeUuid, packageName);
}
- if (file.isDirectory()) {
- mInstaller.rmPackageDir(file.getAbsolutePath());
- } else {
- file.delete();
- }
+ removeCodePathLI(file);
}
}
}
@@ -16987,7 +17027,11 @@
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid);
- mInstaller.removeUserDataDirs(volumeUuid, userHandle);
+ try {
+ mInstaller.removeUserDataDirs(volumeUuid, userHandle);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to remove user data", e);
+ }
}
synchronized (mPackages) {
removeUnusedPackagesLILPw(userManager, userHandle);
@@ -17050,7 +17094,11 @@
/** Called by UserManagerService */
void createNewUser(int userHandle) {
synchronized (mInstallLock) {
- mInstaller.createUserConfig(userHandle);
+ try {
+ mInstaller.createUserConfig(userHandle);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to create user config", e);
+ }
mSettings.createNewUserLI(this, mInstaller, userHandle);
}
synchronized (mPackages) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3f9ce7a..9fef515 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -81,6 +81,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.InstallerConnection.InstallerException;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
@@ -3668,7 +3669,7 @@
int userHandle) {
String[] volumeUuids;
String[] names;
- int[] uids;
+ int[] appIds;
String[] seinfos;
int packagesCount;
synchronized (mPackages) {
@@ -3676,7 +3677,7 @@
packagesCount = packages.size();
volumeUuids = new String[packagesCount];
names = new String[packagesCount];
- uids = new int[packagesCount];
+ appIds = new int[packagesCount];
seinfos = new String[packagesCount];
Iterator<PackageSetting> packagesIterator = packages.iterator();
for (int i = 0; i < packagesCount; i++) {
@@ -3690,7 +3691,7 @@
// required args and call the installer after mPackages lock has been released
volumeUuids[i] = ps.volumeUuid;
names[i] = ps.name;
- uids[i] = UserHandle.getUid(userHandle, ps.appId);
+ appIds[i] = ps.appId;
seinfos[i] = ps.pkg.applicationInfo.seinfo;
}
}
@@ -3698,7 +3699,14 @@
if (names[i] == null) {
continue;
}
- installer.createUserData(volumeUuids[i], names[i], uids[i], userHandle, seinfos[i]);
+ // TODO: triage flags!
+ final int flags = Installer.FLAG_CE_STORAGE | Installer.FLAG_DE_STORAGE;
+ try {
+ installer.createAppData(volumeUuids[i], names[i], userHandle, flags, appIds[i],
+ seinfos[i]);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to prepare app data", e);
+ }
}
synchronized (mPackages) {
applyDefaultPreferredAppsLPw(service, userHandle);
@@ -3799,63 +3807,11 @@
}
boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
- return isEnabledLPr(componentInfo, flags, userId)
- && isMatchLPr(componentInfo, flags);
- }
+ final PackageSetting ps = mPackages.get(componentInfo.packageName);
+ if (ps == null) return false;
- private boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
- if ((flags & MATCH_DISABLED_COMPONENTS) != 0) {
- return true;
- }
- final PackageSetting packageSettings = mPackages.get(componentInfo.packageName);
- if (PackageManagerService.DEBUG_SETTINGS) {
- Log.v(PackageManagerService.TAG, "isEnabledLock - packageName = "
- + componentInfo.packageName + " componentName = " + componentInfo.name);
- Log.v(PackageManagerService.TAG, "enabledComponents: "
- + compToString(packageSettings.getEnabledComponents(userId)));
- Log.v(PackageManagerService.TAG, "disabledComponents: "
- + compToString(packageSettings.getDisabledComponents(userId)));
- }
- if (packageSettings == null) {
- return false;
- }
- PackageUserState ustate = packageSettings.readUserState(userId);
- if ((flags & MATCH_DISABLED_UNTIL_USED_COMPONENTS) != 0) {
- if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
- return true;
- }
- }
- if (ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED
- || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_USER
- || ustate.enabled == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
- || (packageSettings.pkg != null && !packageSettings.pkg.applicationInfo.enabled
- && ustate.enabled == COMPONENT_ENABLED_STATE_DEFAULT)) {
- return false;
- }
- if (ustate.enabledComponents != null
- && ustate.enabledComponents.contains(componentInfo.name)) {
- return true;
- }
- if (ustate.disabledComponents != null
- && ustate.disabledComponents.contains(componentInfo.name)) {
- return false;
- }
- return componentInfo.enabled;
- }
-
- private boolean isMatchLPr(ComponentInfo componentInfo, int flags) {
- if ((flags & MATCH_SYSTEM_ONLY) != 0) {
- final PackageSetting ps = mPackages.get(componentInfo.packageName);
- if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- return false;
- }
- }
-
- final boolean matchesUnaware = ((flags & MATCH_ENCRYPTION_UNAWARE) != 0)
- && !componentInfo.encryptionAware;
- final boolean matchesAware = ((flags & MATCH_ENCRYPTION_AWARE) != 0)
- && componentInfo.encryptionAware;
- return matchesUnaware || matchesAware;
+ final PackageUserState userState = ps.readUserState(userId);
+ return userState.isMatch(componentInfo, flags);
}
String getInstallerPackageNameLPr(String packageName) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3d614a3..ce6b369 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -276,6 +276,8 @@
private final ArrayList<UserRestrictionsListener> mUserRestrictionsListeners =
new ArrayList<>();
+ private final LockPatternUtils mLockPatternUtils;
+
private static UserManagerService sInstance;
public static UserManagerService getInstance() {
@@ -320,6 +322,7 @@
}
mLocalService = new LocalService();
LocalServices.addService(UserManagerInternal.class, mLocalService);
+ mLockPatternUtils = new LockPatternUtils(mContext);
}
void systemReady() {
@@ -456,7 +459,7 @@
@Override
public int getCredentialOwnerProfile(int userHandle) {
checkManageUsersPermission("get the credential owner");
- if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+ if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
synchronized (mUsersLock) {
UserInfo profileParent = getProfileParentLU(userHandle);
if (profileParent != null) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index de1c1ea..9c629bd 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3003,6 +3003,14 @@
UserHandle.CURRENT_OR_SELF);
}
return -1;
+ } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
+ || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+ if (mUseTvRouting) {
+ // On TVs volume keys never go to the foreground app.
+ dispatchDirectAudioEvent(event);
+ return -1;
+ }
} else if (KeyEvent.isMetaKey(keyCode)) {
if (down) {
mPendingMetaAction = true;
@@ -4054,7 +4062,13 @@
cf.top = vf.top = mStableTop;
cf.right = vf.right = mStableRight;
vf.bottom = mStableBottom;
- cf.bottom = mContentBottom;
+
+ if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
+ cf.bottom = mContentBottom;
+ } else {
+ cf.bottom = mDockBottom;
+ vf.bottom = mContentBottom;
+ }
} else {
// Default policy decor for the default display
@@ -5084,10 +5098,6 @@
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
- if (mUseTvRouting) {
- // On TVs volume keys never go to the foreground app
- result &= ~ACTION_PASS_TO_USER;
- }
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (down) {
if (interactive && !mScreenshotChordVolumeDownKeyTriggered
@@ -5147,19 +5157,17 @@
break;
}
}
-
- if ((result & ACTION_PASS_TO_USER) == 0) {
- if (mUseTvRouting) {
- dispatchDirectAudioEvent(event);
- } else {
- // If we aren't passing to the user and no one else
- // handled it send it to the session manager to
- // figure out.
- MediaSessionLegacyHelper.getHelper(mContext)
- .sendVolumeKeyEvent(event, true);
- }
- break;
- }
+ }
+ if (mUseTvRouting) {
+ // On TVs, defer special key handlings to
+ // {@link interceptKeyBeforeDispatching()}.
+ result |= ACTION_PASS_TO_USER;
+ } else if ((result & ACTION_PASS_TO_USER) == 0) {
+ // If we aren't passing to the user and no one else
+ // handled it send it to the session manager to
+ // figure out.
+ MediaSessionLegacyHelper.getHelper(mContext)
+ .sendVolumeKeyEvent(event, true);
}
break;
}
@@ -6402,6 +6410,7 @@
if (mLockScreenTimerActive != enable) {
if (enable) {
if (localLOGV) Log.v(TAG, "setting lockscreen timer");
+ mHandler.removeCallbacks(mScreenLockTimeout); // remove any pending requests
mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
} else {
if (localLOGV) Log.v(TAG, "clearing lockscreen timer");
diff --git a/services/core/java/com/android/server/policy/ShortcutManager.java b/services/core/java/com/android/server/policy/ShortcutManager.java
index 9908624..9284442 100644
--- a/services/core/java/com/android/server/policy/ShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ShortcutManager.java
@@ -26,6 +26,7 @@
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -47,8 +48,10 @@
private static final String ATTRIBUTE_CLASS = "class";
private static final String ATTRIBUTE_SHORTCUT = "shortcut";
private static final String ATTRIBUTE_CATEGORY = "category";
+ private static final String ATTRIBUTE_SHIFT = "shift";
private final SparseArray<ShortcutInfo> mShortcuts = new SparseArray<>();
+ private final SparseArray<ShortcutInfo> mShiftShortcuts = new SparseArray<>();
private final Context mContext;
@@ -75,17 +78,21 @@
public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
ShortcutInfo shortcut = null;
+ // If the Shift key is preesed, then search for the shift shortcuts.
+ boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON;
+ SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mShortcuts;
+
// First try the exact keycode (with modifiers).
int shortcutChar = kcm.get(keyCode, metaState);
if (shortcutChar != 0) {
- shortcut = mShortcuts.get(shortcutChar);
+ shortcut = shortcutMap.get(shortcutChar);
}
// Next try the primary character on that key.
if (shortcut == null) {
shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
if (shortcutChar != 0) {
- shortcut = mShortcuts.get(shortcutChar);
+ shortcut = shortcutMap.get(shortcutChar);
}
}
@@ -114,6 +121,7 @@
String className = parser.getAttributeValue(null, ATTRIBUTE_CLASS);
String shortcutName = parser.getAttributeValue(null, ATTRIBUTE_SHORTCUT);
String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY);
+ String shiftName = parser.getAttributeValue(null, ATTRIBUTE_SHIFT);
if (TextUtils.isEmpty(shortcutName)) {
Log.w(TAG, "Unable to get shortcut for: " + packageName + "/" + className);
@@ -121,6 +129,7 @@
}
final int shortcutChar = shortcutName.charAt(0);
+ final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true"));
final Intent intent;
final String title;
@@ -158,7 +167,11 @@
}
ShortcutInfo shortcut = new ShortcutInfo(title, intent);
- mShortcuts.put(shortcutChar, shortcut);
+ if (isShiftShortcut) {
+ mShiftShortcuts.put(shortcutChar, shortcut);
+ } else {
+ mShortcuts.put(shortcutChar, shortcut);
+ }
}
} catch (XmlPullParserException e) {
Log.w(TAG, "Got exception parsing bookmarks.", e);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index d888c56..1d498e1 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -290,14 +290,9 @@
}
public void setDeviceLockedForUser(int userId, boolean locked) {
- if (LockPatternUtils.isSeparateWorkChallengeEnabled()) {
- UserInfo info = mUserManager.getUserInfo(userId);
- if (info.isManagedProfile()) {
- synchronized (mDeviceLockedForUser) {
- mDeviceLockedForUser.put(userId, locked);
- }
- } else {
- Log.wtf(TAG, "Requested to change lock state for non-profile user " + userId);
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ synchronized (mDeviceLockedForUser) {
+ mDeviceLockedForUser.put(userId, locked);
}
}
}
@@ -669,24 +664,29 @@
public boolean isDeviceLocked(int userId) throws RemoteException {
userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false /* allowAll */, true /* requireFull */, "isDeviceLocked", null);
- if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
- userId = resolveProfileParent(userId);
- }
- return isDeviceLockedInner(userId);
+ long token = Binder.clearCallingIdentity();
+ try {
+ if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ userId = resolveProfileParent(userId);
+ }
+ return isDeviceLockedInner(userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public boolean isDeviceSecure(int userId) throws RemoteException {
userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false /* allowAll */, true /* requireFull */, "isDeviceSecure", null);
- if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
- userId = resolveProfileParent(userId);
- }
long token = Binder.clearCallingIdentity();
try {
- return new LockPatternUtils(mContext).isSecure(userId);
+ if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ userId = resolveProfileParent(userId);
+ }
+ return mLockPatternUtils.isSecure(userId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -783,6 +783,7 @@
@Override
public void setDeviceLockedForUser(int userId, boolean value) {
+ enforceReportPermission();
mHandler.obtainMessage(MSG_SET_DEVICE_LOCKED, value ? 1 : 0, userId)
.sendToTarget();
}
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index a035826..ebbb8b3 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -116,7 +116,7 @@
public void close() {
synchronized (mLock) {
- if (mPtr != 0l) {
+ if (mPtr != 0L) {
nativeClose(mPtr);
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d814ebf..3193ff8 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -465,7 +465,7 @@
try {
context = mContext.createPackageContextAsUser("android", 0, user);
} catch (NameNotFoundException e) {
- Slog.e(TAG, "failed to create package contenxt as user " + user);
+ Slog.e(TAG, "failed to create package context as user " + user);
context = mContext;
}
return context.getContentResolver();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a9025bd..0c429e5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -222,6 +222,10 @@
out.set(left, top, left + width, top + height);
}
+ void getContentRect(Rect out) {
+ out.set(mContentRect);
+ }
+
/** Refer to {@link WindowManagerService#attachStack(int, int, boolean)} */
void attachStack(TaskStack stack, boolean onTop) {
if (stack.mStackId == HOME_STACK_ID) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6bb3e20..71d616d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -63,6 +63,9 @@
private Rect mBounds = new Rect();
final Rect mPreparedFrozenBounds = new Rect();
+ private Rect mPreScrollBounds = new Rect();
+ private boolean mScrollValid;
+
// Bounds used to calculate the insets.
private final Rect mTempInsetBounds = new Rect();
@@ -127,12 +130,25 @@
int yOffset = 0;
if (dockSide != DOCKED_INVALID) {
mStack.getBounds(mTmpRect);
- displayContent.getLogicalDisplayRect(mTmpRect2);
if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
+ // The toast was originally placed at the bottom and centered. To place it
+ // at the bottom-center of the stack, we offset it horizontally by the diff
+ // between the center of the stack bounds vs. the center of the screen.
+ displayContent.getLogicalDisplayRect(mTmpRect2);
xOffset = mTmpRect.centerX() - mTmpRect2.centerX();
} else if (dockSide == DOCKED_TOP) {
+ // The toast was originally placed at the bottom and centered. To place it
+ // at the bottom center of the top stack, we offset it vertically by the diff
+ // between the bottom of the stack bounds vs. the bottom of the content rect.
+ //
+ // Note here we use the content rect instead of the display rect, as we want
+ // the toast's distance to the dock divider (when it's placed at the top half)
+ // to be the same as it's distance to the top of the navigation bar (when it's
+ // placed at the bottom).
+
// We don't adjust for DOCKED_BOTTOM case since it's already at the bottom.
+ displayContent.getContentRect(mTmpRect2);
yOffset = mTmpRect2.bottom - mTmpRect.bottom;
}
mService.mH.obtainMessage(
@@ -258,19 +274,23 @@
// Can't set to fullscreen if we don't have a display to get bounds from...
return BOUNDS_CHANGE_NONE;
}
- if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
+ if (mPreScrollBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) {
return BOUNDS_CHANGE_NONE;
}
int boundsChange = BOUNDS_CHANGE_NONE;
- if (mBounds.left != bounds.left || mBounds.top != bounds.top) {
+ if (mPreScrollBounds.left != bounds.left || mPreScrollBounds.top != bounds.top) {
boundsChange |= BOUNDS_CHANGE_POSITION;
}
- if (mBounds.width() != bounds.width() || mBounds.height() != bounds.height()) {
+ if (mPreScrollBounds.width() != bounds.width() || mPreScrollBounds.height() != bounds.height()) {
boundsChange |= BOUNDS_CHANGE_SIZE;
}
- mBounds.set(bounds);
+
+ mPreScrollBounds.set(bounds);
+
+ resetScrollLocked();
+
mRotation = rotation;
if (displayContent != null) {
displayContent.mDimLayerController.updateDimLayer(this);
@@ -331,6 +351,32 @@
mPreparedFrozenBounds.set(mBounds);
}
+ void resetScrollLocked() {
+ if (mScrollValid) {
+ mScrollValid = false;
+ applyScrollToAllWindows(0, 0);
+ }
+ mBounds.set(mPreScrollBounds);
+ }
+
+ void applyScrollToAllWindows(final int xOffset, final int yOffset) {
+ for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
+ for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+ final WindowState win = windows.get(winNdx);
+ win.mXOffset = xOffset;
+ win.mYOffset = yOffset;
+ }
+ }
+ }
+
+ void applyScrollToWindowIfNeeded(final WindowState win) {
+ if (mScrollValid) {
+ win.mXOffset = mBounds.left;
+ win.mYOffset = mBounds.top;
+ }
+ }
+
boolean scrollLocked(Rect bounds) {
// shift the task bound if it doesn't fully cover the stack area
mStack.getDimBounds(mTmpRect);
@@ -352,21 +398,17 @@
}
}
- if (bounds.equals(mBounds)) {
+ // We can stop here if we're already scrolling and the scrolled bounds not changed.
+ if (mScrollValid && bounds.equals(mBounds)) {
return false;
}
+
// Normal setBounds() does not allow non-null bounds for fullscreen apps.
// We only change bounds for the scrolling case without change it size,
// on resizing path we should still want the validation.
mBounds.set(bounds);
- for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
- final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
- for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
- final WindowState win = windows.get(winNdx);
- win.mXOffset = bounds.left;
- win.mYOffset = bounds.top;
- }
- }
+ mScrollValid = true;
+ applyScrollToAllWindows(bounds.left, bounds.top);
return true;
}
@@ -481,19 +523,25 @@
return;
}
- // Device rotation changed. We don't want the task to move around on the screen when
- // this happens, so update the task bounds so it stays in the same place.
- mTmpRect2.set(mBounds);
+ // Device rotation changed.
+ // - Reset the bounds to the pre-scroll bounds as whatever scrolling was done is no longer
+ // valid.
+ // - Rotate the bounds and notify activity manager if the task can be resized independently
+ // from its stack. The stack will take care of task rotation for the other case.
+ mTmpRect2.set(mPreScrollBounds);
+
+ if (!StackId.isTaskResizeAllowed(mStack.mStackId)) {
+ setBounds(mTmpRect2, mOverrideConfig);
+ return;
+ }
+
displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
- // Post message to inform activity manager of the bounds change simulating
- // a one-way call. We do this to prevent a deadlock between window manager
- // lock and activity manager lock been held. Only tasks within the freeform stack
- // are resizeable independently of their stack resizing.
- if (mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
- mService.mH.sendMessage(mService.mH.obtainMessage(
- RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
- }
+ // Post message to inform activity manager of the bounds change simulating a one-way
+ // call. We do this to prevent a deadlock between window manager lock and activity
+ // manager lock been held.
+ mService.mH.obtainMessage(RESIZE_TASK, mTaskId,
+ RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mPreScrollBounds).sendToTarget();
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index e75780f..632c033 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -18,7 +18,6 @@
import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Debug;
import android.util.EventLog;
@@ -38,6 +37,7 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
@@ -89,6 +89,7 @@
/** Detach this stack from its display when animation completes. */
boolean mDeferDetach;
+ private boolean mUpdateBoundsAfterRotation = false;
TaskStack(WindowManagerService service, int stackId) {
mService = service;
@@ -136,8 +137,8 @@
// it might no longer fully cover the stack area.
// Save the old bounds and re-apply the scroll. This adjusts the bounds to
// fit the new stack bounds.
- task.getBounds(mTmpRect);
task.setBounds(bounds, config);
+ task.getBounds(mTmpRect);
task.scrollLocked(mTmpRect);
} else {
task.setBounds(bounds, config);
@@ -239,6 +240,7 @@
}
void updateDisplayInfo(Rect bounds) {
+ mUpdateBoundsAfterRotation = false;
if (mDisplayContent != null) {
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent);
@@ -248,6 +250,7 @@
} else if (mFullscreen) {
setBounds(null);
} else {
+ mUpdateBoundsAfterRotation = true;
mTmpRect2.set(mBounds);
final int newRotation = mDisplayContent.getDisplayInfo().rotation;
if (mRotation == newRotation) {
@@ -265,6 +268,10 @@
* yet.
*/
void updateBoundsAfterRotation() {
+ if (!mUpdateBoundsAfterRotation) {
+ return;
+ }
+ mUpdateBoundsAfterRotation = false;
final int newRotation = getDisplayInfo().rotation;
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
if (mStackId == DOCKED_STACK_ID) {
@@ -359,6 +366,10 @@
"positionTask: task=" + task + " position=" + position);
mTasks.add(position, task);
+ // If we are moving the task across stacks, the scroll is no longer valid.
+ if (task.mStack != this) {
+ task.resetScrollLocked();
+ }
task.mStack = this;
task.updateDisplayInfo(mDisplayContent);
boolean toTop = position == mTasks.size() - 1;
@@ -539,20 +550,32 @@
outBounds.set(mService.mDockedStackCreateBounds);
return;
}
- // The initial bounds of the docked stack when it is created half the screen space and
- // its bounds can be adjusted after that. The bounds of all other stacks are adjusted
- // to occupy whatever screen space the docked stack isn't occupying.
+
+ // The initial bounds of the docked stack when it is created about half the screen space
+ // and its bounds can be adjusted after that. The bounds of all other stacks are
+ // adjusted to occupy whatever screen space the docked stack isn't occupying.
+ final DisplayInfo di = mDisplayContent.getDisplayInfo();
+ mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
+ mTmpRect2);
+ final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
+ 0 /* minFlingVelocityPxPerSecond */,
+ di.logicalWidth,
+ di.logicalHeight,
+ dockDividerWidth,
+ mService.mCurConfiguration.orientation == ORIENTATION_PORTRAIT,
+ mTmpRect2).getMiddleTarget().position;
+
if (dockOnTopOrLeft) {
if (splitHorizontally) {
- outBounds.right = displayRect.centerX() - dockDividerWidth / 2;
+ outBounds.right = position;
} else {
- outBounds.bottom = displayRect.centerY() - dockDividerWidth / 2;
+ outBounds.bottom = position;
}
} else {
if (splitHorizontally) {
- outBounds.left = displayRect.centerX() + dockDividerWidth / 2;
+ outBounds.left = position - dockDividerWidth;
} else {
- outBounds.top = displayRect.centerY() + dockDividerWidth / 2;
+ outBounds.top = position - dockDividerWidth;
}
}
return;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 685df25..7d142ec 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -119,6 +119,7 @@
import com.android.internal.R;
import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -10269,6 +10270,14 @@
listener);
}
+ @Override
+ public void requestAppKeyboardShortcuts(IResultReceiver receiver) {
+ try {
+ getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver);
+ } catch (RemoteException e) {
+ }
+ }
+
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 058fa67..afbaf00 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1469,10 +1469,8 @@
void applyScrollIfNeeded() {
final Task task = getTask();
- if (task != null && task.isTwoFingerScrollMode()) {
- task.getDimBounds(mTmpRect);
- mXOffset = mTmpRect.left;
- mYOffset = mTmpRect.top;
+ if (task != null) {
+ task.applyScrollToWindowIfNeeded(this);
}
}
@@ -2284,10 +2282,12 @@
void transformFromScreenToSurfaceSpace(Rect rect) {
if (mHScale >= 0) {
- rect.right = rect.left + (int)((rect.right - rect.left) / mHScale);
+ rect.left = (int) (rect.left / mHScale);
+ rect.right = (int) (rect.right / mHScale);
}
if (mVScale >= 0) {
- rect.bottom = rect.top + (int)((rect.bottom - rect.top) / mVScale);
+ rect.top = (int) (rect.top / mVScale);
+ rect.bottom = (int) (rect.bottom / mVScale);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 83ab190..a3a59c0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1355,6 +1355,11 @@
}
}
w.mToken.hasVisible = true;
+
+ final Task task = w.getTask();
+ if (task != null) {
+ task.scheduleShowNonResizeableDockToastIfNeeded();
+ }
}
}
@@ -1513,10 +1518,6 @@
mWin.mAppToken.updateReportedVisibilityLocked();
}
- final Task task = mWin.getTask();
- if (task != null) {
- task.scheduleShowNonResizeableDockToastIfNeeded();
- }
return true;
}
return false;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index dd58b3c..f14b032 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -254,6 +254,7 @@
final IPackageManager mIPackageManager;
final UserManager mUserManager;
final UserManagerInternal mUserManagerInternal;
+ private final LockPatternUtils mLockPatternUtils;
final LocalService mLocalService;
@@ -388,6 +389,7 @@
private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
private static final String TAG_DISABLE_CAMERA = "disable-camera";
private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
+ private static final String TAG_DISABLE_CONTACTS_SEARCH = "disable-contacts-search";
private static final String TAG_DISABLE_BLUETOOTH_CONTACT_SHARING
= "disable-bt-contacts-sharing";
private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
@@ -476,6 +478,7 @@
boolean encryptionRequested = false;
boolean disableCamera = false;
boolean disableCallerId = false;
+ boolean disableContactsSearch = false;
boolean disableBluetoothContactSharing = true;
boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
boolean requireAutoTime = false; // Can only be set by a device owner.
@@ -638,6 +641,11 @@
out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
out.endTag(null, TAG_DISABLE_CALLER_ID);
}
+ if (disableContactsSearch) {
+ out.startTag(null, TAG_DISABLE_CONTACTS_SEARCH);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(disableContactsSearch));
+ out.endTag(null, TAG_DISABLE_CONTACTS_SEARCH);
+ }
if (disableBluetoothContactSharing) {
out.startTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING);
out.attribute(null, ATTR_VALUE,
@@ -809,6 +817,9 @@
} else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
disableCallerId = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_DISABLE_CONTACTS_SEARCH.equals(tag)) {
+ disableContactsSearch = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_DISABLE_BLUETOOTH_CONTACT_SHARING.equals(tag)) {
disableBluetoothContactSharing = Boolean.parseBoolean(parser
.getAttributeValue(null, ATTR_VALUE));
@@ -1034,6 +1045,8 @@
pw.println(disableCamera);
pw.print(prefix); pw.print("disableCallerId=");
pw.println(disableCallerId);
+ pw.print(prefix); pw.print("disableContactsSearch=");
+ pw.println(disableContactsSearch);
pw.print(prefix); pw.print("disableBluetoothContactSharing=");
pw.println(disableBluetoothContactSharing);
pw.print(prefix); pw.print("disableScreenCapture=");
@@ -1317,6 +1330,7 @@
mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
mLocalService = new LocalService();
+ mLockPatternUtils = new LockPatternUtils(mContext);
mHasFeature = mContext.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
@@ -1808,7 +1822,7 @@
if (!mHasFeature) {
return null;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
Intent resolveIntent = new Intent();
resolveIntent.setComponent(adminName);
List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceiversAsUser(
@@ -2411,7 +2425,7 @@
Bundle onEnableData) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
DevicePolicyData policy = getUserData(userHandle);
DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
@@ -2457,7 +2471,7 @@
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null;
}
@@ -2468,7 +2482,7 @@
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
DevicePolicyData policyData = getUserData(userHandle);
return policyData.mRemovingAdmins.contains(adminReceiver);
@@ -2480,7 +2494,7 @@
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (administrator == null) {
@@ -2497,7 +2511,7 @@
return Collections.EMPTY_LIST;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
DevicePolicyData policy = getUserData(userHandle);
final int N = policy.mAdminList.size();
@@ -2517,7 +2531,7 @@
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
DevicePolicyData policy = getUserData(userHandle);
final int N = policy.mAdminList.size();
@@ -2535,7 +2549,7 @@
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin == null) {
@@ -2561,10 +2575,16 @@
}
}
- private boolean isAdminApiLevelPreN(@NonNull ComponentName who, int userHandle) {
+ private boolean isAdminApiLevelMOrBelow(@NonNull ComponentName who, int userHandle) {
DeviceAdminInfo adminInfo = findAdmin(who, userHandle, false);
return adminInfo.getActivityInfo().applicationInfo.targetSdkVersion
- < Build.VERSION_CODES.N;
+ <= Build.VERSION_CODES.M;
+ }
+
+ @Override
+ public boolean isSeparateProfileChallengeAllowed(int userHandle) {
+ ComponentName profileOwner = getProfileOwner(userHandle);
+ return !isAdminApiLevelMOrBelow(profileOwner, userHandle);
}
@Override
@@ -2594,7 +2614,7 @@
if (!mHasFeature) {
return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@@ -2606,7 +2626,7 @@
return admin != null ? admin.passwordQuality : mode;
}
- if (LockPatternUtils.isSeparateWorkChallengeEnabled() && !parent) {
+ if (isSeparateProfileChallengeEnabled(userHandle) && !parent) {
// If a Work Challenge is in use, only return its restrictions.
DevicePolicyData policy = getUserDataUnchecked(userHandle);
final int N = policy.mAdminList.size();
@@ -2626,7 +2646,7 @@
// Only aggregate data for the parent profile plus the non-work challenge
// enabled profiles.
if (!(userInfo.isManagedProfile()
- && LockPatternUtils.isSeparateWorkChallengeEnabled())) {
+ && isSeparateProfileChallengeEnabled(userInfo.id))) {
DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
@@ -2642,6 +2662,15 @@
}
}
+ private boolean isSeparateProfileChallengeEnabled(int userHandle) {
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ return mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
@Override
public void setPasswordMinimumLength(ComponentName who, int length) {
if (!mHasFeature) {
@@ -2664,7 +2693,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -2711,7 +2740,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -2771,7 +2800,7 @@
if (!mHasFeature) {
return 0L;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
long timeout = 0L;
@@ -2898,7 +2927,7 @@
if (!mHasFeature) {
return 0L;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
return getPasswordExpirationLocked(who, userHandle);
}
@@ -2926,7 +2955,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -2970,7 +2999,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3017,7 +3046,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3067,7 +3096,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3117,7 +3146,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3167,7 +3196,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int length = 0;
@@ -3200,7 +3229,7 @@
if (!mHasFeature) {
return true;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
int id = getCredentialOwner(userHandle);
@@ -3212,8 +3241,8 @@
getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
ComponentName adminComponentName = admin.info.getComponent();
// TODO: Include the Admin sdk level check in LockPatternUtils check.
- ComponentName who = !isAdminApiLevelPreN(adminComponentName, userHandle)
- && LockPatternUtils.isSeparateWorkChallengeEnabled()
+ ComponentName who = !isAdminApiLevelMOrBelow(adminComponentName, userHandle)
+ && isSeparateProfileChallengeEnabled(userHandle)
? adminComponentName : null;
if (policy.mActivePasswordQuality < getPasswordQuality(who, userHandle, parent)
|| policy.mActivePasswordLength < getPasswordMinimumLength(null, userHandle)) {
@@ -3272,7 +3301,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
ActiveAdmin admin = (who != null) ? getActiveAdminUncheckedLocked(who, userHandle)
: getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle);
@@ -3285,7 +3314,7 @@
if (!mHasFeature) {
return UserHandle.USER_NULL;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked(userHandle);
return admin != null ? admin.getUserHandle().getIdentifier() : UserHandle.USER_NULL;
@@ -3584,7 +3613,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
long time = 0;
@@ -3887,6 +3916,42 @@
}
}
+ @Override
+ public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage)
+ throws SecurityException {
+ synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
+
+ final int userId = mInjector.userHandleGetCallingUserId();
+ final long token = mInjector.binderClearCallingIdentity();
+ try{
+ ConnectivityManager connectivityManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public String getAlwaysOnVpnPackage(ComponentName admin)
+ throws SecurityException {
+ synchronized (this) {
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ }
+
+ final int userId = mInjector.userHandleGetCallingUserId();
+ final long token = mInjector.binderClearCallingIdentity();
+ try{
+ ConnectivityManager connectivityManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return connectivityManager.getAlwaysOnVpnPackageForUser(userId);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(token);
+ }
+ }
+
private void wipeDataLocked(boolean wipeExtRequested, String reason) {
if (wipeExtRequested) {
StorageManager sm = (StorageManager) mContext.getSystemService(
@@ -3906,7 +3971,7 @@
return;
}
final int userHandle = mInjector.userHandleGetCallingUserId();
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
@@ -3985,7 +4050,7 @@
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -4014,9 +4079,9 @@
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
// Managed Profile password can only be changed when per user encryption is present.
- if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+ if (!isSeparateProfileChallengeEnabled(userHandle)) {
enforceNotManagedProfile(userHandle, "set the active password");
}
@@ -4043,7 +4108,7 @@
setExpirationAlarmCheckLocked(mContext, policy);
// Send a broadcast to each profile using this password as its primary unlock.
- if (LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
sendAdminCommandLocked(
DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
@@ -4083,7 +4148,7 @@
@Override
public void reportFailedPasswordAttempt(int userHandle) {
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
enforceNotManagedProfile(userHandle, "report failed password attempt");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -4125,7 +4190,7 @@
@Override
public void reportSuccessfulPasswordAttempt(int userHandle) {
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -4209,7 +4274,7 @@
if (!mHasFeature) {
return null;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized(this) {
DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
// Scan through active admins and find if anyone has already
@@ -4346,7 +4411,7 @@
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
// Check for permissions if a particular caller is specified
if (who != null) {
@@ -4376,7 +4441,7 @@
if (!mHasFeature) {
// Ok to return current status.
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
return getEncryptionStatus();
}
@@ -4624,7 +4689,7 @@
if (!mHasFeature) {
return 0;
}
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
long ident = mInjector.binderClearCallingIdentity();
try {
synchronized (this) {
@@ -4635,10 +4700,9 @@
UserInfo user = mUserManager.getUserInfo(userHandle);
final List<UserInfo> profiles;
- if (user.isManagedProfile() || LockPatternUtils.isSeparateWorkChallengeEnabled()) {
- // If we are being asked about a managed profile or the main user profile has a
- // separate lock from the work profile, just return keyguard features disabled
- // by admins in the profile.
+ if (user.isManagedProfile()) {
+ // If we are being asked about a managed profile, just return keyguard features
+ // disabled by admins in the profile.
profiles = Collections.singletonList(user);
} else {
// Otherwise return those set by admins in the user
@@ -4657,9 +4721,11 @@
// If we are being asked explictly about this user
// return all disabled features even if its a managed profile.
which |= admin.disabledKeyguardFeatures;
- } else {
+ } else if (!isSeparateProfileChallengeEnabled(
+ userInfo.id)) {
// Otherwise a managed profile is only allowed to disable
- // some features on the parent user.
+ // some features on the parent user, and we only aggregate them if
+ // it doesn't have its own challenge.
which |= (admin.disabledKeyguardFeatures
& PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER);
}
@@ -5185,17 +5251,28 @@
}
}
- private void enforceCrossUserPermission(int userHandle) {
+ private void enforceFullCrossUsersPermission(int userHandle) {
+ enforceSystemUserOrPermission(userHandle,
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ }
+
+ private void enforceCrossUsersPermission(int userHandle) {
+ enforceSystemUserOrPermission(userHandle,
+ android.Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+
+ private void enforceSystemUserOrPermission(int userHandle, String permission) {
if (userHandle < 0) {
throw new IllegalArgumentException("Invalid userId " + userHandle);
}
final int callingUid = mInjector.binderGetCallingUid();
- if (userHandle == UserHandle.getUserId(callingUid)) return;
+ if (userHandle == UserHandle.getUserId(callingUid)) {
+ return;
+ }
if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
|| callingUid == Process.ROOT_UID)) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have"
- + " INTERACT_ACROSS_USERS_FULL permission");
+ mContext.enforceCallingOrSelfPermission(permission,
+ "Must be system or have " + permission + " permission");
}
}
@@ -5405,7 +5482,7 @@
return null;
}
Preconditions.checkNotNull(agent, "agent null");
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
final String componentName = agent.flattenToString();
@@ -6068,7 +6145,7 @@
@Override
public Bundle getUserRestrictions(ComponentName who, int userHandle) {
Preconditions.checkNotNull(who, "ComponentName is null");
- enforceCrossUserPermission(userHandle);
+ enforceFullCrossUsersPermission(userHandle);
synchronized (this) {
ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(who, userHandle);
if (activeAdmin == null) {
@@ -6260,7 +6337,7 @@
@Override
public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
- enforceCrossUserPermission(userId);
+ enforceFullCrossUsersPermission(userId);
if (!mHasFeature) {
return null;
}
@@ -6332,7 +6409,7 @@
DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
if (admin.disableCallerId != disabled) {
admin.disableCallerId = disabled;
- saveSettingsLocked(UserHandle.getCallingUserId());
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
}
}
@@ -6352,8 +6429,7 @@
@Override
public boolean getCrossProfileCallerIdDisabledForUser(int userId) {
- // TODO: Should there be a check to make sure this relationship is within a profile group?
- //enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
+ enforceCrossUsersPermission(userId);
synchronized (this) {
ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
return (admin != null) ? admin.disableCallerId : false;
@@ -6361,6 +6437,44 @@
}
@Override
+ public void setCrossProfileContactsSearchDisabled(ComponentName who, boolean disabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ synchronized (this) {
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (admin.disableContactsSearch != disabled) {
+ admin.disableContactsSearch = disabled;
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+ }
+ }
+
+ @Override
+ public boolean getCrossProfileContactsSearchDisabled(ComponentName who) {
+ if (!mHasFeature) {
+ return false;
+ }
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ synchronized (this) {
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ return admin.disableContactsSearch;
+ }
+ }
+
+ @Override
+ public boolean getCrossProfileContactsSearchDisabledForUser(int userId) {
+ enforceCrossUsersPermission(userId);
+ synchronized (this) {
+ ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
+ return (admin != null) ? admin.disableContactsSearch : false;
+ }
+ }
+
+ @Override
public void startManagedQuickContact(String actualLookupKey, long actualContactId,
long actualDirectoryId, Intent originalIntent) {
final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 287b39c..ee5f23f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -67,6 +67,7 @@
import com.android.server.lights.LightsService;
import com.android.server.media.MediaRouterService;
import com.android.server.media.MediaSessionService;
+import com.android.server.media.MediaResourceMonitorService;
import com.android.server.media.projection.MediaProjectionManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
@@ -128,6 +129,8 @@
"com.android.server.midi.MidiService$Lifecycle";
private static final String WIFI_SERVICE_CLASS =
"com.android.server.wifi.WifiService";
+ private static final String WIFI_NAN_SERVICE_CLASS =
+ "com.android.server.wifi.nan.WifiNanService";
private static final String WIFI_P2P_SERVICE_CLASS =
"com.android.server.wifi.p2p.WifiP2pService";
private static final String ETHERNET_SERVICE_CLASS =
@@ -737,6 +740,11 @@
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_NAN)) {
+ mSystemServiceManager.startService(WIFI_NAN_SERVICE_CLASS);
+ } else {
+ Slog.i(TAG, "No Wi-Fi NAN Service (NAN support Not Present)");
+ }
mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
mSystemServiceManager.startService(
@@ -1025,6 +1033,8 @@
mSystemServiceManager.startService(TvInputManagerService.class);
}
+ mSystemServiceManager.startService(MediaResourceMonitorService.class);
+
if (!disableNonCoreServices) {
traceBeginAndSlog("StartMediaRouterService");
try {
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 2e0866c..5abb6e7 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -555,8 +555,14 @@
// to handle it as the change may affect ongoing print jobs.
UserState userState = getOrCreateUserStateLocked(getChangingUserId());
boolean stoppedSomePackages = false;
- Iterator<PrintServiceInfo> iterator = userState.getEnabledPrintServices()
- .iterator();
+
+ List<PrintServiceInfo> enabledServices = userState
+ .getEnabledPrintServices();
+ if (enabledServices == null) {
+ return false;
+ }
+
+ Iterator<PrintServiceInfo> iterator = enabledServices.iterator();
while (iterator.hasNext()) {
ComponentName componentName = iterator.next().getComponentName();
String componentPackage = componentName.getPackageName();
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 0af1525..5ee4066 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -863,8 +863,7 @@
}
private void throwIfPrinterIdTampered(ComponentName serviceName, PrinterId printerId) {
- if (printerId == null || printerId.getServiceName() == null
- || !printerId.getServiceName().equals(serviceName)) {
+ if (printerId == null || !printerId.getServiceName().equals(serviceName)) {
throw new IllegalArgumentException("Invalid printer id: " + printerId);
}
}
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 4198217..78edc4d 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -20,6 +20,7 @@
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -336,7 +337,7 @@
mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null);
}
- public List<PrintServiceInfo> getEnabledPrintServices() {
+ public @Nullable List<PrintServiceInfo> getEnabledPrintServices() {
synchronized (mLock) {
List<PrintServiceInfo> enabledServices = null;
final int installedServiceCount = mInstalledServices.size();
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index eed326e..248cf66 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -102,6 +102,8 @@
</intent-filter>
</receiver>
+ <service android:name="com.android.server.job.MockPriorityJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
</application>
<instrumentation
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 27deb72..27d5207 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -59,6 +59,7 @@
import android.os.MessageQueue.IdleHandler;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
import android.util.LogPrinter;
@@ -1504,4 +1505,10 @@
ka3.stop();
callback3.expectStopped();
}
+
+ @SmallTest
+ public void testGetCaptivePortalServerUrl() throws Exception {
+ String url = mCm.getCaptivePortalServerUrl();
+ assertEquals("http://connectivitycheck.gstatic.com/generate_204", url);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 312b1b0..ae0a25e 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -179,7 +179,21 @@
loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS);
// Assert late runtime was clamped to be now + period*2.
assertTrue("Early runtime wasn't correctly clamped.",
- loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS*2);
+ loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS * 2);
+ }
+
+ public void testPriorityPersisted() throws Exception {
+ JobInfo.Builder b = new Builder(92, mComponent)
+ .setOverrideDeadline(5000)
+ .setPriority(42)
+ .setPersisted(true);
+ final JobStatus js = new JobStatus(b.build(), SOME_UID);
+ mTaskStoreUnderTest.add(js);
+ Thread.sleep(IO_WAIT);
+ final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet);
+ JobStatus loaded = jobStatusSet.iterator().next();
+ assertEquals("Priority not correctly persisted.", 42, loaded.getPriority());
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java b/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java
new file mode 100644
index 0000000..3ea86f2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/MockPriorityJobService.java
@@ -0,0 +1,106 @@
+/*
+ * 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.server.job;
+
+import android.annotation.TargetApi;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+@TargetApi(24)
+public class MockPriorityJobService extends JobService {
+ private static final String TAG = "MockPriorityJobService";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.e(TAG, "Created test service.");
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ Log.i(TAG, "Test job executing: " + params.getJobId());
+ TestEnvironment.getTestEnvironment().executedEvents.add(
+ new TestEnvironment.Event(TestEnvironment.EVENT_START_JOB, params.getJobId()));
+ return true; // Job not finished
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ Log.i(TAG, "Test job stop executing: " + params.getJobId());
+ int reason = params.getStopReason();
+ int event = TestEnvironment.EVENT_STOP_JOB;
+ Log.d(TAG, "stop reason: " + String.valueOf(reason));
+ if (reason == JobParameters.REASON_PREEMPT) {
+ event = TestEnvironment.EVENT_PREEMPT_JOB;
+ Log.d(TAG, "preempted " + String.valueOf(params.getJobId()));
+ }
+ TestEnvironment.getTestEnvironment().executedEvents
+ .add(new TestEnvironment.Event(event, params.getJobId()));
+ return false; // Do not reschedule
+ }
+
+ public static class TestEnvironment {
+
+ public static final int EVENT_START_JOB = 0;
+ public static final int EVENT_PREEMPT_JOB = 1;
+ public static final int EVENT_STOP_JOB = 2;
+
+ private static TestEnvironment kTestEnvironment;
+
+ private ArrayList<Event> executedEvents = new ArrayList<Event>();
+
+ public static TestEnvironment getTestEnvironment() {
+ if (kTestEnvironment == null) {
+ kTestEnvironment = new TestEnvironment();
+ }
+ return kTestEnvironment;
+ }
+
+ public static class Event {
+ public int event;
+ public int jobId;
+
+ public Event() {
+ }
+
+ public Event(int event, int jobId) {
+ this.event = event;
+ this.jobId = jobId;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Event) {
+ Event otherEvent = (Event) other;
+ return otherEvent.event == event && otherEvent.jobId == jobId;
+ }
+ return false;
+ }
+ }
+
+ public void setUp() {
+ executedEvents.clear();
+ }
+
+ public ArrayList<Event> getExecutedEvents() {
+ return executedEvents;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/PrioritySchedulingTest.java b/services/tests/servicestests/src/com/android/server/job/PrioritySchedulingTest.java
new file mode 100644
index 0000000..63bccfa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/PrioritySchedulingTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.server.job;
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.AndroidTestCase;
+import com.android.server.job.MockPriorityJobService.TestEnvironment;
+import com.android.server.job.MockPriorityJobService.TestEnvironment.Event;
+
+import java.util.ArrayList;
+
+@TargetApi(24)
+public class PrioritySchedulingTest extends AndroidTestCase {
+ /** Environment that notifies of JobScheduler callbacks. */
+ static TestEnvironment kTestEnvironment = TestEnvironment.getTestEnvironment();
+ /** Handle for the service which receives the execution callbacks from the JobScheduler. */
+ static ComponentName kJobServiceComponent;
+ JobScheduler mJobScheduler;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ kTestEnvironment.setUp();
+ kJobServiceComponent = new ComponentName(getContext(), MockPriorityJobService.class);
+ mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ mJobScheduler.cancelAll();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mJobScheduler.cancelAll();
+ super.tearDown();
+ }
+
+ public void testLowerPriorityJobPreempted() throws Exception {
+ JobInfo job1 = new JobInfo.Builder(111, kJobServiceComponent)
+ .setPriority(1)
+ .setOverrideDeadline(7000L)
+ .build();
+ JobInfo job2 = new JobInfo.Builder(222, kJobServiceComponent)
+ .setPriority(1)
+ .setOverrideDeadline(7000L)
+ .build();
+ JobInfo job3 = new JobInfo.Builder(333, kJobServiceComponent)
+ .setPriority(1)
+ .setOverrideDeadline(7000L)
+ .build();
+ JobInfo job4 = new JobInfo.Builder(444, kJobServiceComponent)
+ .setPriority(2)
+ .setMinimumLatency(2000L)
+ .setOverrideDeadline(7000L)
+ .build();
+ mJobScheduler.schedule(job1);
+ mJobScheduler.schedule(job2);
+ mJobScheduler.schedule(job3);
+ mJobScheduler.schedule(job4);
+ Thread.sleep(10000); // Wait for job 4 to preempt one of the lower priority jobs
+
+ Event job4Execution = new Event(TestEnvironment.EVENT_START_JOB, 444);
+ ArrayList<Event> executedEvents = kTestEnvironment.getExecutedEvents();
+ boolean wasJob4Executed = executedEvents.contains(job4Execution);
+ boolean wasSomeJobPreempted = false;
+ for (Event event: executedEvents) {
+ if (event.event == TestEnvironment.EVENT_PREEMPT_JOB) {
+ wasSomeJobPreempted = true;
+ break;
+ }
+ }
+ assertTrue("No job was preempted.", wasSomeJobPreempted);
+ assertTrue("Lower priority jobs were not preempted.", wasJob4Executed);
+ }
+
+ public void testHigherPriorityJobNotPreempted() throws Exception {
+ JobInfo job1 = new JobInfo.Builder(111, kJobServiceComponent)
+ .setPriority(2)
+ .setOverrideDeadline(7000L)
+ .build();
+ JobInfo job2 = new JobInfo.Builder(222, kJobServiceComponent)
+ .setPriority(2)
+ .setOverrideDeadline(7000L)
+ .build();
+ JobInfo job3 = new JobInfo.Builder(333, kJobServiceComponent)
+ .setPriority(2)
+ .setOverrideDeadline(7000L)
+ .build();
+ JobInfo job4 = new JobInfo.Builder(444, kJobServiceComponent)
+ .setPriority(1)
+ .setMinimumLatency(2000L)
+ .setOverrideDeadline(7000L)
+ .build();
+ mJobScheduler.schedule(job1);
+ mJobScheduler.schedule(job2);
+ mJobScheduler.schedule(job3);
+ mJobScheduler.schedule(job4);
+ Thread.sleep(10000); // Wait for job 4 to preempt one of the higher priority jobs
+
+ Event job4Execution = new Event(TestEnvironment.EVENT_START_JOB, 444);
+ boolean wasJob4Executed = kTestEnvironment.getExecutedEvents().contains(job4Execution);
+ assertFalse("Higher priority job was preempted.", wasJob4Executed);
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index e27441e..4253cd4 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -280,6 +280,11 @@
mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL);
}
+ @Override
+ public long getAppIdleRollingWindowDurationMillis() {
+ return mAppIdleWallclockThresholdMillis * 2;
+ }
+
private void cleanUpRemovedUsersLocked() {
final List<UserInfo> users = mUserManager.getUsers(true);
if (users == null || users.size() == 0) {
@@ -548,7 +553,8 @@
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
final UserUsageStatsService service = mUserState.valueAt(i);
- service.onTimeChanged(expectedSystemTime, actualSystemTime, resetBeginIdleTime);
+ service.onTimeChanged(expectedSystemTime, actualSystemTime, mScreenOnTime,
+ resetBeginIdleTime);
}
mRealTimeSnapshot = actualRealtime;
mSystemTimeSnapshot = actualSystemTime;
@@ -1106,7 +1112,13 @@
* Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
*/
private class SettingsObserver extends ContentObserver {
- private static final String KEY_IDLE_DURATION = "idle_duration";
+ /**
+ * This flag has been used to disable app idle on older builds with bug b/26355386.
+ */
+ @Deprecated
+ private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
+
+ private static final String KEY_IDLE_DURATION = "idle_duration2";
private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
private static final String KEY_PAROLE_INTERVAL = "parole_interval";
private static final String KEY_PAROLE_DURATION = "parole_duration";
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 72621a4..224faf4 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -59,6 +59,7 @@
private final Context mContext;
private final UsageStatsDatabase mDatabase;
private final IntervalStats[] mCurrentStats;
+ private IntervalStats mAppIdleRollingWindow;
private boolean mStatsChanged = false;
private final UnixCalendar mDailyExpiryDate;
private final StatsUpdatedListener mListener;
@@ -72,6 +73,7 @@
interface StatsUpdatedListener {
void onStatsUpdated();
+ long getAppIdleRollingWindowDurationMillis();
}
UserUsageStatsService(Context context, int userId, File usageStatsDir,
@@ -133,6 +135,8 @@
stat.updateConfigurationStats(null, stat.lastTimeSaved);
}
+ refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
+
if (mDatabase.isNewUpdate()) {
initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
mDatabase.isFirstUpdate());
@@ -158,18 +162,24 @@
for (IntervalStats stats : mCurrentStats) {
stats.update(packageName, currentTimeMillis, Event.SYSTEM_INTERACTION);
stats.updateBeginIdleTime(packageName, deviceUsageTime);
- mStatsChanged = true;
}
+
+ mAppIdleRollingWindow.update(packageName, currentTimeMillis,
+ Event.SYSTEM_INTERACTION);
+ mAppIdleRollingWindow.updateBeginIdleTime(packageName, deviceUsageTime);
+ mStatsChanged = true;
}
}
// Persist the new OTA-related access stats.
persistActiveStats();
}
- void onTimeChanged(long oldTime, long newTime, boolean resetBeginIdleTime) {
+ void onTimeChanged(long oldTime, long newTime, long deviceUsageTime,
+ boolean resetBeginIdleTime) {
persistActiveStats();
mDatabase.onTimeChanged(newTime - oldTime);
loadActiveStats(newTime, resetBeginIdleTime);
+ refreshAppIdleRollingWindow(newTime, deviceUsageTime);
}
void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -181,7 +191,7 @@
if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
// Need to rollover
- rolloverStats(event.mTimeStamp);
+ rolloverStats(event.mTimeStamp, deviceUsageTime);
}
final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
@@ -211,6 +221,11 @@
}
}
+ if (event.mEventType != Event.CONFIGURATION_CHANGE) {
+ mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime);
+ }
+
notifyStatsChanged();
}
@@ -222,6 +237,7 @@
for (IntervalStats stats : mCurrentStats) {
stats.updateBeginIdleTime(packageName, beginIdleTime);
}
+ mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime);
notifyStatsChanged();
}
@@ -229,6 +245,7 @@
for (IntervalStats stats : mCurrentStats) {
stats.updateSystemLastUsedTime(packageName, lastUsedTime);
}
+ mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime);
notifyStatsChanged();
}
@@ -387,9 +404,8 @@
}
long getBeginIdleTime(String packageName) {
- final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
UsageStats packageUsage;
- if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+ if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
return -1;
} else {
return packageUsage.getBeginIdleTime();
@@ -397,9 +413,8 @@
}
long getSystemLastUsedTime(String packageName) {
- final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
UsageStats packageUsage;
- if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
+ if ((packageUsage = mAppIdleRollingWindow.packageStats.get(packageName)) == null) {
return -1;
} else {
return packageUsage.getLastTimeSystemUsed();
@@ -420,7 +435,7 @@
}
}
- private void rolloverStats(final long currentTimeMillis) {
+ private void rolloverStats(final long currentTimeMillis, final long deviceUsageTime) {
final long startTime = SystemClock.elapsedRealtime();
Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
@@ -461,6 +476,8 @@
}
persistActiveStats();
+ refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime);
+
final long totalTime = SystemClock.elapsedRealtime() - startTime;
Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
+ " milliseconds");
@@ -503,6 +520,7 @@
}
}
}
+
mStatsChanged = false;
updateRolloverDeadline();
}
@@ -516,6 +534,71 @@
mDailyExpiryDate.getTimeInMillis() + ")");
}
+ private static void mergePackageStats(IntervalStats dst, IntervalStats src,
+ final long deviceUsageTime) {
+ dst.endTime = Math.max(dst.endTime, src.endTime);
+
+ final int srcPackageCount = src.packageStats.size();
+ for (int i = 0; i < srcPackageCount; i++) {
+ final String packageName = src.packageStats.keyAt(i);
+ final UsageStats srcStats = src.packageStats.valueAt(i);
+ UsageStats dstStats = dst.packageStats.get(packageName);
+ if (dstStats == null) {
+ dstStats = new UsageStats(srcStats);
+ dst.packageStats.put(packageName, dstStats);
+ } else {
+ dstStats.add(src.packageStats.valueAt(i));
+ }
+
+ // App idle times can not begin in the future. This happens if we had a time change.
+ if (dstStats.mBeginIdleTime > deviceUsageTime) {
+ dstStats.mBeginIdleTime = deviceUsageTime;
+ }
+ }
+ }
+
+ /**
+ * App idle operates on a rolling window of time. When we roll over time, we end up with a
+ * period of time where in-memory stats are empty and we don't hit the disk for older stats
+ * for performance reasons. Suddenly all apps will become idle.
+ *
+ * Instead, at times we do a deep query to find all the apps that have run in the past few
+ * days and keep the cached data up to date.
+ *
+ * @param currentTimeMillis
+ */
+ void refreshAppIdleRollingWindow(final long currentTimeMillis, final long deviceUsageTime) {
+ // Start the rolling window for AppIdle requests.
+ final long startRangeMillis = currentTimeMillis -
+ mListener.getAppIdleRollingWindowDurationMillis();
+
+ List<IntervalStats> stats = mDatabase.queryUsageStats(UsageStatsManager.INTERVAL_DAILY,
+ startRangeMillis, currentTimeMillis, new StatCombiner<IntervalStats>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<IntervalStats> accumulatedResult) {
+ IntervalStats accum;
+ if (accumulatedResult.isEmpty()) {
+ accum = new IntervalStats();
+ accum.beginTime = stats.beginTime;
+ accumulatedResult.add(accum);
+ } else {
+ accum = accumulatedResult.get(0);
+ }
+
+ mergePackageStats(accum, stats, deviceUsageTime);
+ }
+ });
+
+ if (stats == null || stats.isEmpty()) {
+ mAppIdleRollingWindow = new IntervalStats();
+ mergePackageStats(mAppIdleRollingWindow,
+ mCurrentStats[UsageStatsManager.INTERVAL_YEARLY], deviceUsageTime);
+ } else {
+ mAppIdleRollingWindow = stats.get(0);
+ }
+ }
+
//
// -- DUMP related methods --
//
@@ -538,6 +621,9 @@
pw.println(" stats");
printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
}
+
+ pw.println("AppIdleRollingWindow cache");
+ printIntervalStats(pw, mAppIdleRollingWindow, screenOnTime, true);
}
private String formatDateTime(long dateTime, boolean pretty) {
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index b18af33..b56ce73 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -687,7 +687,7 @@
.append("] PhoneAccount: ")
.append(mAccountHandle)
.append(" Capabilities: ")
- .append(mCapabilities)
+ .append(capabilitiesToString(mCapabilities))
.append(" Schemes: ");
for (String scheme : mSupportedUriSchemes) {
sb.append(scheme)
@@ -698,4 +698,42 @@
sb.append("]");
return sb.toString();
}
+
+ /**
+ * Generates a string representation of a capabilities bitmask.
+ *
+ * @param capabilities The capabilities bitmask.
+ * @return String representation of the capabilities bitmask.
+ */
+ private String capabilitiesToString(int capabilities) {
+ StringBuilder sb = new StringBuilder();
+ if (hasCapabilities(CAPABILITY_VIDEO_CALLING)) {
+ sb.append("Video ");
+ }
+ if (hasCapabilities(CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) {
+ sb.append("Presence ");
+ }
+ if (hasCapabilities(CAPABILITY_CALL_PROVIDER)) {
+ sb.append("CallProvider ");
+ }
+ if (hasCapabilities(CAPABILITY_CALL_SUBJECT)) {
+ sb.append("CallSubject ");
+ }
+ if (hasCapabilities(CAPABILITY_CONNECTION_MANAGER)) {
+ sb.append("ConnectionMgr ");
+ }
+ if (hasCapabilities(CAPABILITY_EMERGENCY_CALLS_ONLY)) {
+ sb.append("EmergOnly ");
+ }
+ if (hasCapabilities(CAPABILITY_MULTI_USER)) {
+ sb.append("MultiUser ");
+ }
+ if (hasCapabilities(CAPABILITY_PLACE_EMERGENCY_CALLS)) {
+ sb.append("PlaceEmerg ");
+ }
+ if (hasCapabilities(CAPABILITY_SIM_SUBSCRIPTION)) {
+ sb.append("SimSub ");
+ }
+ return sb.toString();
+ }
}
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index dabf706..216603c 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -16,13 +16,23 @@
package android.telecom;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Represents attributes of video calls.
*/
public class VideoProfile implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({QUALITY_UNKNOWN, QUALITY_HIGH, QUALITY_MEDIUM, QUALITY_LOW, QUALITY_DEFAULT})
+ public @interface VideoQuality {}
+
/**
* "Unknown" video quality.
* @hide
@@ -48,6 +58,14 @@
*/
public static final int QUALITY_DEFAULT = 4;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ flag = true,
+ value = {STATE_AUDIO_ONLY, STATE_TX_ENABLED, STATE_RX_ENABLED, STATE_BIDIRECTIONAL,
+ STATE_PAUSED})
+ public @interface VideoState {}
+
/**
* Used when answering or dialing a call to indicate that the call does not have a video
* component.
@@ -107,7 +125,7 @@
*
* @param videoState The video state.
*/
- public VideoProfile(int videoState) {
+ public VideoProfile(@VideoState int videoState) {
this(videoState, QUALITY_DEFAULT);
}
@@ -117,7 +135,7 @@
* @param videoState The video state.
* @param quality The video quality.
*/
- public VideoProfile(int videoState, int quality) {
+ public VideoProfile(@VideoState int videoState, @VideoQuality int quality) {
mVideoState = videoState;
mQuality = quality;
}
@@ -130,6 +148,7 @@
* {@link VideoProfile#STATE_RX_ENABLED},
* {@link VideoProfile#STATE_PAUSED}.
*/
+ @VideoState
public int getVideoState() {
return mVideoState;
}
@@ -139,6 +158,7 @@
* Valid values: {@link VideoProfile#QUALITY_HIGH}, {@link VideoProfile#QUALITY_MEDIUM},
* {@link VideoProfile#QUALITY_LOW}, {@link VideoProfile#QUALITY_DEFAULT}.
*/
+ @VideoQuality
public int getQuality() {
return mQuality;
}
@@ -211,7 +231,7 @@
* @param videoState The video state.
* @return String representation of the video state.
*/
- public static String videoStateToString(int videoState) {
+ public static String videoStateToString(@VideoState int videoState) {
StringBuilder sb = new StringBuilder();
sb.append("Audio");
@@ -240,7 +260,7 @@
* @param videoState The video state.
* @return {@code True} if the video state is audio only, {@code false} otherwise.
*/
- public static boolean isAudioOnly(int videoState) {
+ public static boolean isAudioOnly(@VideoState int videoState) {
return !hasState(videoState, VideoProfile.STATE_TX_ENABLED)
&& !hasState(videoState, VideoProfile.STATE_RX_ENABLED);
}
@@ -251,7 +271,7 @@
* @param videoState The video state.
* @return {@code True} if video transmission or reception is enabled, {@code false} otherwise.
*/
- public static boolean isVideo(int videoState) {
+ public static boolean isVideo(@VideoState int videoState) {
return hasState(videoState, VideoProfile.STATE_TX_ENABLED)
|| hasState(videoState, VideoProfile.STATE_RX_ENABLED)
|| hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
@@ -263,7 +283,7 @@
* @param videoState The video state.
* @return {@code True} if video transmission is enabled, {@code false} otherwise.
*/
- public static boolean isTransmissionEnabled(int videoState) {
+ public static boolean isTransmissionEnabled(@VideoState int videoState) {
return hasState(videoState, VideoProfile.STATE_TX_ENABLED);
}
@@ -273,7 +293,7 @@
* @param videoState The video state.
* @return {@code True} if video reception is enabled, {@code false} otherwise.
*/
- public static boolean isReceptionEnabled(int videoState) {
+ public static boolean isReceptionEnabled(@VideoState int videoState) {
return hasState(videoState, VideoProfile.STATE_RX_ENABLED);
}
@@ -283,7 +303,7 @@
* @param videoState The video state.
* @return {@code True} if the video is bi-directional, {@code false} otherwise.
*/
- public static boolean isBidirectional(int videoState) {
+ public static boolean isBidirectional(@VideoState int videoState) {
return hasState(videoState, VideoProfile.STATE_BIDIRECTIONAL);
}
@@ -293,7 +313,7 @@
* @param videoState The video state.
* @return {@code True} if the video is paused, {@code false} otherwise.
*/
- public static boolean isPaused(int videoState) {
+ public static boolean isPaused(@VideoState int videoState) {
return hasState(videoState, VideoProfile.STATE_PAUSED);
}
@@ -304,7 +324,7 @@
* @param state The state to check.
* @return {@code True} if the state is set.
*/
- private static boolean hasState(int videoState, int state) {
+ private static boolean hasState(@VideoState int videoState, @VideoState int state) {
return (videoState & state) == state;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6ffc026..10815b3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -473,6 +473,15 @@
public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool";
/**
+ * Determine whether user can switch Wi-Fi preferred or Cellular preferred in calling preference.
+ * Some operators support Wi-Fi Calling only, not VoLTE.
+ * They don't need "Cellular preferred" option.
+ * In this case, set uneditalbe attribute for preferred preference.
+ * @hide
+ */
+ public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool";
+
+ /**
* If this is true, the SIM card (through Customer Service Profile EF file) will be able to
* prevent manual operator selection. If false, this SIM setting will be ignored and manual
* operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -486,6 +495,13 @@
*/
public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+ /**
+ * Boolean indicating if intent for emergency call state changes should be broadcast
+ * @hide
+ */
+ public static final String KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL =
+ "broadcast_emergency_call_state_changes_bool";
+
// These variables are used by the MMS service and exposed through another API, {@link
// SmsManager}. The variable names and string values are copied from there.
public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
@@ -553,6 +569,23 @@
public static final String BOOL_ALLOW_VIDEO_PAUSE =
"bool_allow_video_pause";
+
+ /**
+ * Flag indicating whether the carrier supports RCS presence indication for video calls. When
+ * {@code true}, the carrier supports RCS presence indication for video calls. When presence
+ * is supported, the device should use the
+ * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the
+ * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate
+ * whether each contact supports video calling. The UI is made aware that presence is enabled
+ * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE}
+ * and can choose to hide or show the video calling icon based on whether a contact supports
+ * video.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -612,6 +645,7 @@
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true);
+ sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
@@ -628,6 +662,7 @@
sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false);
sDefaults.putBoolean(BOOL_ALLOW_EMERGENCY_VIDEO_CALLS, false);
sDefaults.putBoolean(BOOL_ALLOW_VIDEO_PAUSE, true);
+ sDefaults.putBoolean(KEY_EDITABLE_WFC_MODE_BOOL, true);
// MMS defaults
sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
@@ -662,6 +697,7 @@
sDefaults.putString(KEY_MMS_UA_PROF_URL_STRING, "");
sDefaults.putString(KEY_MMS_USER_AGENT_STRING, "");
sDefaults.putBoolean(KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL, true);
+ sDefaults.putBoolean(KEY_USE_RCS_PRESENCE_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 9998937..39f3213 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -24,6 +24,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.Uri;
+import android.os.Looper;
import android.telephony.Rlog;
import android.os.Handler;
import android.os.Message;
@@ -347,7 +348,31 @@
* for #onSubscriptionsChanged to be invoked.
*/
public static class OnSubscriptionsChangedListener {
- private final Handler mHandler = new Handler() {
+ private final Handler mHandler;
+
+ public OnSubscriptionsChangedListener() {
+ mHandler = new OnSubscriptionsChangedListenerHandler();
+ }
+
+ /**
+ * Contructor that takes in looper as parameter in case a subclass/instantiation needs
+ * to use a specific looper (like in tests where mainLooper may need to be used).
+ * @param looper Looper to be used for mHandler
+ * @hide
+ */
+ protected OnSubscriptionsChangedListener(Looper looper) {
+ mHandler = new OnSubscriptionsChangedListenerHandler(looper);
+ }
+
+ private class OnSubscriptionsChangedListenerHandler extends Handler {
+ private OnSubscriptionsChangedListenerHandler() {
+ super();
+ }
+
+ private OnSubscriptionsChangedListenerHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
if (DBG) {
@@ -355,7 +380,7 @@
}
OnSubscriptionsChangedListener.this.onSubscriptionsChanged();
}
- };
+ }
/**
* Callback invoked when there is any change to any SubscriptionInfo. Typically
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 59a8a67..b9d7297 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -53,6 +53,9 @@
private static final String TAG = "CallerInfo";
private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
+ public static final long USER_TYPE_CURRENT = 0;
+ public static final long USER_TYPE_WORK = 1;
+
/**
* Please note that, any one of these member variables can be null,
* and any accesses to them should be prepared to handle such a case.
@@ -103,6 +106,8 @@
public Uri contactRefUri;
public String lookupKey;
+ public long userType;
+
/**
* Contact display photo URI. If a contact has no display photo but a thumbnail, it'll be
* the thumbnail URI instead.
@@ -154,6 +159,7 @@
// TODO: Move all the basic initialization here?
mIsEmergency = false;
mIsVoiceMail = false;
+ userType = USER_TYPE_CURRENT;
}
/**
@@ -173,6 +179,7 @@
info.cachedPhoto = null;
info.isCachedPhotoCurrent = false;
info.contactExists = false;
+ info.userType = USER_TYPE_CURRENT;
if (VDBG) Rlog.v(TAG, "getCallerInfo() based on cursor...");
@@ -225,6 +232,9 @@
Rlog.v(TAG, "==> got info.contactIdOrZero: " + info.contactIdOrZero);
}
}
+ if (Contacts.isEnterpriseContactId(contactId)) {
+ info.userType = USER_TYPE_WORK;
+ }
} else {
// No valid columnIndex, so we can't look up person_id.
Rlog.w(TAG, "Couldn't find contact_id column for " + contactRef);
@@ -628,6 +638,7 @@
/**
* @return a string debug representation of this instance.
*/
+ @Override
public String toString() {
// Warning: never check in this file with VERBOSE_DEBUG = true
// because that will result in PII in the system log.
@@ -658,6 +669,7 @@
.append("\nemergency: " + mIsEmergency)
.append("\nvoicemail " + mIsVoiceMail)
.append("\ncontactExists " + contactExists)
+ .append("\nuserType " + userType)
.append(" }")
.toString();
} else {
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl b/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl
new file mode 100644
index 0000000..5dffa28
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ITelephonyDebug.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.internal.telephony;
+
+import android.os.Bundle;
+
+
+/**
+ * Interface used to interact with the Telephony debug service.
+ *
+ * {@hide}
+ */
+interface ITelephonyDebug {
+
+ /**
+ * Write telephony event
+ * @param timestamp returned by System.currentTimeMillis()
+ * @param phoneId for which event is written
+ * @param tag constant defined in TelephonyEventLog
+ * @param param1 optional
+ * @param param2 optional
+ * @param data optional
+ */
+ void writeEvent(long timestamp, int phoneId, int tag, int param1, int param2, in Bundle data);
+}
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index a183de5..ecd89ed 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -35,17 +35,17 @@
IDLE, RINGING, OFFHOOK;
};
- /**
- * The state of a data connection.
- * <ul>
- * <li>CONNECTED = IP traffic should be available</li>
- * <li>CONNECTING = Currently setting up data connection</li>
- * <li>DISCONNECTED = IP not available</li>
- * <li>SUSPENDED = connection is created but IP traffic is
- * temperately not available. i.e. voice call is in place
- * in 2G network</li>
- * </ul>
- */
+ /**
+ * The state of a data connection.
+ * <ul>
+ * <li>CONNECTED = IP traffic should be available</li>
+ * <li>CONNECTING = Currently setting up data connection</li>
+ * <li>DISCONNECTED = IP not available</li>
+ * <li>SUSPENDED = connection is created but IP traffic is
+ * temperately not available. i.e. voice call is in place
+ * in 2G network</li>
+ * </ul>
+ */
public enum DataState {
CONNECTED, CONNECTING, DISCONNECTED, SUSPENDED;
};
@@ -89,6 +89,7 @@
public static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
public static final String DATA_NETWORK_ROAMING_KEY = "networkRoaming";
public static final String PHONE_IN_ECM_STATE = "phoneinECMState";
+ public static final String PHONE_IN_EMERGENCY_CALL = "phoneInEmergencyCall";
public static final String REASON_LINK_PROPERTIES_CHANGED = "linkPropertiesChanged";
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index d2e4de3..c70f8cf 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -75,6 +75,7 @@
*/
public static final String ACTION_RADIO_TECHNOLOGY_CHANGED
= "android.intent.action.RADIO_TECHNOLOGY";
+
/**
* <p>Broadcast Action: The emergency callback mode is changed.
* <ul>
@@ -94,6 +95,28 @@
*/
public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED
= "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
+
+ /**
+ * <p>Broadcast Action: The emergency call state is changed.
+ * <ul>
+ * <li><em>phoneInEmergencyCall</em> - A boolean value, true if phone in emergency call,
+ * false otherwise</li>
+ * </ul>
+ * <p class="note">
+ * You can <em>not</em> receive this through components declared
+ * in manifests, only by explicitly registering for it with
+ * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
+ * android.content.IntentFilter) Context.registerReceiver()}.
+ *
+ * <p class="note">
+ * Requires no permission.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ */
+ public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED
+ = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
+
/**
* Broadcast Action: The phone's signal strength has changed. The intent will have the
* following extra values:</p>
diff --git a/tests/FeatureSplit/base/AndroidManifest.xml b/tests/FeatureSplit/base/AndroidManifest.xml
index 989e802..e82b3b9 100644
--- a/tests/FeatureSplit/base/AndroidManifest.xml
+++ b/tests/FeatureSplit/base/AndroidManifest.xml
@@ -16,7 +16,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.split.feature">
- <application android:label="@string/app_title"
- android:hasCode="false">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+ <application android:label="@string/app_title">
+ <activity android:name=".ActivityMain" android:label="Feature Base">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/FeatureSplit/base/res/layout/main.xml b/tests/FeatureSplit/base/res/layout/main.xml
new file mode 100644
index 0000000..f01b920
--- /dev/null
+++ b/tests/FeatureSplit/base/res/layout/main.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="16dp">
+ <TextView android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:textAppearance="?android:textAppearanceLarge" />
+</RelativeLayout>
diff --git a/tests/FeatureSplit/base/res/values/values.xml b/tests/FeatureSplit/base/res/values/values.xml
index 564d301..854a8bb 100644
--- a/tests/FeatureSplit/base/res/values/values.xml
+++ b/tests/FeatureSplit/base/res/values/values.xml
@@ -16,6 +16,7 @@
<resources>
<string name="app_title">FeatureSplit APK</string>
+ <string name="base">Base</string>
<item type="id" name="test_id"/>
<integer name="test_integer">100</integer>
diff --git a/tests/FeatureSplit/base/src/com/android/test/split/feature/ActivityMain.java b/tests/FeatureSplit/base/src/com/android/test/split/feature/ActivityMain.java
new file mode 100644
index 0000000..6cca7c3
--- /dev/null
+++ b/tests/FeatureSplit/base/src/com/android/test/split/feature/ActivityMain.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.split.feature;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class ActivityMain extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ ((TextView) findViewById(R.id.text)).setText(R.string.base);
+ }
+}
+
diff --git a/tests/FeatureSplit/feature1/Android.mk b/tests/FeatureSplit/feature1/Android.mk
index adfb575..aa222dd 100644
--- a/tests/FeatureSplit/feature1/Android.mk
+++ b/tests/FeatureSplit/feature1/Android.mk
@@ -22,10 +22,12 @@
LOCAL_MODULE_TAGS := tests
featureOf := FeatureSplitBase
+
+LOCAL_APK_LIBRARIES := $(featureOf)
featureOfApk := $(call intermediates-dir-for,APPS,$(featureOf))/package.apk
localRStamp := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)/src/R.stamp
$(localRStamp): $(featureOfApk)
-LOCAL_AAPT_FLAGS := --feature-of $(featureOfApk)
+LOCAL_AAPT_FLAGS := --feature-of $(featureOfApk) --custom-package com.android.test.split.feature.one
include $(BUILD_PACKAGE)
diff --git a/tests/FeatureSplit/feature1/AndroidManifest.xml b/tests/FeatureSplit/feature1/AndroidManifest.xml
index 2aadc6d..42619b6 100644
--- a/tests/FeatureSplit/feature1/AndroidManifest.xml
+++ b/tests/FeatureSplit/feature1/AndroidManifest.xml
@@ -17,5 +17,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.split.feature"
featureName="feature1">
- <application android:hasCode="false" />
+
+ <uses-sdk android:minSdkVersion="21" />
+
+ <application>
+ <activity android:name=".one.One" android:label="Feature One">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
</manifest>
diff --git a/tests/FeatureSplit/feature1/res/values/values.xml b/tests/FeatureSplit/feature1/res/values/values.xml
index 70eb56a..10dbd97 100644
--- a/tests/FeatureSplit/feature1/res/values/values.xml
+++ b/tests/FeatureSplit/feature1/res/values/values.xml
@@ -15,6 +15,7 @@
-->
<resources>
+ <string name="feature_string">Feature1</string>
<item type="id" name="test_id2"/>
<integer name="test_integer2">200</integer>
<color name="test_color2">#00ff00</color>
diff --git a/tests/FeatureSplit/feature1/src/com/android/test/split/feature/one/One.java b/tests/FeatureSplit/feature1/src/com/android/test/split/feature/one/One.java
new file mode 100644
index 0000000..def1339
--- /dev/null
+++ b/tests/FeatureSplit/feature1/src/com/android/test/split/feature/one/One.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.split.feature.one;
+
+import com.android.test.split.feature.ActivityMain;
+
+import android.widget.TextView;
+import android.os.Bundle;
+
+public class One extends ActivityMain {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ((TextView) findViewById(com.android.test.split.feature.R.id.text))
+ .setText(R.string.feature_string);
+ }
+}
diff --git a/tests/FeatureSplit/feature2/Android.mk b/tests/FeatureSplit/feature2/Android.mk
index e69cbe8..1a0322b 100644
--- a/tests/FeatureSplit/feature2/Android.mk
+++ b/tests/FeatureSplit/feature2/Android.mk
@@ -24,6 +24,8 @@
featureOf := FeatureSplitBase
featureAfter := FeatureSplit1
+LOCAL_APK_LIBRARIES := $(featureOf)
+
featureOfApk := $(call intermediates-dir-for,APPS,$(featureOf))/package.apk
featureAfterApk := $(call intermediates-dir-for,APPS,$(featureAfter))/package.apk
localRStamp := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)/src/R.stamp
@@ -31,5 +33,6 @@
LOCAL_AAPT_FLAGS := --feature-of $(featureOfApk)
LOCAL_AAPT_FLAGS += --feature-after $(featureAfterApk)
+LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.two
include $(BUILD_PACKAGE)
diff --git a/tests/FeatureSplit/feature2/AndroidManifest.xml b/tests/FeatureSplit/feature2/AndroidManifest.xml
index d139900..b50044a 100644
--- a/tests/FeatureSplit/feature2/AndroidManifest.xml
+++ b/tests/FeatureSplit/feature2/AndroidManifest.xml
@@ -17,5 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.split.feature"
featureName="feature2">
+
+ <uses-sdk android:minSdkVersion="21" />
+
<application android:hasCode="false"/>
</manifest>
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index 64353de..13f8b3b 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -30,6 +30,11 @@
static const char* kWildcardName = "any";
+const ConfigDescription& ConfigDescription::defaultConfig() {
+ static ConfigDescription config = {};
+ return config;
+}
+
static bool parseMcc(const char* name, ResTable_config* out) {
if (strcmp(name, kWildcardName) == 0) {
if (out) out->mcc = 0;
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 4af089d..5749816 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -29,6 +29,11 @@
* initialization and comparison methods.
*/
struct ConfigDescription : public android::ResTable_config {
+ /**
+ * Returns an immutable default config.
+ */
+ static const ConfigDescription& defaultConfig();
+
/*
* Parse a string of the form 'fr-sw600dp-land' and fill in the
* given ResTable_config with resulting configuration parameters.
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 5fce2c1..b4e75f9 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -210,4 +210,19 @@
std::cout << "}" << std::endl;
}
+void Debug::dumpHex(const void* data, size_t len) {
+ const uint8_t* d = (const uint8_t*) data;
+ for (size_t i = 0; i < len; i++) {
+ std::cerr << std::hex << std::setfill('0') << std::setw(2) << (uint32_t) d[i] << " ";
+ if (i % 8 == 7) {
+ std::cerr << "\n";
+ }
+ }
+
+ if (len - 1 % 8 != 7) {
+ std::cerr << std::endl;
+ }
+}
+
+
} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 5b0d7d6..ba05be9 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -20,12 +20,16 @@
#include "Resource.h"
#include "ResourceTable.h"
+// Include for printf-like debugging.
+#include <iostream>
+
namespace aapt {
struct Debug {
static void printTable(ResourceTable* table);
static void printStyleGraph(ResourceTable* table,
const ResourceName& targetStyle);
+ static void dumpHex(const void* data, size_t len);
};
} // namespace aapt
diff --git a/tools/aapt2/Flags.cpp b/tools/aapt2/Flags.cpp
index 9435396..666e8a8e 100644
--- a/tools/aapt2/Flags.cpp
+++ b/tools/aapt2/Flags.cpp
@@ -81,6 +81,8 @@
}
void Flags::usage(const StringPiece& command, std::ostream* out) {
+ constexpr size_t kWidth = 50;
+
*out << command << " [options]";
for (const Flag& flag : mFlags) {
if (flag.required) {
@@ -100,11 +102,11 @@
// the first line) followed by the description line. This will make sure that multiline
// descriptions are still right justified and aligned.
for (StringPiece line : util::tokenize<char>(flag.description, '\n')) {
- *out << " " << std::setw(30) << std::left << argLine << line << "\n";
+ *out << " " << std::setw(kWidth) << std::left << argLine << line << "\n";
argLine = " ";
}
}
- *out << " " << std::setw(30) << std::left << "-h" << "Displays this help menu\n";
+ *out << " " << std::setw(kWidth) << std::left << "-h" << "Displays this help menu\n";
out->flush();
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 5e7d3ec..b37d366 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -70,6 +70,7 @@
*/
struct ParsedResource {
ResourceName name;
+ ConfigDescription config;
Source source;
ResourceId id;
Maybe<SymbolState> symbolState;
@@ -108,8 +109,7 @@
}
// Recursively adds resources to the ResourceTable.
-static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& config,
- IDiagnostics* diag, ParsedResource* res) {
+static bool addResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) {
if (res->symbolState) {
Symbol symbol;
symbol.state = res->symbolState.value();
@@ -125,14 +125,14 @@
res->value->setComment(std::move(res->comment));
res->value->setSource(std::move(res->source));
- if (!table->addResource(res->name, res->id, config, std::move(res->value), diag)) {
+ if (!table->addResource(res->name, res->id, res->config, std::move(res->value), diag)) {
return false;
}
}
bool error = false;
for (ParsedResource& child : res->childResources) {
- error |= !addResourcesToTable(table, config, diag, &child);
+ error |= !addResourcesToTable(table, diag, &child);
}
return !error;
}
@@ -290,6 +290,7 @@
}
ParsedResource parsedResource;
+ parsedResource.config = mConfig;
parsedResource.source = mSource.withLine(parser->getLineNumber());
parsedResource.comment = std::move(comment);
@@ -310,7 +311,7 @@
// Record that we stripped out this resource name.
// We will check that at least one variant of this resource was included.
strippedResources.insert(parsedResource.name);
- } else if (!addResourcesToTable(mTable, mConfig, mDiag, &parsedResource)) {
+ } else if (!addResourcesToTable(mTable, mDiag, &parsedResource)) {
error = true;
}
}
@@ -769,6 +770,13 @@
bool weak) {
outResource->name.type = ResourceType::kAttr;
+ // Attributes only end up in default configuration.
+ if (outResource->config != ConfigDescription::defaultConfig()) {
+ mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
+ << outResource->config << "' for attribute " << outResource->name);
+ outResource->config = ConfigDescription::defaultConfig();
+ }
+
uint32_t typeMask = 0;
Maybe<StringPiece16> maybeFormat = xml::findAttribute(parser, u"format");
@@ -940,8 +948,7 @@
}
return Attribute::Symbol{
- Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())),
- val.data };
+ Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())), val.data };
}
static Maybe<Reference> parseXmlAttributeName(StringPiece16 str) {
@@ -1190,12 +1197,21 @@
return true;
}
-bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseDeclareStyleable(xml::XmlPullParser* parser,
+ ParsedResource* outResource) {
outResource->name.type = ResourceType::kStyleable;
// Declare-styleable is kPrivate by default, because it technically only exists in R.java.
outResource->symbolState = SymbolState::kPublic;
+ // Declare-styleable only ends up in default config;
+ if (outResource->config != ConfigDescription::defaultConfig()) {
+ mDiag->warn(DiagMessage(outResource->source) << "ignoring configuration '"
+ << outResource->config << "' for styleable "
+ << outResource->name.entry);
+ outResource->config = ConfigDescription::defaultConfig();
+ }
+
std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
std::u16string comment;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 8d10ba1..cf0fcd1 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -47,13 +47,26 @@
mContext = test::ContextBuilder().build();
}
+ ::testing::AssertionResult testParse(const StringPiece& str) {
+ return testParse(str, ConfigDescription{}, {});
+ }
+
+ ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config) {
+ return testParse(str, config, {});
+ }
+
::testing::AssertionResult testParse(const StringPiece& str,
- std::initializer_list<std::u16string> products = {}) {
+ std::initializer_list<std::u16string> products) {
+ return testParse(str, {}, std::move(products));
+ }
+
+ ::testing::AssertionResult testParse(const StringPiece& str, const ConfigDescription& config,
+ std::initializer_list<std::u16string> products) {
std::stringstream input(kXmlPreamble);
input << "<resources>\n" << str << "\n</resources>" << std::endl;
ResourceParserOptions parserOptions;
parserOptions.products = products;
- ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {},
+ ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, config,
parserOptions);
xml::XmlPullParser xmlParser(input);
if (parser.parse(&xmlParser)) {
@@ -138,6 +151,26 @@
EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
}
+// Old AAPT allowed attributes to be defined under different configurations, but ultimately
+// stored them with the default configuration. Check that we have the same behavior.
+TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
+ const ConfigDescription watchConfig = test::parseConfigOrDie("watch");
+ std::string input = R"EOF(
+ <attr name="foo" />
+ <declare-styleable name="bar">
+ <attr name="baz" />
+ </declare-styleable>)EOF";
+ ASSERT_TRUE(testParse(input, watchConfig));
+
+ EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/foo", watchConfig));
+ EXPECT_EQ(nullptr, test::getValueForConfig<Attribute>(&mTable, u"@attr/baz", watchConfig));
+ EXPECT_EQ(nullptr, test::getValueForConfig<Styleable>(&mTable, u"@styleable/bar", watchConfig));
+
+ EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/foo"));
+ EXPECT_NE(nullptr, test::getValue<Attribute>(&mTable, u"@attr/baz"));
+ EXPECT_NE(nullptr, test::getValue<Styleable>(&mTable, u"@styleable/bar"));
+}
+
TEST_F(ResourceParserTest, ParseAttrWithMinMax) {
std::string input = "<attr name=\"foo\" min=\"10\" max=\"23\" format=\"integer\"/>";
ASSERT_TRUE(testParse(input));
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 1dc123e..07f62af 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -176,7 +176,7 @@
/*
* Style parent's are a bit different. We accept the following formats:
*
- * @[[*]package:]style/<entry>
+ * @[[*]package:][style/]<entry>
* ?[[*]package:]style/<entry>
* <[*]package>:[style/]<entry>
* [[*]package:style/]<entry>
@@ -216,14 +216,6 @@
*outError = err.str();
return {};
}
- } else {
- // No type was defined, this should not have a leading identifier.
- if (hasLeadingIdentifiers) {
- std::stringstream err;
- err << "invalid parent reference '" << str << "'";
- *outError = err.str();
- return {};
- }
}
if (!hasLeadingIdentifiers && ref.package.empty() && !typeStr.empty()) {
@@ -294,6 +286,12 @@
const StringPiece16& str) {
android::Res_value flags = { };
flags.dataType = android::Res_value::TYPE_INT_DEC;
+ flags.data = 0u;
+
+ if (util::trimWhitespace(str).empty()) {
+ // Empty string is a valid flag (0).
+ return util::make_unique<BinaryPrimitive>(flags);
+ }
for (StringPiece16 part : util::tokenize(str, u'|')) {
StringPiece16 trimmedPart = util::trimWhitespace(part);
@@ -386,12 +384,12 @@
bool tryParseBool(const StringPiece16& str, bool* outValue) {
StringPiece16 trimmedStr(util::trimWhitespace(str));
- if (trimmedStr == u"true" || trimmedStr == u"TRUE") {
+ if (trimmedStr == u"true" || trimmedStr == u"TRUE" || trimmedStr == u"True") {
if (outValue) {
*outValue = true;
}
return true;
- } else if (trimmedStr == u"false" || trimmedStr == u"FALSE") {
+ } else if (trimmedStr == u"false" || trimmedStr == u"FALSE" || trimmedStr == u"False") {
if (outValue) {
*outValue = false;
}
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index 88efa67..c9f93e1 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -16,12 +16,34 @@
#include "Resource.h"
#include "ResourceUtils.h"
+#include "test/Builders.h"
#include "test/Common.h"
#include <gtest/gtest.h>
namespace aapt {
+TEST(ResourceUtilsTest, ParseBool) {
+ bool val = false;
+ EXPECT_TRUE(ResourceUtils::tryParseBool(u"true", &val));
+ EXPECT_TRUE(val);
+
+ EXPECT_TRUE(ResourceUtils::tryParseBool(u"TRUE", &val));
+ EXPECT_TRUE(val);
+
+ EXPECT_TRUE(ResourceUtils::tryParseBool(u"True", &val));
+ EXPECT_TRUE(val);
+
+ EXPECT_TRUE(ResourceUtils::tryParseBool(u"false", &val));
+ EXPECT_FALSE(val);
+
+ EXPECT_TRUE(ResourceUtils::tryParseBool(u"FALSE", &val));
+ EXPECT_FALSE(val);
+
+ EXPECT_TRUE(ResourceUtils::tryParseBool(u"False", &val));
+ EXPECT_FALSE(val);
+}
+
TEST(ResourceUtilsTest, ParseResourceName) {
ResourceNameRef actual;
bool actualPriv = false;
@@ -154,6 +176,10 @@
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+ ref = ResourceUtils::parseStyleParentReference(u"@android:foo", &errStr);
+ AAPT_ASSERT_TRUE(ref);
+ EXPECT_EQ(ref.value().name.value(), kAndroidStyleFooName);
+
ref = ResourceUtils::parseStyleParentReference(u"foo", &errStr);
AAPT_ASSERT_TRUE(ref);
EXPECT_EQ(ref.value().name.value(), kStyleFooName);
@@ -164,4 +190,16 @@
EXPECT_TRUE(ref.value().privateReference);
}
+TEST(ResourceUtilsTest, ParseEmptyFlag) {
+ std::unique_ptr<Attribute> attr = test::AttributeBuilder(false)
+ .setTypeMask(android::ResTable_map::TYPE_FLAGS)
+ .addItem(u"one", 0x01)
+ .addItem(u"two", 0x02)
+ .build();
+
+ std::unique_ptr<BinaryPrimitive> result = ResourceUtils::tryParseFlagSymbol(attr.get(), u"");
+ ASSERT_NE(nullptr, result);
+ EXPECT_EQ(0u, result->value.data);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index 8552f47..aadb00b 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
+#include "StringPool.h"
#include "util/BigBuffer.h"
#include "util/StringPiece.h"
-#include "StringPool.h"
#include "util/Util.h"
#include <algorithm>
@@ -342,7 +342,14 @@
// Encode the actual UTF16 string length.
data = encodeLength(data, entry->value.size());
- strncpy16(data, entry->value.data(), entry->value.size());
+ const size_t byteLength = entry->value.size() * sizeof(char16_t);
+
+ // NOTE: For some reason, strncpy16(data, entry->value.data(), entry->value.size())
+ // truncates the string.
+ memcpy(data, entry->value.data(), byteLength);
+
+ // The null-terminating character is already here due to the block of data being set
+ // to 0s on allocation.
}
}
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index c722fbe..e93c2fba 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -180,6 +180,22 @@
ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
}
+TEST(StringPoolTest, FlattenOddCharactersUtf16) {
+ StringPool pool;
+ pool.makeRef(u"\u093f");
+ BigBuffer buffer(1024);
+ StringPool::flattenUtf16(&buffer, pool);
+
+ std::unique_ptr<uint8_t[]> data = util::copy(buffer);
+ android::ResStringPool test;
+ ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
+ size_t len = 0;
+ const char16_t* str = test.stringAt(0, &len);
+ EXPECT_EQ(1u, len);
+ EXPECT_EQ(u'\u093f', *str);
+ EXPECT_EQ(0u, str[1]);
+}
+
constexpr const char16_t* sLongString = u"バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。";
TEST(StringPoolTest, FlattenUtf8) {
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index c78670f..689ace6 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -117,7 +117,11 @@
if (!data.configStr.empty()) {
name << "-" << data.configStr;
}
- name << "_" << data.name << "." << data.extension << ".flat";
+ name << "_" << data.name;
+ if (!data.extension.empty()) {
+ name << "." << data.extension;
+ }
+ name << ".flat";
return name.str();
}
@@ -386,16 +390,26 @@
fileExportWriter.getChunkHeader()->size =
util::hostToDevice32(buffer.size() + f.value().getDataLength());
- if (writer->writeEntry(buffer)) {
- if (writer->writeEntry(f.value().getDataPtr(), f.value().getDataLength())) {
- if (writer->finishEntry()) {
- return true;
- }
+ if (!writer->writeEntry(buffer)) {
+ context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
+ return false;
+ }
+
+ // Only write if we have something to write. This is because mmap fails with length of 0,
+ // but we still want to compile the file to get the resource ID.
+ if (f.value().getDataPtr() && f.value().getDataLength() > 0) {
+ if (!writer->writeEntry(f.value().getDataPtr(), f.value().getDataLength())) {
+ context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
+ return false;
}
}
- context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
- return false;
+ if (!writer->finishEntry()) {
+ context->getDiagnostics()->error(DiagMessage(outputPath) << "failed to write");
+ return false;
+ }
+
+ return true;
}
class CompileContext : public IAaptContext {
diff --git a/tools/aapt2/io/Data.h b/tools/aapt2/io/Data.h
index 9081c55..467e604 100644
--- a/tools/aapt2/io/Data.h
+++ b/tools/aapt2/io/Data.h
@@ -79,6 +79,21 @@
size_t mSize;
};
+/**
+ * When mmap fails because the file has length 0, we use the EmptyData to simulate data of length 0.
+ */
+class EmptyData : public IData {
+public:
+ const void* data() const override {
+ static const uint8_t d = 0;
+ return &d;
+ }
+
+ size_t size() const override {
+ return 0u;
+ }
+};
+
} // namespace io
} // namespace aapt
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 76f87ae..e758d8a4 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -32,7 +32,10 @@
std::unique_ptr<IData> RegularFile::openAsData() {
android::FileMap map;
if (Maybe<android::FileMap> map = file::mmapPath(mSource.path, nullptr)) {
- return util::make_unique<MmappedData>(std::move(map.value()));
+ if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
+ return util::make_unique<MmappedData>(std::move(map.value()));
+ }
+ return util::make_unique<EmptyData>();
}
return {};
}
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 8a87d96..51b9cec 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -51,16 +51,19 @@
std::vector<std::string> includePaths;
std::vector<std::string> overlayFiles;
Maybe<std::string> generateJavaClassPath;
- std::set<std::string> extraJavaPackages;
+ Maybe<std::u16string> customJavaPackage;
+ std::set<std::u16string> extraJavaPackages;
Maybe<std::string> generateProguardRulesPath;
bool noAutoVersion = false;
bool staticLib = false;
bool verbose = false;
bool outputToDirectory = false;
bool autoAddOverlay = false;
+ bool doNotCompressAnything = false;
+ std::vector<std::string> extensionsToNotCompress;
Maybe<std::u16string> privateSymbols;
- Maybe<std::u16string> minSdkVersionDefault;
- Maybe<std::u16string> targetSdkVersionDefault;
+ ManifestFixerOptions manifestFixerOptions;
+
};
struct LinkContext : public IAaptContext {
@@ -186,7 +189,20 @@
return resFile;
}
- bool copyFileToArchive(io::IFile* file, const std::string& outPath, uint32_t flags,
+ uint32_t getCompressionFlags(const StringPiece& str) {
+ if (mOptions.doNotCompressAnything) {
+ return 0;
+ }
+
+ for (const std::string& extension : mOptions.extensionsToNotCompress) {
+ if (util::stringEndsWith<char>(str, extension)) {
+ return 0;
+ }
+ }
+ return ArchiveEntry::kCompress;
+ }
+
+ bool copyFileToArchive(io::IFile* file, const std::string& outPath,
IArchiveWriter* writer) {
std::unique_ptr<io::IData> data = file->openAsData();
if (!data) {
@@ -202,7 +218,7 @@
return false;
}
- if (writer->startEntry(outPath, flags)) {
+ if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
data->size() - static_cast<size_t>(offset))) {
if (writer->finishEntry()) {
@@ -319,7 +335,6 @@
return false;
}
-
if (writer->startEntry(path, ArchiveEntry::kCompress)) {
if (writer->writeEntry(buffer)) {
if (writer->finishEntry()) {
@@ -508,7 +523,10 @@
}
bool processFile(const std::string& path, bool override) {
- if (util::stringEndsWith<char>(path, ".flata")) {
+ 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);
}
@@ -520,7 +538,7 @@
const Source& src = file->getSource();
if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
return mergeResourceTable(file, override);
- } else {
+ } 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();
if (!data) {
@@ -533,7 +551,11 @@
if (resourceFile) {
return mergeCompiledFile(file, std::move(resourceFile), override);
}
+ } else {
+ // Ignore non .flat files. This could be classes.dex or something else that happens
+ // to be in an archive.
}
+
return false;
}
@@ -646,10 +668,7 @@
bool error = false;
{
- ManifestFixerOptions manifestFixerOptions;
- manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
- manifestFixerOptions.targetSdkVersionDefault = mOptions.targetSdkVersionDefault;
- ManifestFixer manifestFixer(manifestFixerOptions);
+ ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
if (!manifestFixer.consume(&mContext, manifestXml.get())) {
error = true;
}
@@ -786,7 +805,7 @@
mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
}
- if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath, 0,
+ if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
archiveWriter.get())) {
error = true;
}
@@ -813,12 +832,18 @@
if (mOptions.generateJavaClassPath) {
JavaClassGeneratorOptions options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+
if (mOptions.staticLib) {
options.useFinal = false;
}
- StringPiece16 actualPackage = mContext.getCompilationPackage();
+ const StringPiece16 actualPackage = mContext.getCompilationPackage();
StringPiece16 outputPackage = mContext.getCompilationPackage();
+ if (mOptions.customJavaPackage) {
+ // Override the output java package to the custom one.
+ outputPackage = mOptions.customJavaPackage.value();
+ }
if (mOptions.privateSymbols) {
// If we defined a private symbols package, we only emit Public symbols
@@ -826,7 +851,7 @@
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
- mContext.getCompilationPackage(), options)) {
+ outputPackage, options)) {
return 1;
}
@@ -838,9 +863,8 @@
return 1;
}
- for (const std::string& extraPackage : mOptions.extraJavaPackages) {
- if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage),
- options)) {
+ for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
+ if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
return 1;
}
}
@@ -877,13 +901,16 @@
LinkOptions options;
Maybe<std::string> privateSymbolsPackage;
Maybe<std::string> minSdkVersion, targetSdkVersion;
+ Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
+ Maybe<std::string> versionCode, versionName;
+ Maybe<std::string> customJavaPackage;
std::vector<std::string> extraJavaPackages;
Flags flags = Flags()
.requiredFlag("-o", "Output path", &options.outputPath)
.requiredFlag("--manifest", "Path to the Android manifest to build",
&options.manifestPath)
.optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths)
- .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics. "
+ .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
"The last conflicting resource given takes precedence.",
&options.overlayFiles)
.optionalFlag("--java", "Directory in which to generate R.java",
@@ -900,15 +927,29 @@
"AndroidManifest.xml", &minSdkVersion)
.optionalFlag("--target-sdk-version", "Default target SDK version to use for "
"AndroidManifest.xml", &targetSdkVersion)
+ .optionalFlag("--version-code", "Version code (integer) to inject into the "
+ "AndroidManifest.xml if none is present", &versionCode)
+ .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)
.optionalFlag("--private-symbols", "Package name to use when generating R.java for "
"private symbols.\n"
"If not specified, public and private symbols will use the application's "
"package name", &privateSymbolsPackage)
+ .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
+ &customJavaPackage)
.optionalFlagList("--extra-packages", "Generate the same R.java but with different "
"package names", &extraJavaPackages)
.optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
"overlays without <add-resource> tags", &options.autoAddOverlay)
+ .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
+ &renameManifestPackage)
+ .optionalFlag("--rename-instrumentation-target-package",
+ "Changes the name of the target package for instrumentation. Most useful "
+ "when used\nin conjunction with --rename-manifest-package",
+ &renameInstrumentationTargetPackage)
+ .optionalFlagList("-0", "File extensions not to compress",
+ &options.extensionsToNotCompress)
.optionalSwitch("-v", "Enables verbose logging", &options.verbose);
if (!flags.parse("aapt2 link", args, &std::cerr)) {
@@ -920,18 +961,42 @@
}
if (minSdkVersion) {
- options.minSdkVersionDefault = util::utf8ToUtf16(minSdkVersion.value());
+ options.manifestFixerOptions.minSdkVersionDefault =
+ util::utf8ToUtf16(minSdkVersion.value());
}
if (targetSdkVersion) {
- options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value());
+ options.manifestFixerOptions.targetSdkVersionDefault =
+ util::utf8ToUtf16(targetSdkVersion.value());
+ }
+
+ if (renameManifestPackage) {
+ options.manifestFixerOptions.renameManifestPackage =
+ util::utf8ToUtf16(renameManifestPackage.value());
+ }
+
+ if (renameInstrumentationTargetPackage) {
+ options.manifestFixerOptions.renameInstrumentationTargetPackage =
+ util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
+ }
+
+ if (versionCode) {
+ options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
+ }
+
+ if (versionName) {
+ options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
+ }
+
+ if (customJavaPackage) {
+ options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
}
// Populate the set of extra packages for which to generate R.java.
for (std::string& extraPackage : extraJavaPackages) {
// A given package can actually be a colon separated list of packages.
for (StringPiece package : util::split(extraPackage, ':')) {
- options.extraJavaPackages.insert(package.toString());
+ options.extraJavaPackages.insert(util::utf8ToUtf16(package));
}
}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 2034c57..9baf1d8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -22,25 +22,43 @@
namespace aapt {
static bool verifyManifest(IAaptContext* context, const Source& source, xml::Element* manifestEl) {
- bool error = false;
-
xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
if (!attr) {
context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
<< "missing 'package' attribute");
- error = true;
} else if (ResourceUtils::isReference(attr->value)) {
context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
<< "value for attribute 'package' must not be a "
"reference");
- error = true;
} else if (!util::isJavaPackageName(attr->value)) {
context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
<< "invalid package name '" << attr->value << "'");
- error = true;
+ } else {
+ return true;
+ }
+ return false;
+}
+
+static bool includeVersionName(IAaptContext* context, const Source& source,
+ const StringPiece16& versionName, xml::Element* manifestEl) {
+ if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName")) {
+ return true;
}
- return !error;
+ manifestEl->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid, u"versionName", versionName.toString() });
+ return true;
+}
+
+static bool includeVersionCode(IAaptContext* context, const Source& source,
+ const StringPiece16& versionCode, xml::Element* manifestEl) {
+ if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode")) {
+ return true;
+ }
+
+ manifestEl->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid, u"versionCode", versionCode.toString() });
+ return true;
}
static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element* el,
@@ -62,6 +80,76 @@
return true;
}
+class FullyQualifiedClassNameVisitor : public xml::Visitor {
+public:
+ using xml::Visitor::visit;
+
+ FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
+ }
+
+ void visit(xml::Element* el) override {
+ for (xml::Attribute& attr : el->attributes) {
+ if (Maybe<std::u16string> newValue =
+ util::getFullyQualifiedClassName(mPackage, attr.value)) {
+ attr.value = std::move(newValue.value());
+ }
+ }
+
+ // Super implementation to iterate over the children.
+ xml::Visitor::visit(el);
+ }
+
+private:
+ StringPiece16 mPackage;
+};
+
+static bool renameManifestPackage(IAaptContext* context, const Source& source,
+ const StringPiece16& packageOverride, xml::Element* manifestEl) {
+ if (!util::isJavaPackageName(packageOverride)) {
+ context->getDiagnostics()->error(DiagMessage() << "invalid manifest package override '"
+ << packageOverride << "'");
+ return false;
+ }
+
+ xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
+
+ // We've already verified that the manifest element is present, with a package name specified.
+ assert(attr);
+
+ std::u16string originalPackage = std::move(attr->value);
+ attr->value = packageOverride.toString();
+
+ FullyQualifiedClassNameVisitor visitor(originalPackage);
+ manifestEl->accept(&visitor);
+ return true;
+}
+
+static bool renameInstrumentationTargetPackage(IAaptContext* context, const Source& source,
+ const StringPiece16& packageOverride,
+ xml::Element* manifestEl) {
+ if (!util::isJavaPackageName(packageOverride)) {
+ context->getDiagnostics()->error(DiagMessage()
+ << "invalid instrumentation target package override '"
+ << packageOverride << "'");
+ return false;
+ }
+
+ xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
+ if (!instrumentationEl) {
+ // No error if there is no work to be done.
+ return true;
+ }
+
+ xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
+ if (!attr) {
+ // No error if there is no work to be done.
+ return true;
+ }
+
+ attr->value = packageOverride.toString();
+ return true;
+}
+
bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
xml::Element* root = xml::findRootElement(doc->root.get());
if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
@@ -74,6 +162,36 @@
return false;
}
+ if (mOptions.versionCodeDefault) {
+ if (!includeVersionCode(context, doc->file.source, mOptions.versionCodeDefault.value(),
+ root)) {
+ return false;
+ }
+ }
+
+ if (mOptions.versionNameDefault) {
+ if (!includeVersionName(context, doc->file.source, mOptions.versionNameDefault.value(),
+ root)) {
+ return false;
+ }
+ }
+
+ if (mOptions.renameManifestPackage) {
+ // Rename manifest package.
+ if (!renameManifestPackage(context, doc->file.source,
+ mOptions.renameManifestPackage.value(), root)) {
+ return false;
+ }
+ }
+
+ if (mOptions.renameInstrumentationTargetPackage) {
+ if (!renameInstrumentationTargetPackage(context, doc->file.source,
+ mOptions.renameInstrumentationTargetPackage.value(),
+ root)) {
+ return false;
+ }
+ }
+
bool foundUsesSdk = false;
for (xml::Element* el : root->getChildElements()) {
if (!el->namespaceUri.empty()) {
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index a77e6d5..b8d9c83 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -28,6 +28,10 @@
struct ManifestFixerOptions {
Maybe<std::u16string> minSdkVersionDefault;
Maybe<std::u16string> targetSdkVersionDefault;
+ Maybe<std::u16string> renameManifestPackage;
+ Maybe<std::u16string> renameInstrumentationTargetPackage;
+ Maybe<std::u16string> versionNameDefault;
+ Maybe<std::u16string> versionCodeDefault;
};
/**
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index f6bf895..f40fbfb 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -82,8 +82,6 @@
EXPECT_EQ(nullptr, verify("<manifest package=\"@string/str\" />"));
}
-
-
TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") };
@@ -97,7 +95,7 @@
xml::Element* el;
xml::Attribute* attr;
- el = xml::findRootElement(doc->root.get());
+ el = xml::findRootElement(doc.get());
ASSERT_NE(nullptr, el);
el = el->findChild({}, u"uses-sdk");
ASSERT_NE(nullptr, el);
@@ -115,7 +113,7 @@
</manifest>)EOF", options);
ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc->root.get());
+ el = xml::findRootElement(doc.get());
ASSERT_NE(nullptr, el);
el = el->findChild({}, u"uses-sdk");
ASSERT_NE(nullptr, el);
@@ -133,7 +131,7 @@
</manifest>)EOF", options);
ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc->root.get());
+ el = xml::findRootElement(doc.get());
ASSERT_NE(nullptr, el);
el = el->findChild({}, u"uses-sdk");
ASSERT_NE(nullptr, el);
@@ -149,7 +147,7 @@
package="android" />)EOF", options);
ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc->root.get());
+ el = xml::findRootElement(doc.get());
ASSERT_NE(nullptr, el);
el = el->findChild({}, u"uses-sdk");
ASSERT_NE(nullptr, el);
@@ -161,4 +159,98 @@
EXPECT_EQ(u"22", attr->value);
}
+TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
+ ManifestFixerOptions options;
+ options.renameManifestPackage = std::u16string(u"com.android");
+
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <application name=".MainApplication" text="hello">
+ <activity name=".activity.Start" />
+ <receiver name="com.google.android.Receiver" />
+ </application>
+ </manifest>)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ xml::Element* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
+
+ xml::Attribute* attr = nullptr;
+
+ attr = manifestEl->findAttribute({}, u"package");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"com.android"), attr->value);
+
+ xml::Element* applicationEl = manifestEl->findChild({}, u"application");
+ ASSERT_NE(nullptr, applicationEl);
+
+ attr = applicationEl->findAttribute({}, u"name");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"android.MainApplication"), attr->value);
+
+ attr = applicationEl->findAttribute({}, u"text");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"hello"), attr->value);
+
+ xml::Element* el;
+ el = applicationEl->findChild({}, u"activity");
+ ASSERT_NE(nullptr, el);
+
+ attr = el->findAttribute({}, u"name");
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ(std::u16string(u"android.activity.Start"), attr->value);
+
+ el = applicationEl->findChild({}, u"receiver");
+ ASSERT_NE(nullptr, el);
+
+ attr = el->findAttribute({}, u"name");
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ(std::u16string(u"com.google.android.Receiver"), attr->value);
+}
+
+TEST_F(ManifestFixerTest, RenameManifestInstrumentationPackageAndFullyQualifyTarget) {
+ ManifestFixerOptions options;
+ options.renameInstrumentationTargetPackage = std::u16string(u"com.android");
+
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <instrumentation android:targetPackage="android" />
+ </manifest>)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ xml::Element* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
+
+ xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
+ ASSERT_NE(nullptr, instrumentationEl);
+
+ xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"com.android"), attr->value);
+}
+
+TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
+ ManifestFixerOptions options;
+ options.versionNameDefault = std::u16string(u"Beta");
+ options.versionCodeDefault = std::u16string(u"0x10000000");
+
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android" />)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ xml::Element* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
+
+ xml::Attribute* attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"Beta"), attr->value);
+
+ attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"0x10000000"), attr->value);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index a81dc7b..04e8199 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -163,6 +163,11 @@
}
android::FileMap fileMap;
+ if (fileStats.st_size == 0) {
+ // mmap doesn't like a length of 0. Instead we return an empty FileMap.
+ return std::move(fileMap);
+ }
+
if (!fileMap.create(path.data(), fd, 0, fileStats.st_size, true)) {
if (outError) *outError = strerror(errno);
return {};
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 9ecc974..7b0c71d 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -175,10 +175,11 @@
return {};
}
- std::u16string result(package.data(), package.size());
if (className.data()[0] != u'.') {
- result += u'.';
+ return {};
}
+
+ std::u16string result(package.data(), package.size());
result.append(className.data(), className.size());
if (!isJavaClassName(result)) {
return {};
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index 9208e07..1e0c7fa 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -144,8 +144,7 @@
TEST(UtilTest, FullyQualifiedClassName) {
Maybe<std::u16string> res = util::getFullyQualifiedClassName(u"android", u"asdf");
- AAPT_ASSERT_TRUE(res);
- EXPECT_EQ(res.value(), u"android.asdf");
+ AAPT_ASSERT_FALSE(res);
res = util::getFullyQualifiedClassName(u"android", u".asdf");
AAPT_ASSERT_TRUE(res);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 01ee18b..9f0153a 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -19,6 +19,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import com.android.internal.app.IAssistScreenshotReceiver;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -550,4 +551,8 @@
public void setResizeDimLayer(boolean visible, int targetStackId, float alpha)
throws RemoteException {
}
+
+ @Override
+ public void requestAppKeyboardShortcuts(IResultReceiver receiver) throws RemoteException {
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/view/WindowCallback.java b/tools/layoutlib/bridge/src/android/view/WindowCallback.java
index d691c8e..411417c 100644
--- a/tools/layoutlib/bridge/src/android/view/WindowCallback.java
+++ b/tools/layoutlib/bridge/src/android/view/WindowCallback.java
@@ -16,10 +16,13 @@
package android.view;
+import android.annotation.Nullable;
import android.view.ActionMode.Callback;
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
+import java.util.List;
+
/**
* An empty implementation of {@link Window.Callback} that always returns null/false.
*/
@@ -138,4 +141,9 @@
public void onActionModeFinished(ActionMode mode) {
}
+
+ @Override
+ public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, @Nullable Menu menu) {
+
+ }
}
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 63411b0..e6fb620 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
@@ -197,7 +197,12 @@
mRenderResources = renderResources;
mConfig = config;
- mAssets = new BridgeAssetManager();
+ AssetManager systemAssetManager = AssetManager.getSystem();
+ if (systemAssetManager instanceof BridgeAssetManager) {
+ mAssets = (BridgeAssetManager) systemAssetManager;
+ } else {
+ throw new AssertionError("Creating BridgeContext without initializing Bridge");
+ }
mAssets.setAssetRepository(assets);
mApplicationInfo = new ApplicationInfo();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index 3662573..48012db 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -16,6 +16,8 @@
package com.android.layoutlib.bridge.android;
+import com.android.internal.os.IResultReceiver;
+
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
@@ -95,6 +97,10 @@
}
@Override
+ public void requestAppKeyboardShortcuts(IResultReceiver receiver) throws RemoteException {
+ }
+
+ @Override
public IBinder asBinder() {
// pass for now.
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
index 7e5ae8d..d417eb7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
@@ -63,4 +63,9 @@
public void removeViewImmediate(View arg0) {
// pass
}
+
+ @Override
+ public void requestAppKeyboardShortcuts(
+ KeyboardShortcutsReceiver receiver) {
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
index 9aab340..8c60bae 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
@@ -114,7 +114,7 @@
if (value != null) {
if (value.getClass() != ViewAttribute.IS_CHECKED.getAttributeClass()) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
- "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s",
+ "Wrong Adapter Item value class for IS_CHECKED. Expected Boolean, got %s",
value.getClass().getName()), null);
} else {
cb.setChecked((Boolean) value);
@@ -134,7 +134,7 @@
if (value != null) {
if (value.getClass() != ViewAttribute.SRC.getAttributeClass()) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN, String.format(
- "Wrong Adapter Item value class for TEXT. Expected Boolean, got %s",
+ "Wrong Adapter Item value class for SRC. Expected Boolean, got %s",
value.getClass().getName()), null);
} else {
// FIXME
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index dea86bf..b1b3759 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -380,10 +380,11 @@
}
/**
- * Create a new rendering session and test that rendering given layout on nexus 5
- * doesn't throw any exceptions and matches the provided image.
- * <p/>If frameTimeNanos is >= 0 a frame will be executed during the rendering. The time
- * indicates how far in the future is.
+ * Create a new rendering session and test that rendering the given layout doesn't throw any
+ * exceptions and matches the provided image.
+ * <p>
+ * If frameTimeNanos is >= 0 a frame will be executed during the rendering. The time indicates
+ * how far in the future is.
*/
private void renderAndVerify(SessionParams params, String goldenFileName, long frameTimeNanos)
throws ClassNotFoundException {
@@ -414,8 +415,8 @@
}
/**
- * Create a new rendering session and test that rendering given layout on nexus 5
- * doesn't throw any exceptions and matches the provided image.
+ * Create a new rendering session and test that rendering the given layout doesn't throw any
+ * exceptions and matches the provided image.
*/
private void renderAndVerify(SessionParams params, String goldenFileName)
throws ClassNotFoundException {
@@ -423,7 +424,7 @@
}
/**
- * Create a new rendering session and test that rendering given layout on nexus 5
+ * Create a new rendering session and test that rendering the given layout on nexus 5
* doesn't throw any exceptions and matches the provided image.
*/
private void renderAndVerify(String layoutFileName, String goldenFileName)
@@ -432,7 +433,7 @@
}
/**
- * Create a new rendering session and test that rendering given layout on given device
+ * Create a new rendering session and test that rendering the given layout on given device
* doesn't throw any exceptions and matches the provided image.
*/
private void renderAndVerify(String layoutFileName, String goldenFileName,
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index dc329e2..0c06ae8 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -24,15 +24,12 @@
import android.net.StaticIpConfiguration;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.Log;
-import java.util.HashMap;
+import java.util.Arrays;
import java.util.BitSet;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
+import java.util.HashMap;
/**
* A class representing a configured Wi-Fi network, including the
@@ -182,19 +179,6 @@
}
/** @hide */
- public static final int DISABLED_UNKNOWN_REASON = 0;
- /** @hide */
- public static final int DISABLED_DNS_FAILURE = 1;
- /** @hide */
- public static final int DISABLED_DHCP_FAILURE = 2;
- /** @hide */
- public static final int DISABLED_AUTH_FAILURE = 3;
- /** @hide */
- public static final int DISABLED_ASSOCIATION_REJECT = 4;
- /** @hide */
- public static final int DISABLED_BY_WIFI_MANAGER = 5;
-
- /** @hide */
public static final int UNKNOWN_UID = -1;
/**
@@ -206,24 +190,12 @@
/**
* The current status of this network configuration entry.
+ * Fixme We need remove this field to use only Quality network selection status only
* @see Status
*/
public int status;
/**
- * The configuration needs to be written to networkHistory.txt
- * @hide
- */
- public boolean dirty;
-
- /**
- * The code referring to a reason for disabling the network
- * Valid when {@link #status} == Status.DISABLED
- * @hide
- */
- public int disableReason;
-
- /**
* The network's SSID. Can either be an ASCII string,
* which must be enclosed in double quotation marks
* (e.g., {@code "MyNetwork"}, or a string of
@@ -356,6 +328,13 @@
/**
* @hide
+ * This network configuration is visible to and usable by other users on the
+ * same device.
+ */
+ public boolean shared;
+
+ /**
+ * @hide
*/
private IpConfiguration mIpConfiguration;
@@ -421,12 +400,6 @@
/**
* @hide
- * Uid used by autoJoin
- */
- public String autoJoinBSSID;
-
- /**
- * @hide
* Status of user approval for connection
*/
public int userApproved = USER_UNSPECIFIED;
@@ -444,77 +417,6 @@
/** @hide **/
public static int INVALID_RSSI = -127;
- /** @hide **/
- public static int UNWANTED_BLACKLIST_SOFT_RSSI_24 = -80;
-
- /** @hide **/
- public static int UNWANTED_BLACKLIST_SOFT_RSSI_5 = -70;
-
- /** @hide **/
- public static int GOOD_RSSI_24 = -65;
-
- /** @hide **/
- public static int LOW_RSSI_24 = -77;
-
- /** @hide **/
- public static int BAD_RSSI_24 = -87;
-
- /** @hide **/
- public static int GOOD_RSSI_5 = -60;
-
- /** @hide **/
- public static int LOW_RSSI_5 = -72;
-
- /** @hide **/
- public static int BAD_RSSI_5 = -82;
-
- /** @hide **/
- public static int UNWANTED_BLACKLIST_SOFT_BUMP = 4;
-
- /** @hide **/
- public static int UNWANTED_BLACKLIST_HARD_BUMP = 8;
-
- /** @hide **/
- public static int UNBLACKLIST_THRESHOLD_24_SOFT = -77;
-
- /** @hide **/
- public static int UNBLACKLIST_THRESHOLD_24_HARD = -68;
-
- /** @hide **/
- public static int UNBLACKLIST_THRESHOLD_5_SOFT = -63;
-
- /** @hide **/
- public static int UNBLACKLIST_THRESHOLD_5_HARD = -56;
-
- /** @hide **/
- public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_24 = -80;
-
- /** @hide **/
- public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_5 = -70;
-
- /** @hide
- * 5GHz band is prefered low over 2.4 if the 5GHz RSSI is higher than this threshold */
- public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65;
-
- /** @hide
- * 5GHz band is penalized if the 5GHz RSSI is lower than this threshold **/
- public static int G_BAND_PREFERENCE_RSSI_THRESHOLD = -75;
-
- /** @hide
- * Boost given to RSSI on a home network for the purpose of calculating the score
- * This adds stickiness to home networks, as defined by:
- * - less than 4 known BSSIDs
- * - PSK only
- * - TODO: add a test to verify that all BSSIDs are behind same gateway
- ***/
- public static int HOME_NETWORK_RSSI_BOOST = 5;
-
- /** @hide
- * RSSI boost for configuration which use autoJoinUseAggressiveJoinAttemptThreshold
- * To be more aggressive when initially attempting to auto join
- */
- public static int MAX_INITIAL_AUTO_JOIN_RSSI_BOOST = 8;
-
/**
* @hide
* A summary of the RSSI and Band status for that configuration
@@ -600,38 +502,6 @@
visibility = status;
}
- /** @hide */
- public static final int AUTO_JOIN_ENABLED = 0;
- /**
- * if this is set, the WifiConfiguration cannot use linkages so as to bump
- * it's relative priority.
- * - status between and 128 indicate various level of blacklisting depending
- * on the severity or frequency of the connection error
- * - deleted status indicates that the user is deleting the configuration, and so
- * although it may have been self added we will not re-self-add it, ignore it,
- * not return it to applications, and not connect to it
- * */
-
- /** @hide
- * network was temporary disabled due to bad connection, most likely due
- * to weak RSSI */
- public static final int AUTO_JOIN_TEMPORARY_DISABLED = 1;
- /** @hide
- * network was temporary disabled due to bad connection, which cant be attributed
- * to weak RSSI */
- public static final int AUTO_JOIN_TEMPORARY_DISABLED_LINK_ERRORS = 32;
- /** @hide */
- public static final int AUTO_JOIN_TEMPORARY_DISABLED_AT_SUPPLICANT = 64;
- /** @hide */
- public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE = 128;
- /** @hide */
- public static final int AUTO_JOIN_DISABLED_NO_CREDENTIALS = 160;
- /** @hide */
- public static final int AUTO_JOIN_DISABLED_USER_ACTION = 161;
-
- /** @hide */
- public static final int AUTO_JOIN_DELETED = 200;
-
// States for the userApproved field
/**
* @hide
@@ -656,29 +526,6 @@
/**
* @hide
- */
- public int autoJoinStatus;
-
- /**
- * @hide
- * Number of connection failures
- */
- public int numConnectionFailures;
-
- /**
- * @hide
- * Number of IP config failures
- */
- public int numIpConfigFailures;
-
- /**
- * @hide
- * Number of Auth failures
- */
- public int numAuthFailures;
-
- /**
- * @hide
* Number of reports indicating no Internet Access
*/
public int numNoInternetAccessReports;
@@ -715,12 +562,6 @@
/**
* @hide
- * Last time we blacklisted the configuration
- */
- public long blackListTimestamp;
-
- /**
- * @hide
* Last time the system was connected to this configuration.
*/
public long lastConnected;
@@ -795,18 +636,6 @@
/**
* @hide
- * Indicate that we didn't auto-join because rssi was too low
- */
- public boolean autoJoinBailedDueToLowRssi;
-
- /**
- * @hide
- * AutoJoin even though RSSI is 10dB below threshold
- */
- public int autoJoinUseAggressiveJoinAttemptThreshold;
-
- /**
- * @hide
* Number of time the scorer overrode a the priority based choice, when comparing two
* WifiConfigurations, note that since comparing WifiConfiguration happens very often
* potentially at every scan, this number might become very large, even on an idle
@@ -892,6 +721,361 @@
*/
public HashMap<String, Integer> connectChoices;
+ /** @hide
+ * Boost given to RSSI on a home network for the purpose of calculating the score
+ * This adds stickiness to home networks, as defined by:
+ * - less than 4 known BSSIDs
+ * - PSK only
+ * - TODO: add a test to verify that all BSSIDs are behind same gateway
+ ***/
+ public static final int HOME_NETWORK_RSSI_BOOST = 5;
+
+ /**
+ * @hide
+ * This class is used to contain all the information and API used for quality network selection
+ */
+ public static class NetworkSelectionStatus {
+ /**
+ * Quality Network Selection Status enable, temporary disabled, permanently disabled
+ */
+ /**
+ * This network is allowed to join Quality Network Selection
+ */
+ public static final int NETWORK_SELECTION_ENABLED = 0;
+ /**
+ * network was temporary disabled. Can be re-enabled after a time period expire
+ */
+ public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1;
+ /**
+ * network was permanently disabled.
+ */
+ public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2;
+ /**
+ * Maximum Network selection status
+ */
+ public static final int NETWORK_SELECTION_STATUS_MAX = 3;
+
+ /**
+ * Quality network selection status String (for debug purpose). Use Quality network
+ * selection status value as index to extec the corresponding debug string
+ */
+ private static final String[] QUALITY_NETWORK_SELECTION_STATUS = {
+ "NETWORK_SELECTION_ENABLED", "NETWORK_SELECTION_TEMPORARY_DISABLED",
+ "NETWORK_SELECTION_PERMANENTLY_DISABLED"};
+
+ //Quality Network disabled reasons
+ /**
+ * Default value. Means not disabled
+ */
+ public static final int NETWORK_SELECTION_ENABLE = 0;
+ /**
+ * This network is disabled because higher layer (>2) network is bad
+ */
+ public static final int DISABLED_BAD_LINK = 1;
+ /**
+ * This network is disabled because multiple association rejects
+ */
+ public static final int DISABLED_ASSOCIATION_REJECTION = 2;
+ /**
+ * This network is disabled because multiple authentication failure
+ */
+ public static final int DISABLED_AUTHENTICATION_FAILURE = 3;
+ /**
+ * This network is disabled because multiple DHCP failure
+ */
+ public static final int DISABLED_DHCP_FAILURE = 4;
+ /**
+ * This network is disabled because of security network but no credentials
+ */
+ public static final int DISABLED_DNS_FAILURE = 5;
+ /**
+ * This network is disabled because EAP-TLS failure
+ */
+ public static final int DISABLED_TLS_VERSION_MISMATCH = 6;
+ /**
+ * This network is disabled due to WifiManager disable it explicitly
+ */
+ public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 7;
+ /**
+ * This network is disabled because no Internet connected and user do not want
+ */
+ public static final int DISABLED_NO_INTERNET = 8;
+ /**
+ * This network is disabled due to WifiManager disable it explicitly
+ */
+ public static final int DISABLED_BY_WIFI_MANAGER = 9;
+ /**
+ * This Maximum disable reason value
+ */
+ public static final int NETWORK_SELECTION_DISABLED_MAX = 10;
+
+ /**
+ * Quality network selection disable reason String (for debug purpose)
+ */
+ private static final String[] QUALITY_NETWORK_SELECTION_DISABLE_REASON = {
+ "NETWORK_SELECTION_ENABLE", "NETWORK_SELECTION_DISABLED_BAD_LINK",
+ "NETWORK_SELECTION_DISABLED_ASSOCIATION_REJECTION ",
+ "NETWORK_SELECTION_DISABLED_AUTHENTICATION_FAILURE",
+ "NETWORK_SELECTION_DISABLED_DHCP_FAILURE",
+ "NETWORK_SELECTION_DISABLED_DNS_FAILURE", "NETWORK_SELECTION_DISABLED_TLS_VERSION",
+ "NETWORK_SELECTION_DISABLED_AUTHENTICATION_NO_CREDENTIALS",
+ "NETWORK_SELECTION_DISABLED_BY_WIFI_MANAGER"};
+
+ /**
+ * Invalid time stamp for network selection disable
+ */
+ public static final long INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP = -1L;
+
+ // fields for QualityNetwork Selection
+ /**
+ * Network selection status, should be in one of three status: enable, temporaily disabled
+ * or permanently disabled
+ */
+ private int mStatus;
+
+ /**
+ * Reason for disable this network
+ */
+ private int mNetworkSelectionDisableReason;
+
+ /**
+ * Last time we temporarily disabled the configuration
+ */
+ private long mTemporarilyDisabledTimestamp = INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
+
+ /**
+ * counter for each Network selection disable reason
+ */
+ private int[] mNetworkSeclectionDisableCounter = new int[NETWORK_SELECTION_DISABLED_MAX];
+
+ /**
+ * return current Quality network selection status in String (for debug purpose)
+ */
+ public String getNetworkStatusString() {
+ return QUALITY_NETWORK_SELECTION_STATUS[mStatus];
+ }
+
+ private NetworkSelectionStatus() {};
+
+ /**
+ * @param reason specific error reason
+ * @return corresponding network disable reason String (for debug purpose)
+ */
+ public static String getNetworkDisableReasonString(int reason) {
+ if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ return QUALITY_NETWORK_SELECTION_DISABLE_REASON[reason];
+ } else {
+ return null;
+ }
+ }
+ /**
+ * @return current network disable reason in String (for debug purpose)
+ */
+ public String getNetworkDisableReasonString() {
+ return QUALITY_NETWORK_SELECTION_DISABLE_REASON[mNetworkSelectionDisableReason];
+ }
+
+ /**
+ * get current network network selection status
+ */
+ public int getNetworkSelectionStatus() {
+ return mStatus;
+ }
+ /**
+ * @return whether current network is enabled to join network selection
+ */
+ public boolean isNetworkEnabled() {
+ return mStatus == NETWORK_SELECTION_ENABLED;
+ }
+
+ /**
+ * @return whether current network is temporary disabled
+ */
+ public boolean isNetworkTemporaryDisabled() {
+ return mStatus == NETWORK_SELECTION_TEMPORARY_DISABLED;
+ }
+
+ /**
+ * return whether current network is permanently disabled
+ */
+ public boolean isNetworkPermanentlyDisabled() {
+ return mStatus == NETWORK_SELECTION_PERMANENTLY_DISABLED;
+ }
+ /**
+ * @param status network selection status to set
+ */
+ public void setNetworkSelectionStatus(int status) {
+ if (status >= 0 && status < NETWORK_SELECTION_STATUS_MAX) {
+ mStatus = status;
+ }
+ }
+ /**
+ * @return current network's disable reason
+ */
+ public int getNetworkSelectionDisableReason() {
+ return mNetworkSelectionDisableReason;
+ }
+
+ /**
+ * @param reason Network disable reason
+ */
+ public void setNetworkSelectionDisableReason(int reason) {
+ if (reason >= 0 && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ mNetworkSelectionDisableReason = reason;
+ } else {
+ throw new IllegalArgumentException("Illegal reason value: " + reason);
+ }
+ }
+ /**
+ * @param reason whether current network is disabled by this reason
+ */
+ public boolean isDisabledByReason(int reason) {
+ return mNetworkSelectionDisableReason == reason;
+ }
+ /**
+ * @param timeStamp Set when current network is disabled in millisecond since January 1,
+ * 1970 00:00:00.0 UTC
+ */
+ public void setDisableTime(long timeStamp) {
+ mTemporarilyDisabledTimestamp = timeStamp;
+ }
+
+ /**
+ * @return Get when current network is disabled in millisecond since January 1,
+ * 1970 00:00:00.0 UTC
+ */
+ public long getDisableTime() {
+ return mTemporarilyDisabledTimestamp;
+ }
+
+ /**
+ * @param reason specific failure reason
+ * @exception throw IllegalArgumentException for illegal input
+ * @return counter number for specific error reason.
+ */
+ public int getDisableReasonCounter(int reason) {
+ if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ return mNetworkSeclectionDisableCounter[reason];
+ } else {
+ throw new IllegalArgumentException("Illegal reason value: " + reason);
+ }
+ }
+
+ /**
+ * set the counter of a specific failure reason
+ * @param reason reason for disable error
+ * @param value the counter value for this specific reason
+ * @exception throw IllegalArgumentException for illegal input
+ */
+ public void setDisableReasonCounter(int reason, int value) {
+ if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ mNetworkSeclectionDisableCounter[reason] = value;
+ } else {
+ throw new IllegalArgumentException("Illegal reason value: " + reason);
+ }
+ }
+
+ /**
+ * increment the counter of a specific failure reason
+ * @param reason a specific failure reason
+ * @exception throw IllegalArgumentException for illegal input
+ */
+ public void incrementDisableReasonCounter(int reason) {
+ if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ mNetworkSeclectionDisableCounter[reason]++;
+ } else {
+ throw new IllegalArgumentException("Illegal reason value: " + reason);
+ }
+ }
+ /**
+ * clear the counter of a specific failure reason
+ * @hide
+ * @param reason a specific failure reason
+ * @exception throw IllegalArgumentException for illegal input
+ */
+ public void clearDisableReasonCounter(int reason) {
+ if (reason >= NETWORK_SELECTION_ENABLE && reason < NETWORK_SELECTION_DISABLED_MAX) {
+ mNetworkSeclectionDisableCounter[reason] = NETWORK_SELECTION_ENABLE;
+ } else {
+ throw new IllegalArgumentException("Illegal reason value: " + reason);
+ }
+ }
+ /**
+ * clear all the failure reason counters
+ */
+ public void clearDisableReasonCounter() {
+ Arrays.fill(mNetworkSeclectionDisableCounter, NETWORK_SELECTION_ENABLE);
+ }
+
+ /**
+ * BSSID for connection to this network (through network selection procedure)
+ */
+ private String mNetworkSelectionBSSID;
+
+ /**
+ * get current network Selection BSSID
+ * @return current network Selection BSSID
+ */
+ public String getNetworkSelectionBSSID() {
+ return mNetworkSelectionBSSID;
+ }
+
+ /**
+ * set network Selection BSSID
+ * @param bssid The target BSSID for assocaition
+ */
+ public void setNetworkSelectionBSSID(String bssid) {
+ mNetworkSelectionBSSID = bssid;
+ }
+
+ public void copy(NetworkSelectionStatus source) {
+ mStatus = source.mStatus;
+ mNetworkSelectionDisableReason = source.mNetworkSelectionDisableReason;
+ for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
+ index++) {
+ mNetworkSeclectionDisableCounter[index] =
+ source.mNetworkSeclectionDisableCounter[index];
+ }
+ mTemporarilyDisabledTimestamp = source.mTemporarilyDisabledTimestamp;
+ mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
+ }
+
+ public void writeToParcel(Parcel dest) {
+ dest.writeInt(getNetworkSelectionStatus());
+ dest.writeInt(getNetworkSelectionDisableReason());
+ for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
+ index++) {
+ dest.writeInt(getDisableReasonCounter(index));
+ }
+ dest.writeLong(getDisableTime());
+ dest.writeString(getNetworkSelectionBSSID());
+ }
+
+ public void readFromParcel(Parcel in) {
+ setNetworkSelectionStatus(in.readInt());
+ setNetworkSelectionDisableReason(in.readInt());
+ for (int index = NETWORK_SELECTION_ENABLE; index < NETWORK_SELECTION_DISABLED_MAX;
+ index++) {
+ setDisableReasonCounter(index, in.readInt());
+ }
+ setDisableTime(in.readLong());
+ setNetworkSelectionBSSID(in.readString());
+ }
+ }
+
+ /**
+ * @hide
+ * network selection related member
+ */
+ private final NetworkSelectionStatus mNetworkSelectionStatus = new NetworkSelectionStatus();
+
+ /**
+ * @hide
+ * @return network selection status
+ */
+ public NetworkSelectionStatus getNetworkSelectionStatus() {
+ return mNetworkSelectionStatus;
+ }
/**
* @hide
* Linked Configurations: represent the set of Wificonfigurations that are equivalent
@@ -910,7 +1094,6 @@
roamingConsortiumIds = new long[0];
priority = 0;
hiddenSSID = false;
- disableReason = DISABLED_UNKNOWN_REASON;
allowedKeyManagement = new BitSet();
allowedProtocols = new BitSet();
allowedAuthAlgorithms = new BitSet();
@@ -921,7 +1104,6 @@
wepKeys[i] = null;
}
enterpriseConfig = new WifiEnterpriseConfig();
- autoJoinStatus = AUTO_JOIN_ENABLED;
selfAdded = false;
didSelfAdd = false;
ephemeral = false;
@@ -929,6 +1111,7 @@
mIpConfiguration = new IpConfiguration();
lastUpdateUid = -1;
creatorUid = -1;
+ shared = true;
}
/**
@@ -946,10 +1129,12 @@
* @hide
*/
public boolean isLinked(WifiConfiguration config) {
- if (config.linkedConfigurations != null && linkedConfigurations != null) {
- if (config.linkedConfigurations.get(configKey()) != null
- && linkedConfigurations.get(config.configKey()) != null) {
- return true;
+ if (config != null) {
+ if (config.linkedConfigurations != null && linkedConfigurations != null) {
+ if (config.linkedConfigurations.get(configKey()) != null
+ && linkedConfigurations.get(config.configKey()) != null) {
+ return true;
+ }
}
}
return false;
@@ -964,20 +1149,6 @@
allowedKeyManagement.get(KeyMgmt.IEEE8021X);
}
- /** @hide **/
- public void setAutoJoinStatus(int status) {
- if (status < 0) status = 0;
- if (status == 0) {
- blackListTimestamp = 0;
- } else if (status > autoJoinStatus) {
- blackListTimestamp = System.currentTimeMillis();
- }
- if (status != autoJoinStatus) {
- autoJoinStatus = status;
- dirty = true;
- }
- }
-
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
@@ -988,24 +1159,28 @@
}
sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
append(" PROVIDER-NAME: ").append(this.providerFriendlyName).
- append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN).
- append(" PRIO: ").append(this.priority).
- append('\n');
- if (this.numConnectionFailures > 0) {
- sbuf.append(" numConnectFailures ").append(this.numConnectionFailures).append("\n");
+ append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
+ .append(" PRIO: ").append(this.priority)
+ .append('\n');
+
+
+ sbuf.append(" NetworkSelectionStatus ")
+ .append(mNetworkSelectionStatus.getNetworkStatusString() + "\n");
+ if (mNetworkSelectionStatus.getNetworkSelectionDisableReason() > 0) {
+ sbuf.append(" mNetworkSelectionDisableReason ")
+ .append(mNetworkSelectionStatus.getNetworkDisableReasonString() + "\n");
+
+ for (int index = mNetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+ index < mNetworkSelectionStatus.NETWORK_SELECTION_DISABLED_MAX; index++) {
+ if (mNetworkSelectionStatus.getDisableReasonCounter(index) != 0) {
+ sbuf.append(NetworkSelectionStatus.getNetworkDisableReasonString(index)
+ + " counter:" + mNetworkSelectionStatus.getDisableReasonCounter(index)
+ + "\n");
+ }
+ }
}
- if (this.numIpConfigFailures > 0) {
- sbuf.append(" numIpConfigFailures ").append(this.numIpConfigFailures).append("\n");
- }
- if (this.numAuthFailures > 0) {
- sbuf.append(" numAuthFailures ").append(this.numAuthFailures).append("\n");
- }
- if (this.autoJoinStatus > 0) {
- sbuf.append(" autoJoinStatus ").append(this.autoJoinStatus).append("\n");
- }
- if (this.disableReason > 0) {
- sbuf.append(" disableReason ").append(this.disableReason).append("\n");
- }
+
+
if (this.numAssociation > 0) {
sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
}
@@ -1094,11 +1269,15 @@
sbuf.append("IP config:\n");
sbuf.append(mIpConfiguration.toString());
- if (this.autoJoinBSSID != null) sbuf.append(" autoJoinBSSID=" + autoJoinBSSID);
+ if (mNetworkSelectionStatus.getNetworkSelectionBSSID() != null) {
+ sbuf.append(" networkSelectionBSSID="
+ + mNetworkSelectionStatus.getNetworkSelectionBSSID());
+ }
long now_ms = System.currentTimeMillis();
- if (this.blackListTimestamp != 0) {
+ if (mNetworkSelectionStatus.getDisableTime() != NetworkSelectionStatus
+ .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP) {
sbuf.append('\n');
- long diff = now_ms - this.blackListTimestamp;
+ long diff = now_ms - mNetworkSelectionStatus.getDisableTime();
if (diff <= 0) {
sbuf.append(" blackListed since <incorrect>");
} else {
@@ -1172,11 +1351,6 @@
sbuf.append('\n');
sbuf.append("triggeredJoin: ").append(this.numUserTriggeredJoinAttempts);
sbuf.append('\n');
- sbuf.append("autoJoinBailedDueToLowRssi: ").append(this.autoJoinBailedDueToLowRssi);
- sbuf.append('\n');
- sbuf.append("autoJoinUseAggressiveJoinAttemptThreshold: ");
- sbuf.append(this.autoJoinUseAggressiveJoinAttemptThreshold);
- sbuf.append('\n');
return sbuf.toString();
}
@@ -1323,6 +1497,9 @@
key = mCachedConfigKey;
} else if (providerFriendlyName != null) {
key = FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+ if (!shared) {
+ key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
+ }
} else {
if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
@@ -1334,6 +1511,9 @@
} else {
key = SSID + KeyMgmt.strings[KeyMgmt.NONE];
}
+ if (!shared) {
+ key += "-" + Integer.toString(UserHandle.getUserId(creatorUid));
+ }
mCachedConfigKey = key;
}
return key;
@@ -1346,27 +1526,6 @@
return configKey(false);
}
- /** @hide
- * return the config key string based on a scan result
- */
- static public String configKey(ScanResult result) {
- String key = "\"" + result.SSID + "\"";
-
- if (result.capabilities.contains("WEP")) {
- key = key + "-WEP";
- }
-
- if (result.capabilities.contains("PSK")) {
- key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK];
- }
-
- if (result.capabilities.contains("EAP")) {
- key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP];
- }
-
- return key;
- }
-
/** @hide */
public IpConfiguration getIpConfiguration() {
return mIpConfiguration;
@@ -1428,13 +1587,16 @@
return 0;
}
+ /** @hide */
+ public boolean isVisibleToUser(int userId) {
+ return shared || (UserHandle.getUserId(creatorUid) == userId);
+ }
+
/** copy constructor {@hide} */
public WifiConfiguration(WifiConfiguration source) {
if (source != null) {
networkId = source.networkId;
status = source.status;
- disableReason = source.disableReason;
- disableReason = source.disableReason;
SSID = source.SSID;
BSSID = source.BSSID;
FQDN = source.FQDN;
@@ -1442,6 +1604,7 @@
providerFriendlyName = source.providerFriendlyName;
preSharedKey = source.preSharedKey;
+ mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus());
apBand = source.apBand;
apChannel = source.apChannel;
@@ -1458,7 +1621,6 @@
allowedAuthAlgorithms = (BitSet) source.allowedAuthAlgorithms.clone();
allowedPairwiseCiphers = (BitSet) source.allowedPairwiseCiphers.clone();
allowedGroupCiphers = (BitSet) source.allowedGroupCiphers.clone();
-
enterpriseConfig = new WifiEnterpriseConfig(source.enterpriseConfig);
defaultGwMacAddress = source.defaultGwMacAddress;
@@ -1476,7 +1638,6 @@
linkedConfigurations.putAll(source.linkedConfigurations);
}
mCachedConfigKey = null; //force null configKey
- autoJoinStatus = source.autoJoinStatus;
selfAdded = source.selfAdded;
validatedInternetAccess = source.validatedInternetAccess;
ephemeral = source.ephemeral;
@@ -1492,16 +1653,13 @@
creatorName = source.creatorName;
lastUpdateName = source.lastUpdateName;
peerWifiConfiguration = source.peerWifiConfiguration;
- blackListTimestamp = source.blackListTimestamp;
+
lastConnected = source.lastConnected;
lastDisconnected = source.lastDisconnected;
lastConnectionFailure = source.lastConnectionFailure;
lastRoamingFailure = source.lastRoamingFailure;
lastRoamingFailureReason = source.lastRoamingFailureReason;
roamingFailureBlackListTimeMilli = source.roamingFailureBlackListTimeMilli;
- numConnectionFailures = source.numConnectionFailures;
- numIpConfigFailures = source.numIpConfigFailures;
- numAuthFailures = source.numAuthFailures;
numScorerOverride = source.numScorerOverride;
numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
numAssociation = source.numAssociation;
@@ -1512,16 +1670,12 @@
numTicksAtBadRSSI = source.numTicksAtBadRSSI;
numTicksAtNotHighRSSI = source.numTicksAtNotHighRSSI;
numUserTriggeredJoinAttempts = source.numUserTriggeredJoinAttempts;
- autoJoinBSSID = source.autoJoinBSSID;
- autoJoinUseAggressiveJoinAttemptThreshold
- = source.autoJoinUseAggressiveJoinAttemptThreshold;
- autoJoinBailedDueToLowRssi = source.autoJoinBailedDueToLowRssi;
- dirty = source.dirty;
userApproved = source.userApproved;
numNoInternetAccessReports = source.numNoInternetAccessReports;
noInternetAccessExpected = source.noInternetAccessExpected;
creationTime = source.creationTime;
updateTime = source.updateTime;
+ shared = source.shared;
}
}
@@ -1535,12 +1689,11 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(networkId);
dest.writeInt(status);
- dest.writeInt(disableReason);
+ mNetworkSelectionStatus.writeToParcel(dest);
dest.writeString(SSID);
dest.writeString(BSSID);
dest.writeInt(apBand);
dest.writeInt(apChannel);
- dest.writeString(autoJoinBSSID);
dest.writeString(FQDN);
dest.writeString(providerFriendlyName);
dest.writeInt(roamingConsortiumIds.length);
@@ -1568,7 +1721,6 @@
dest.writeParcelable(mIpConfiguration, flags);
dest.writeString(dhcpServer);
dest.writeString(defaultGwMacAddress);
- dest.writeInt(autoJoinStatus);
dest.writeInt(selfAdded ? 1 : 0);
dest.writeInt(didSelfAdd ? 1 : 0);
dest.writeInt(validatedInternetAccess ? 1 : 0);
@@ -1578,14 +1730,10 @@
dest.writeInt(lastUpdateUid);
dest.writeString(creatorName);
dest.writeString(lastUpdateName);
- dest.writeLong(blackListTimestamp);
dest.writeLong(lastConnectionFailure);
dest.writeLong(lastRoamingFailure);
dest.writeInt(lastRoamingFailureReason);
dest.writeLong(roamingFailureBlackListTimeMilli);
- dest.writeInt(numConnectionFailures);
- dest.writeInt(numIpConfigFailures);
- dest.writeInt(numAuthFailures);
dest.writeInt(numScorerOverride);
dest.writeInt(numScorerOverrideAndSwitchedNetwork);
dest.writeInt(numAssociation);
@@ -1596,11 +1744,10 @@
dest.writeInt(numTicksAtBadRSSI);
dest.writeInt(numTicksAtNotHighRSSI);
dest.writeInt(numUserTriggeredJoinAttempts);
- dest.writeInt(autoJoinUseAggressiveJoinAttemptThreshold);
- dest.writeInt(autoJoinBailedDueToLowRssi ? 1 : 0);
dest.writeInt(userApproved);
dest.writeInt(numNoInternetAccessReports);
dest.writeInt(noInternetAccessExpected ? 1 : 0);
+ dest.writeInt(shared ? 1 : 0);
}
/** Implement the Parcelable interface {@hide} */
@@ -1610,12 +1757,11 @@
WifiConfiguration config = new WifiConfiguration();
config.networkId = in.readInt();
config.status = in.readInt();
- config.disableReason = in.readInt();
+ config.mNetworkSelectionStatus.readFromParcel(in);
config.SSID = in.readString();
config.BSSID = in.readString();
config.apBand = in.readInt();
config.apChannel = in.readInt();
- config.autoJoinBSSID = in.readString();
config.FQDN = in.readString();
config.providerFriendlyName = in.readString();
int numRoamingConsortiumIds = in.readInt();
@@ -1640,11 +1786,9 @@
config.allowedGroupCiphers = readBitSet(in);
config.enterpriseConfig = in.readParcelable(null);
-
config.mIpConfiguration = in.readParcelable(null);
config.dhcpServer = in.readString();
config.defaultGwMacAddress = in.readString();
- config.autoJoinStatus = in.readInt();
config.selfAdded = in.readInt() != 0;
config.didSelfAdd = in.readInt() != 0;
config.validatedInternetAccess = in.readInt() != 0;
@@ -1654,14 +1798,10 @@
config.lastUpdateUid = in.readInt();
config.creatorName = in.readString();
config.lastUpdateName = in.readString();
- config.blackListTimestamp = in.readLong();
config.lastConnectionFailure = in.readLong();
config.lastRoamingFailure = in.readLong();
config.lastRoamingFailureReason = in.readInt();
config.roamingFailureBlackListTimeMilli = in.readLong();
- config.numConnectionFailures = in.readInt();
- config.numIpConfigFailures = in.readInt();
- config.numAuthFailures = in.readInt();
config.numScorerOverride = in.readInt();
config.numScorerOverrideAndSwitchedNetwork = in.readInt();
config.numAssociation = in.readInt();
@@ -1672,11 +1812,10 @@
config.numTicksAtBadRSSI = in.readInt();
config.numTicksAtNotHighRSSI = in.readInt();
config.numUserTriggeredJoinAttempts = in.readInt();
- config.autoJoinUseAggressiveJoinAttemptThreshold = in.readInt();
- config.autoJoinBailedDueToLowRssi = in.readInt() != 0;
config.userApproved = in.readInt();
config.numNoInternetAccessReports = in.readInt();
config.noInternetAccessExpected = in.readInt() != 0;
+ config.shared = in.readInt() != 0;
return config;
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7a5a74f..c1269f9 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1007,7 +1007,7 @@
/**
* @return true if this adapter supports Neighbour Awareness Network APIs
- * @hide
+ * @hide PROPOSED_NAN_API
*/
public boolean isNanSupported() {
return isFeatureSupported(WIFI_FEATURE_NAN);
diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.aidl b/wifi/java/android/net/wifi/nan/ConfigRequest.aidl
new file mode 100644
index 0000000..38dddc2
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/ConfigRequest.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.net.wifi.nan;
+
+parcelable ConfigRequest;
diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.java b/wifi/java/android/net/wifi/nan/ConfigRequest.java
new file mode 100644
index 0000000..23e3754
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/ConfigRequest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Defines a request object to configure a Wi-Fi NAN network. Built using
+ * {@link ConfigRequest.Builder}. Configuration is requested using
+ * {@link WifiNanManager#requestConfig(ConfigRequest)}. Note that the actual
+ * achieved configuration may be different from the requested configuration -
+ * since multiple applications may request different configurations.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class ConfigRequest implements Parcelable {
+ /**
+ * Lower range of possible cluster ID.
+ *
+ * @hide
+ */
+ public static final int CLUSTER_ID_MIN = 0;
+
+ /**
+ * Upper range of possible cluster ID.
+ *
+ * @hide
+ */
+ public static final int CLUSTER_ID_MAX = 0xFFFF;
+
+ /**
+ * Indicates whether 5G band support is requested.
+ *
+ * @hide
+ */
+ public final boolean mSupport5gBand;
+
+ /**
+ * Specifies the desired master preference.
+ *
+ * @hide
+ */
+ public final int mMasterPreference;
+
+ /**
+ * Specifies the desired lower range of the cluster ID. Must be lower then
+ * {@link ConfigRequest#mClusterHigh}.
+ *
+ * @hide
+ */
+ public final int mClusterLow;
+
+ /**
+ * Specifies the desired higher range of the cluster ID. Must be higher then
+ * {@link ConfigRequest#mClusterLow}.
+ *
+ * @hide
+ */
+ public final int mClusterHigh;
+
+ private ConfigRequest(boolean support5gBand, int masterPreference, int clusterLow,
+ int clusterHigh) {
+ mSupport5gBand = support5gBand;
+ mMasterPreference = masterPreference;
+ mClusterLow = clusterLow;
+ mClusterHigh = clusterHigh;
+ }
+
+ @Override
+ public String toString() {
+ return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + ", mMasterPreference="
+ + mMasterPreference + ", mClusterLow=" + mClusterLow + ", mClusterHigh="
+ + mClusterHigh + "]";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSupport5gBand ? 1 : 0);
+ dest.writeInt(mMasterPreference);
+ dest.writeInt(mClusterLow);
+ dest.writeInt(mClusterHigh);
+ }
+
+ public static final Creator<ConfigRequest> CREATOR = new Creator<ConfigRequest>() {
+ @Override
+ public ConfigRequest[] newArray(int size) {
+ return new ConfigRequest[size];
+ }
+
+ @Override
+ public ConfigRequest createFromParcel(Parcel in) {
+ boolean support5gBand = in.readInt() != 0;
+ int masterPreference = in.readInt();
+ int clusterLow = in.readInt();
+ int clusterHigh = in.readInt();
+ return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh);
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof ConfigRequest)) {
+ return false;
+ }
+
+ ConfigRequest lhs = (ConfigRequest) o;
+
+ return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference
+ && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+
+ result = 31 * result + (mSupport5gBand ? 1 : 0);
+ result = 31 * result + mMasterPreference;
+ result = 31 * result + mClusterLow;
+ result = 31 * result + mClusterHigh;
+
+ return result;
+ }
+
+ /**
+ * Builder used to build {@link ConfigRequest} objects.
+ */
+ public static final class Builder {
+ private boolean mSupport5gBand;
+ private int mMasterPreference;
+ private int mClusterLow;
+ private int mClusterHigh;
+
+ /**
+ * Default constructor for the Builder.
+ */
+ public Builder() {
+ mSupport5gBand = false;
+ mMasterPreference = 0;
+ mClusterLow = 0;
+ mClusterHigh = CLUSTER_ID_MAX;
+ }
+
+ /**
+ * Specify whether 5G band support is required in this request.
+ *
+ * @param support5gBand Support for 5G band is required.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setSupport5gBand(boolean support5gBand) {
+ mSupport5gBand = support5gBand;
+ return this;
+ }
+
+ /**
+ * Specify the Master Preference requested. The permitted range is 0 to
+ * 255 with 1 and 255 excluded (reserved).
+ *
+ * @param masterPreference The requested master preference
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setMasterPreference(int masterPreference) {
+ if (masterPreference < 0) {
+ throw new IllegalArgumentException(
+ "Master Preference specification must be non-negative");
+ }
+ if (masterPreference == 1 || masterPreference == 255 || masterPreference > 255) {
+ throw new IllegalArgumentException("Master Preference specification must not "
+ + "exceed 255 or use 1 or 255 (reserved values)");
+ }
+
+ mMasterPreference = masterPreference;
+ return this;
+ }
+
+ /**
+ * The Cluster ID is generated randomly for new NAN networks. Specify
+ * the lower range of the cluster ID. The upper range is specified using
+ * the {@link ConfigRequest.Builder#setClusterHigh(int)}. The permitted
+ * range is 0 to the value specified by
+ * {@link ConfigRequest.Builder#setClusterHigh(int)}. Equality is
+ * permitted which restricts the Cluster ID to the specified value.
+ *
+ * @param clusterLow The lower range of the generated cluster ID.
+ * @return The builder to facilitate chaining
+ * {@code builder.setClusterLow(..).setClusterHigh(..)}.
+ */
+ public Builder setClusterLow(int clusterLow) {
+ if (clusterLow < CLUSTER_ID_MIN) {
+ throw new IllegalArgumentException("Cluster specification must be non-negative");
+ }
+ if (clusterLow > CLUSTER_ID_MAX) {
+ throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
+ }
+
+ mClusterLow = clusterLow;
+ return this;
+ }
+
+ /**
+ * The Cluster ID is generated randomly for new NAN networks. Specify
+ * the lower upper of the cluster ID. The lower range is specified using
+ * the {@link ConfigRequest.Builder#setClusterLow(int)}. The permitted
+ * range is the value specified by
+ * {@link ConfigRequest.Builder#setClusterLow(int)} to 0xFFFF. Equality
+ * is permitted which restricts the Cluster ID to the specified value.
+ *
+ * @param clusterHigh The upper range of the generated cluster ID.
+ * @return The builder to facilitate chaining
+ * {@code builder.setClusterLow(..).setClusterHigh(..)}.
+ */
+ public Builder setClusterHigh(int clusterHigh) {
+ if (clusterHigh < CLUSTER_ID_MIN) {
+ throw new IllegalArgumentException("Cluster specification must be non-negative");
+ }
+ if (clusterHigh > CLUSTER_ID_MAX) {
+ throw new IllegalArgumentException("Cluster specification must not exceed 0xFFFF");
+ }
+
+ mClusterHigh = clusterHigh;
+ return this;
+ }
+
+ /**
+ * Build {@link ConfigRequest} given the current requests made on the
+ * builder.
+ */
+ public ConfigRequest build() {
+ if (mClusterLow > mClusterHigh) {
+ throw new IllegalArgumentException(
+ "Invalid argument combination - must have Cluster Low <= Cluster High");
+ }
+
+ return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
new file mode 100644
index 0000000..13efc36
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl
@@ -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.
+ */
+
+package android.net.wifi.nan;
+
+import android.net.wifi.nan.ConfigRequest;
+
+/**
+ * Callback interface that WifiNanManager implements
+ *
+ * {@hide}
+ */
+oneway interface IWifiNanEventListener
+{
+ void onConfigCompleted(in ConfigRequest completedConfig);
+ void onConfigFailed(int reason);
+ void onNanDown(int reason);
+ void onIdentityChanged();
+}
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
new file mode 100644
index 0000000..ff3d29f
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.app.PendingIntent;
+
+import android.net.wifi.nan.ConfigRequest;
+import android.net.wifi.nan.IWifiNanEventListener;
+import android.net.wifi.nan.IWifiNanSessionListener;
+import android.net.wifi.nan.PublishData;
+import android.net.wifi.nan.PublishSettings;
+import android.net.wifi.nan.SubscribeData;
+import android.net.wifi.nan.SubscribeSettings;
+
+/**
+ * Interface that WifiNanService implements
+ *
+ * {@hide}
+ */
+interface IWifiNanManager
+{
+ // client API
+ void connect(in IBinder binder, in IWifiNanEventListener listener, int events);
+ void disconnect(in IBinder binder);
+ void requestConfig(in ConfigRequest configRequest);
+
+ // session API
+ int createSession(in IWifiNanSessionListener listener, int events);
+ void publish(int sessionId, in PublishData publishData, in PublishSettings publishSettings);
+ void subscribe(int sessionId, in SubscribeData subscribeData,
+ in SubscribeSettings subscribeSettings);
+ void sendMessage(int sessionId, int peerId, in byte[] message, int messageLength);
+ void stopSession(int sessionId);
+ void destroySession(int sessionId);
+}
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl
new file mode 100644
index 0000000..773f83b
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.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 android.net.wifi.nan;
+
+/**
+ * Callback interface that WifiNanManager implements
+ *
+ * {@hide}
+ */
+oneway interface IWifiNanSessionListener
+{
+ void onPublishFail(int reason);
+ void onPublishTerminated(int reason);
+
+ void onSubscribeFail(int reason);
+ void onSubscribeTerminated(int reason);
+
+ void onMatch(int peerId, in byte[] serviceSpecificInfo,
+ int serviceSpecificInfoLength, in byte[] matchFilter, int matchFilterLength);
+
+ void onMessageSendSuccess();
+ void onMessageSendFail(int reason);
+ void onMessageReceived(int peerId, in byte[] message, int messageLength);
+}
diff --git a/wifi/java/android/net/wifi/nan/PublishData.aidl b/wifi/java/android/net/wifi/nan/PublishData.aidl
new file mode 100644
index 0000000..15e4ddf
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/PublishData.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.net.wifi.nan;
+
+parcelable PublishData;
diff --git a/wifi/java/android/net/wifi/nan/PublishData.java b/wifi/java/android/net/wifi/nan/PublishData.java
new file mode 100644
index 0000000..80119eb
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/PublishData.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Defines the data for a NAN publish session. Built using
+ * {@link PublishData.Builder}. Publish is done using
+ * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
+ * or {@link WifiNanPublishSession#publish(PublishData, PublishSettings)}.
+ * @hide PROPOSED_NAN_API
+ */
+public class PublishData implements Parcelable {
+ /**
+ * @hide
+ */
+ public final String mServiceName;
+
+ /**
+ * @hide
+ */
+ public final int mServiceSpecificInfoLength;
+
+ /**
+ * @hide
+ */
+ public final byte[] mServiceSpecificInfo;
+
+ /**
+ * @hide
+ */
+ public final int mTxFilterLength;
+
+ /**
+ * @hide
+ */
+ public final byte[] mTxFilter;
+
+ /**
+ * @hide
+ */
+ public final int mRxFilterLength;
+
+ /**
+ * @hide
+ */
+ public final byte[] mRxFilter;
+
+ private PublishData(String serviceName, byte[] serviceSpecificInfo,
+ int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter,
+ int rxFilterLength) {
+ mServiceName = serviceName;
+ mServiceSpecificInfoLength = serviceSpecificInfoLength;
+ mServiceSpecificInfo = serviceSpecificInfo;
+ mTxFilterLength = txFilterLength;
+ mTxFilter = txFilter;
+ mRxFilterLength = rxFilterLength;
+ mRxFilter = rxFilter;
+ }
+
+ @Override
+ public String toString() {
+ return "PublishData [mServiceName='" + mServiceName + "', mServiceSpecificInfo='"
+ + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength))
+ + "', mTxFilter="
+ + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString()
+ + ", mRxFilter="
+ + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString()
+ + "']";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mServiceName);
+ dest.writeInt(mServiceSpecificInfoLength);
+ if (mServiceSpecificInfoLength != 0) {
+ dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength);
+ }
+ dest.writeInt(mTxFilterLength);
+ if (mTxFilterLength != 0) {
+ dest.writeByteArray(mTxFilter, 0, mTxFilterLength);
+ }
+ dest.writeInt(mRxFilterLength);
+ if (mRxFilterLength != 0) {
+ dest.writeByteArray(mRxFilter, 0, mRxFilterLength);
+ }
+ }
+
+ public static final Creator<PublishData> CREATOR = new Creator<PublishData>() {
+ @Override
+ public PublishData[] newArray(int size) {
+ return new PublishData[size];
+ }
+
+ @Override
+ public PublishData createFromParcel(Parcel in) {
+ String serviceName = in.readString();
+ int ssiLength = in.readInt();
+ byte[] ssi = new byte[ssiLength];
+ if (ssiLength != 0) {
+ in.readByteArray(ssi);
+ }
+ int txFilterLength = in.readInt();
+ byte[] txFilter = new byte[txFilterLength];
+ if (txFilterLength != 0) {
+ in.readByteArray(txFilter);
+ }
+ int rxFilterLength = in.readInt();
+ byte[] rxFilter = new byte[rxFilterLength];
+ if (rxFilterLength != 0) {
+ in.readByteArray(rxFilter);
+ }
+
+ return new PublishData(serviceName, ssi, ssiLength, txFilter, txFilterLength, rxFilter,
+ rxFilterLength);
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof PublishData)) {
+ return false;
+ }
+
+ PublishData lhs = (PublishData) o;
+
+ if (!mServiceName.equals(lhs.mServiceName)
+ || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength
+ || mTxFilterLength != lhs.mTxFilterLength
+ || mRxFilterLength != lhs.mRxFilterLength) {
+ return false;
+ }
+
+ if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) {
+ for (int i = 0; i < mServiceSpecificInfoLength; ++i) {
+ if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) {
+ return false;
+ }
+ }
+ } else if (mServiceSpecificInfoLength != 0) {
+ return false; // invalid != invalid
+ }
+
+ if (mTxFilter != null && lhs.mTxFilter != null) {
+ for (int i = 0; i < mTxFilterLength; ++i) {
+ if (mTxFilter[i] != lhs.mTxFilter[i]) {
+ return false;
+ }
+ }
+ } else if (mTxFilterLength != 0) {
+ return false; // invalid != invalid
+ }
+
+ if (mRxFilter != null && lhs.mRxFilter != null) {
+ for (int i = 0; i < mRxFilterLength; ++i) {
+ if (mRxFilter[i] != lhs.mRxFilter[i]) {
+ return false;
+ }
+ }
+ } else if (mRxFilterLength != 0) {
+ return false; // invalid != invalid
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+
+ result = 31 * result + mServiceName.hashCode();
+ result = 31 * result + mServiceSpecificInfoLength;
+ result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
+ result = 31 * result + mTxFilterLength;
+ result = 31 * result + Arrays.hashCode(mTxFilter);
+ result = 31 * result + mRxFilterLength;
+ result = 31 * result + Arrays.hashCode(mRxFilter);
+
+ return result;
+ }
+
+ /**
+ * Builder used to build {@link PublishData} objects.
+ */
+ public static final class Builder {
+ private String mServiceName;
+ private int mServiceSpecificInfoLength;
+ private byte[] mServiceSpecificInfo = new byte[0];
+ private int mTxFilterLength;
+ private byte[] mTxFilter = new byte[0];
+ private int mRxFilterLength;
+ private byte[] mRxFilter = new byte[0];
+
+ /**
+ * Specify the service name of the publish session. The actual on-air
+ * value is a 6 byte hashed representation of this string.
+ *
+ * @param serviceName The service name for the publish session.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setServiceName(String serviceName) {
+ mServiceName = serviceName;
+ return this;
+ }
+
+ /**
+ * Specify service specific information for the publish session. This is
+ * a free-form byte array available to the application to send
+ * additional information as part of the discovery operation - i.e. it
+ * will not be used to determine whether a publish/subscribe match
+ * occurs.
+ *
+ * @param serviceSpecificInfo A byte-array for the service-specific
+ * information field.
+ * @param serviceSpecificInfoLength The length of the byte-array to be
+ * used.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo,
+ int serviceSpecificInfoLength) {
+ if (serviceSpecificInfoLength != 0 && (serviceSpecificInfo == null
+ || serviceSpecificInfo.length < serviceSpecificInfoLength)) {
+ throw new IllegalArgumentException("Non-matching combination of "
+ + "serviceSpecificInfo and serviceSpecificInfoLength");
+ }
+ mServiceSpecificInfoLength = serviceSpecificInfoLength;
+ mServiceSpecificInfo = serviceSpecificInfo;
+ return this;
+ }
+
+ /**
+ * Specify service specific information for the publish session - same
+ * as {@link PublishData.Builder#setServiceSpecificInfo(byte[], int)}
+ * but obtaining the data from a String.
+ *
+ * @param serviceSpecificInfoStr The service specific information string
+ * to be included (as a byte array) in the publish
+ * information.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setServiceSpecificInfo(String serviceSpecificInfoStr) {
+ mServiceSpecificInfoLength = serviceSpecificInfoStr.length();
+ mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
+ return this;
+ }
+
+ /**
+ * The transmit filter for an active publish session
+ * {@link PublishSettings.Builder#setPublishType(int)} and
+ * {@link PublishSettings#PUBLISH_TYPE_UNSOLICITED}. Included in
+ * transmitted publish packets and used by receivers (subscribers) to
+ * determine whether they match - in addition to just relying on the
+ * service name.
+ * <p>
+ * Format is an LV byte array - the {@link TlvBufferUtils} utility class
+ * is available to form and parse.
+ *
+ * @param txFilter The byte-array containing the LV formatted transmit
+ * filter.
+ * @param txFilterLength The number of bytes in the transmit filter
+ * argument.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setTxFilter(byte[] txFilter, int txFilterLength) {
+ if (txFilterLength != 0 && (txFilter == null || txFilter.length < txFilterLength)) {
+ throw new IllegalArgumentException(
+ "Non-matching combination of txFilter and txFilterLength");
+ }
+ mTxFilter = txFilter;
+ mTxFilterLength = txFilterLength;
+ return this;
+ }
+
+ /**
+ * The transmit filter for a passive publish session
+ * {@link PublishSettings.Builder#setPublishType(int)} and
+ * {@link PublishSettings#PUBLISH_TYPE_SOLICITED}. Used by the publisher
+ * to determine whether they match transmitted subscriber packets
+ * (active subscribers) - in addition to just relying on the service
+ * name.
+ * <p>
+ * Format is an LV byte array - the {@link TlvBufferUtils} utility class
+ * is available to form and parse.
+ *
+ * @param rxFilter The byte-array containing the LV formatted receive
+ * filter.
+ * @param rxFilterLength The number of bytes in the receive filter
+ * argument.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) {
+ if (rxFilterLength != 0 && (rxFilter == null || rxFilter.length < rxFilterLength)) {
+ throw new IllegalArgumentException(
+ "Non-matching combination of rxFilter and rxFilterLength");
+ }
+ mRxFilter = rxFilter;
+ mRxFilterLength = rxFilterLength;
+ return this;
+ }
+
+ /**
+ * Build {@link PublishData} given the current requests made on the
+ * builder.
+ */
+ public PublishData build() {
+ return new PublishData(mServiceName, mServiceSpecificInfo, mServiceSpecificInfoLength,
+ mTxFilter, mTxFilterLength, mRxFilter, mRxFilterLength);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/PublishSettings.aidl b/wifi/java/android/net/wifi/nan/PublishSettings.aidl
new file mode 100644
index 0000000..ff69293
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/PublishSettings.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.net.wifi.nan;
+
+parcelable PublishSettings;
diff --git a/wifi/java/android/net/wifi/nan/PublishSettings.java b/wifi/java/android/net/wifi/nan/PublishSettings.java
new file mode 100644
index 0000000..bbc5340
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/PublishSettings.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Defines the settings (configuration) for a NAN publish session. Built using
+ * {@link PublishSettings.Builder}. Publish is done using
+ * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
+ * or {@link WifiNanPublishSession#publish(PublishData, PublishSettings)}.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class PublishSettings implements Parcelable {
+
+ /**
+ * Defines an unsolicited publish session - i.e. a publish session where
+ * publish packets are transmitted over-the-air. Configuration is done using
+ * {@link PublishSettings.Builder#setPublishType(int)}.
+ */
+ public static final int PUBLISH_TYPE_UNSOLICITED = 0;
+
+ /**
+ * Defines a solicited publish session - i.e. a publish session where
+ * publish packets are not transmitted over-the-air and the device listens
+ * and matches to transmitted subscribe packets. Configuration is done using
+ * {@link PublishSettings.Builder#setPublishType(int)}.
+ */
+ public static final int PUBLISH_TYPE_SOLICITED = 1;
+
+ /**
+ * @hide
+ */
+ public final int mPublishType;
+
+ /**
+ * @hide
+ */
+ public final int mPublishCount;
+
+ /**
+ * @hide
+ */
+ public final int mTtlSec;
+
+ private PublishSettings(int publishType, int publichCount, int ttlSec) {
+ mPublishType = publishType;
+ mPublishCount = publichCount;
+ mTtlSec = ttlSec;
+ }
+
+ @Override
+ public String toString() {
+ return "PublishSettings [mPublishType=" + mPublishType + ", mPublishCount=" + mPublishCount
+ + ", mTtlSec=" + mTtlSec + "]";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mPublishType);
+ dest.writeInt(mPublishCount);
+ dest.writeInt(mTtlSec);
+ }
+
+ public static final Creator<PublishSettings> CREATOR = new Creator<PublishSettings>() {
+ @Override
+ public PublishSettings[] newArray(int size) {
+ return new PublishSettings[size];
+ }
+
+ @Override
+ public PublishSettings createFromParcel(Parcel in) {
+ int publishType = in.readInt();
+ int publishCount = in.readInt();
+ int ttlSec = in.readInt();
+ return new PublishSettings(publishType, publishCount, ttlSec);
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof PublishSettings)) {
+ return false;
+ }
+
+ PublishSettings lhs = (PublishSettings) o;
+
+ return mPublishType == lhs.mPublishType && mPublishCount == lhs.mPublishCount
+ && mTtlSec == lhs.mTtlSec;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+
+ result = 31 * result + mPublishType;
+ result = 31 * result + mPublishCount;
+ result = 31 * result + mTtlSec;
+
+ return result;
+ }
+
+ /**
+ * Builder used to build {@link PublishSettings} objects.
+ */
+ public static final class Builder {
+ int mPublishType;
+ int mPublishCount;
+ int mTtlSec;
+
+ /**
+ * Sets the type of the publish session: solicited (aka active - publish
+ * packets are transmitted over-the-air), or unsolicited (aka passive -
+ * no publish packets are transmitted, a match is made against an active
+ * subscribe session whose packets are transmitted over-the-air).
+ *
+ * @param publishType Publish session type: solicited (
+ * {@link PublishSettings#PUBLISH_TYPE_SOLICITED}) or
+ * unsolicited (
+ * {@link PublishSettings#PUBLISH_TYPE_UNSOLICITED}).
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setPublishType(int publishType) {
+ if (publishType < PUBLISH_TYPE_UNSOLICITED || publishType > PUBLISH_TYPE_SOLICITED) {
+ throw new IllegalArgumentException("Invalid publishType - " + publishType);
+ }
+ mPublishType = publishType;
+ return this;
+ }
+
+ /**
+ * Sets the number of times a solicited (
+ * {@link PublishSettings.Builder#setPublishType(int)}) publish session
+ * will transmit a packet. When the count is reached an event will be
+ * generated for {@link WifiNanSessionListener#onPublishTerminated(int)}
+ * with reason={@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
+ *
+ * @param publishCount Number of publish packets to transmit.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setPublishCount(int publishCount) {
+ if (publishCount < 0) {
+ throw new IllegalArgumentException("Invalid publishCount - must be non-negative");
+ }
+ mPublishCount = publishCount;
+ return this;
+ }
+
+ /**
+ * Sets the time interval (in seconds) a solicited (
+ * {@link PublishSettings.Builder#setPublishCount(int)}) publish session
+ * will be alive - i.e. transmitting a packet. When the TTL is reached
+ * an event will be generated for
+ * {@link WifiNanSessionListener#onPublishTerminated(int)} with reason=
+ * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
+ *
+ * @param ttlSec Lifetime of a publish session in seconds.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setTtlSec(int ttlSec) {
+ if (ttlSec < 0) {
+ throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+ }
+ mTtlSec = ttlSec;
+ return this;
+ }
+
+ /**
+ * Build {@link PublishSettings} given the current requests made on the
+ * builder.
+ */
+ public PublishSettings build() {
+ return new PublishSettings(mPublishType, mPublishCount, mTtlSec);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/SubscribeData.aidl b/wifi/java/android/net/wifi/nan/SubscribeData.aidl
new file mode 100644
index 0000000..662fdb8
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/SubscribeData.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.net.wifi.nan;
+
+parcelable SubscribeData;
diff --git a/wifi/java/android/net/wifi/nan/SubscribeData.java b/wifi/java/android/net/wifi/nan/SubscribeData.java
new file mode 100644
index 0000000..cd6e918
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/SubscribeData.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * Defines the data for a NAN subscribe session. Built using
+ * {@link SubscribeData.Builder}. Subscribe is done using
+ * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
+ * or
+ * {@link WifiNanSubscribeSession#subscribe(SubscribeData, SubscribeSettings)}.
+ * @hide PROPOSED_NAN_API
+ */
+public class SubscribeData implements Parcelable {
+ /**
+ * @hide
+ */
+ public final String mServiceName;
+
+ /**
+ * @hide
+ */
+ public final int mServiceSpecificInfoLength;
+
+ /**
+ * @hide
+ */
+ public final byte[] mServiceSpecificInfo;
+
+ /**
+ * @hide
+ */
+ public final int mTxFilterLength;
+
+ /**
+ * @hide
+ */
+ public final byte[] mTxFilter;
+
+ /**
+ * @hide
+ */
+ public final int mRxFilterLength;
+
+ /**
+ * @hide
+ */
+ public final byte[] mRxFilter;
+
+ private SubscribeData(String serviceName, byte[] serviceSpecificInfo,
+ int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter,
+ int rxFilterLength) {
+ mServiceName = serviceName;
+ mServiceSpecificInfoLength = serviceSpecificInfoLength;
+ mServiceSpecificInfo = serviceSpecificInfo;
+ mTxFilterLength = txFilterLength;
+ mTxFilter = txFilter;
+ mRxFilterLength = rxFilterLength;
+ mRxFilter = rxFilter;
+ }
+
+ @Override
+ public String toString() {
+ return "SubscribeData [mServiceName='" + mServiceName + "', mServiceSpecificInfo='"
+ + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength))
+ + "', mTxFilter="
+ + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString()
+ + ", mRxFilter="
+ + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString()
+ + "']";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mServiceName);
+ dest.writeInt(mServiceSpecificInfoLength);
+ if (mServiceSpecificInfoLength != 0) {
+ dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength);
+ }
+ dest.writeInt(mTxFilterLength);
+ if (mTxFilterLength != 0) {
+ dest.writeByteArray(mTxFilter, 0, mTxFilterLength);
+ }
+ dest.writeInt(mRxFilterLength);
+ if (mRxFilterLength != 0) {
+ dest.writeByteArray(mRxFilter, 0, mRxFilterLength);
+ }
+ }
+
+ public static final Creator<SubscribeData> CREATOR = new Creator<SubscribeData>() {
+ @Override
+ public SubscribeData[] newArray(int size) {
+ return new SubscribeData[size];
+ }
+
+ @Override
+ public SubscribeData createFromParcel(Parcel in) {
+ String serviceName = in.readString();
+ int ssiLength = in.readInt();
+ byte[] ssi = new byte[ssiLength];
+ if (ssiLength != 0) {
+ in.readByteArray(ssi);
+ }
+ int txFilterLength = in.readInt();
+ byte[] txFilter = new byte[txFilterLength];
+ if (txFilterLength != 0) {
+ in.readByteArray(txFilter);
+ }
+ int rxFilterLength = in.readInt();
+ byte[] rxFilter = new byte[rxFilterLength];
+ if (rxFilterLength != 0) {
+ in.readByteArray(rxFilter);
+ }
+
+ return new SubscribeData(serviceName, ssi, ssiLength, txFilter, txFilterLength,
+ rxFilter, rxFilterLength);
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof SubscribeData)) {
+ return false;
+ }
+
+ SubscribeData lhs = (SubscribeData) o;
+
+ if (!mServiceName.equals(lhs.mServiceName)
+ || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength
+ || mTxFilterLength != lhs.mTxFilterLength
+ || mRxFilterLength != lhs.mRxFilterLength) {
+ return false;
+ }
+
+ if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) {
+ for (int i = 0; i < mServiceSpecificInfoLength; ++i) {
+ if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) {
+ return false;
+ }
+ }
+ } else if (mServiceSpecificInfoLength != 0) {
+ return false; // invalid != invalid
+ }
+
+ if (mTxFilter != null && lhs.mTxFilter != null) {
+ for (int i = 0; i < mTxFilterLength; ++i) {
+ if (mTxFilter[i] != lhs.mTxFilter[i]) {
+ return false;
+ }
+ }
+ } else if (mTxFilterLength != 0) {
+ return false; // invalid != invalid
+ }
+
+ if (mRxFilter != null && lhs.mRxFilter != null) {
+ for (int i = 0; i < mRxFilterLength; ++i) {
+ if (mRxFilter[i] != lhs.mRxFilter[i]) {
+ return false;
+ }
+ }
+ } else if (mRxFilterLength != 0) {
+ return false; // invalid != invalid
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+
+ result = 31 * result + mServiceName.hashCode();
+ result = 31 * result + mServiceSpecificInfoLength;
+ result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
+ result = 31 * result + mTxFilterLength;
+ result = 31 * result + Arrays.hashCode(mTxFilter);
+ result = 31 * result + mRxFilterLength;
+ result = 31 * result + Arrays.hashCode(mRxFilter);
+
+ return result;
+ }
+
+ /**
+ * Builder used to build {@link SubscribeData} objects.
+ */
+ public static final class Builder {
+ private String mServiceName;
+ private int mServiceSpecificInfoLength;
+ private byte[] mServiceSpecificInfo = new byte[0];
+ private int mTxFilterLength;
+ private byte[] mTxFilter = new byte[0];
+ private int mRxFilterLength;
+ private byte[] mRxFilter = new byte[0];
+
+ /**
+ * Specify the service name of the subscribe session. The actual on-air
+ * value is a 6 byte hashed representation of this string.
+ *
+ * @param serviceName The service name for the subscribe session.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setServiceName(String serviceName) {
+ mServiceName = serviceName;
+ return this;
+ }
+
+ /**
+ * Specify service specific information for the subscribe session. This
+ * is a free-form byte array available to the application to send
+ * additional information as part of the discovery operation - i.e. it
+ * will not be used to determine whether a publish/subscribe match
+ * occurs.
+ *
+ * @param serviceSpecificInfo A byte-array for the service-specific
+ * information field.
+ * @param serviceSpecificInfoLength The length of the byte-array to be
+ * used.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo,
+ int serviceSpecificInfoLength) {
+ mServiceSpecificInfoLength = serviceSpecificInfoLength;
+ mServiceSpecificInfo = serviceSpecificInfo;
+ return this;
+ }
+
+ /**
+ * Specify service specific information for the subscribe session - same
+ * as {@link SubscribeData.Builder#setServiceSpecificInfo(byte[], int)}
+ * but obtaining the data from a String.
+ *
+ * @param serviceSpecificInfoStr The service specific information string
+ * to be included (as a byte array) in the subscribe
+ * information.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setServiceSpecificInfo(String serviceSpecificInfoStr) {
+ mServiceSpecificInfoLength = serviceSpecificInfoStr.length();
+ mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
+ return this;
+ }
+
+ /**
+ * The transmit filter for an active subscribe session
+ * {@link SubscribeSettings.Builder#setSubscribeType(int)} and
+ * {@link SubscribeSettings#SUBSCRIBE_TYPE_ACTIVE}. Included in
+ * transmitted subscribe packets and used by receivers (passive
+ * publishers) to determine whether they match - in addition to just
+ * relying on the service name.
+ * <p>
+ * Format is an LV byte array - the {@link TlvBufferUtils} utility class
+ * is available to form and parse.
+ *
+ * @param txFilter The byte-array containing the LV formatted transmit
+ * filter.
+ * @param txFilterLength The number of bytes in the transmit filter
+ * argument.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setTxFilter(byte[] txFilter, int txFilterLength) {
+ mTxFilter = txFilter;
+ mTxFilterLength = txFilterLength;
+ return this;
+ }
+
+ /**
+ * The transmit filter for a passive subsribe session
+ * {@link SubscribeSettings.Builder#setSubscribeType(int)} and
+ * {@link SubscribeSettings#SUBSCRIBE_TYPE_PASSIVE}. Used by the
+ * subscriber to determine whether they match transmitted publish
+ * packets - in addition to just relying on the service name.
+ * <p>
+ * Format is an LV byte array - the {@link TlvBufferUtils} utility class
+ * is available to form and parse.
+ *
+ * @param rxFilter The byte-array containing the LV formatted receive
+ * filter.
+ * @param rxFilterLength The number of bytes in the receive filter
+ * argument.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) {
+ mRxFilter = rxFilter;
+ mRxFilterLength = rxFilterLength;
+ return this;
+ }
+
+ /**
+ * Build {@link SubscribeData} given the current requests made on the
+ * builder.
+ */
+ public SubscribeData build() {
+ return new SubscribeData(mServiceName, mServiceSpecificInfo, mServiceSpecificInfoLength,
+ mTxFilter, mTxFilterLength, mRxFilter, mRxFilterLength);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl b/wifi/java/android/net/wifi/nan/SubscribeSettings.aidl
new file mode 100644
index 0000000..44849bc
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/SubscribeSettings.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.net.wifi.nan;
+
+parcelable SubscribeSettings;
diff --git a/wifi/java/android/net/wifi/nan/SubscribeSettings.java b/wifi/java/android/net/wifi/nan/SubscribeSettings.java
new file mode 100644
index 0000000..5c4f8fb
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/SubscribeSettings.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Defines the settings (configuration) for a NAN subscribe session. Built using
+ * {@link SubscribeSettings.Builder}. Subscribe is done using
+ * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
+ * or {@link WifiNanSubscribeSession#subscribe(SubscribeData, SubscribeSettings)}.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class SubscribeSettings implements Parcelable {
+
+ /**
+ * Defines a passive subscribe session - i.e. a subscribe session where
+ * subscribe packets are not transmitted over-the-air and the device listens
+ * and matches to transmitted publish packets. Configuration is done using
+ * {@link SubscribeSettings.Builder#setSubscribeType(int)}.
+ */
+ public static final int SUBSCRIBE_TYPE_PASSIVE = 0;
+
+ /**
+ * Defines an active subscribe session - i.e. a subscribe session where
+ * subscribe packets are transmitted over-the-air. Configuration is done
+ * using {@link SubscribeSettings.Builder#setSubscribeType(int)}.
+ */
+ public static final int SUBSCRIBE_TYPE_ACTIVE = 1;
+
+ /**
+ * @hide
+ */
+ public final int mSubscribeType;
+
+ /**
+ * @hide
+ */
+ public final int mSubscribeCount;
+
+ /**
+ * @hide
+ */
+ public final int mTtlSec;
+
+ private SubscribeSettings(int subscribeType, int publichCount, int ttlSec) {
+ mSubscribeType = subscribeType;
+ mSubscribeCount = publichCount;
+ mTtlSec = ttlSec;
+ }
+
+ @Override
+ public String toString() {
+ return "SubscribeSettings [mSubscribeType=" + mSubscribeType + ", mSubscribeCount="
+ + mSubscribeCount + ", mTtlSec=" + mTtlSec + "]";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mSubscribeType);
+ dest.writeInt(mSubscribeCount);
+ dest.writeInt(mTtlSec);
+ }
+
+ public static final Creator<SubscribeSettings> CREATOR = new Creator<SubscribeSettings>() {
+ @Override
+ public SubscribeSettings[] newArray(int size) {
+ return new SubscribeSettings[size];
+ }
+
+ @Override
+ public SubscribeSettings createFromParcel(Parcel in) {
+ int subscribeType = in.readInt();
+ int subscribeCount = in.readInt();
+ int ttlSec = in.readInt();
+ return new SubscribeSettings(subscribeType, subscribeCount, ttlSec);
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof SubscribeSettings)) {
+ return false;
+ }
+
+ SubscribeSettings lhs = (SubscribeSettings) o;
+
+ return mSubscribeType == lhs.mSubscribeType && mSubscribeCount == lhs.mSubscribeCount
+ && mTtlSec == lhs.mTtlSec;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+
+ result = 31 * result + mSubscribeType;
+ result = 31 * result + mSubscribeCount;
+ result = 31 * result + mTtlSec;
+
+ return result;
+ }
+
+ /**
+ * Builder used to build {@link SubscribeSettings} objects.
+ */
+ public static final class Builder {
+ int mSubscribeType;
+ int mSubscribeCount;
+ int mTtlSec;
+
+ /**
+ * Sets the type of the subscribe session: active (subscribe packets are
+ * transmitted over-the-air), or passive (no subscribe packets are
+ * transmitted, a match is made against a solicited/active publish
+ * session whose packets are transmitted over-the-air).
+ *
+ * @param subscribeType Subscribe session type: active (
+ * {@link SubscribeSettings#SUBSCRIBE_TYPE_ACTIVE}) or
+ * passive ( {@link SubscribeSettings#SUBSCRIBE_TYPE_PASSIVE}
+ * ).
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setSubscribeType(int subscribeType) {
+ if (subscribeType < SUBSCRIBE_TYPE_PASSIVE || subscribeType > SUBSCRIBE_TYPE_ACTIVE) {
+ throw new IllegalArgumentException("Invalid subscribeType - " + subscribeType);
+ }
+ mSubscribeType = subscribeType;
+ return this;
+ }
+
+ /**
+ * Sets the number of times an active (
+ * {@link SubscribeSettings.Builder#setSubscribeType(int)}) subscribe
+ * session will transmit a packet. When the count is reached an event
+ * will be generated for
+ * {@link WifiNanSessionListener#onSubscribeTerminated(int)} with reason=
+ * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
+ *
+ * @param subscribeCount Number of subscribe packets to transmit.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setSubscribeCount(int subscribeCount) {
+ if (subscribeCount < 0) {
+ throw new IllegalArgumentException("Invalid subscribeCount - must be non-negative");
+ }
+ mSubscribeCount = subscribeCount;
+ return this;
+ }
+
+ /**
+ * Sets the time interval (in seconds) an active (
+ * {@link SubscribeSettings.Builder#setSubscribeType(int)}) subscribe
+ * session will be alive - i.e. transmitting a packet. When the TTL is
+ * reached an event will be generated for
+ * {@link WifiNanSessionListener#onSubscribeTerminated(int)} with reason=
+ * {@link WifiNanSessionListener#TERMINATE_REASON_DONE}.
+ *
+ * @param ttlSec Lifetime of a subscribe session in seconds.
+ * @return The builder to facilitate chaining
+ * {@code builder.setXXX(..).setXXX(..)}.
+ */
+ public Builder setTtlSec(int ttlSec) {
+ if (ttlSec < 0) {
+ throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
+ }
+ mTtlSec = ttlSec;
+ return this;
+ }
+
+ /**
+ * Build {@link SubscribeSettings} given the current requests made on
+ * the builder.
+ */
+ public SubscribeSettings build() {
+ return new SubscribeSettings(mSubscribeType, mSubscribeCount, mTtlSec);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
new file mode 100644
index 0000000..ea8785a
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import libcore.io.Memory;
+
+import java.nio.BufferOverflowException;
+import java.nio.ByteOrder;
+import java.util.Iterator;
+
+/**
+ * Utility class to construct and parse byte arrays using the TLV format -
+ * Type/Length/Value format. The utilities accept a configuration of the size of
+ * the Type field and the Length field. A Type field size of 0 is allowed -
+ * allowing usage for LV (no T) array formats.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class TlvBufferUtils {
+ private TlvBufferUtils() {
+ // no reason to ever create this class
+ }
+
+ /**
+ * Utility class to construct byte arrays using the TLV format -
+ * Type/Length/Value.
+ * <p>
+ * A constructor is created specifying the size of the Type (T) and Length
+ * (L) fields. A specification of zero size T field is allowed - resulting
+ * in LV type format.
+ * <p>
+ * The byte array is either provided (using
+ * {@link TlvConstructor#wrap(byte[])}) or allocated (using
+ * {@link TlvConstructor#allocate(int)}).
+ * <p>
+ * Values are added to the structure using the {@code TlvConstructor.put*()}
+ * methods.
+ * <p>
+ * The final byte array is obtained using {@link TlvConstructor#getArray()}
+ * and {@link TlvConstructor#getActualLength()} methods.
+ */
+ public static class TlvConstructor {
+ private int mTypeSize;
+ private int mLengthSize;
+
+ private byte[] mArray;
+ private int mArrayLength;
+ private int mPosition;
+
+ /**
+ * Define a TLV constructor with the specified size of the Type (T) and
+ * Length (L) fields.
+ *
+ * @param typeSize Number of bytes used for the Type (T) field. Values
+ * of 0, 1, or 2 bytes are allowed. A specification of 0
+ * bytes implies that the field being constructed has the LV
+ * format rather than the TLV format.
+ * @param lengthSize Number of bytes used for the Length (L) field.
+ * Values of 1 or 2 bytes are allowed.
+ */
+ public TlvConstructor(int typeSize, int lengthSize) {
+ if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) {
+ throw new IllegalArgumentException(
+ "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize);
+ }
+ mTypeSize = typeSize;
+ mLengthSize = lengthSize;
+ }
+
+ /**
+ * Set the byte array to be used to construct the TLV.
+ *
+ * @param array Byte array to be formatted.
+ * @return The constructor to facilitate chaining
+ * {@code ctr.putXXX(..).putXXX(..)}.
+ */
+ public TlvConstructor wrap(byte[] array) {
+ mArray = array;
+ mArrayLength = array.length;
+ return this;
+ }
+
+ /**
+ * Allocates a new byte array to be used ot construct a TLV.
+ *
+ * @param capacity The size of the byte array to be allocated.
+ * @return The constructor to facilitate chaining
+ * {@code ctr.putXXX(..).putXXX(..)}.
+ */
+ public TlvConstructor allocate(int capacity) {
+ mArray = new byte[capacity];
+ mArrayLength = capacity;
+ return this;
+ }
+
+ /**
+ * Copies a byte into the TLV with the indicated type. For an LV
+ * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+ * TlvConstructor(int, int)} ) the type field is ignored.
+ *
+ * @param type The value to be placed into the Type field.
+ * @param b The byte to be inserted into the structure.
+ * @return The constructor to facilitate chaining
+ * {@code ctr.putXXX(..).putXXX(..)}.
+ */
+ public TlvConstructor putByte(int type, byte b) {
+ checkLength(1);
+ addHeader(type, 1);
+ mArray[mPosition++] = b;
+ return this;
+ }
+
+ /**
+ * Copies a byte array into the TLV with the indicated type. For an LV
+ * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+ * TlvConstructor(int, int)} ) the type field is ignored.
+ *
+ * @param type The value to be placed into the Type field.
+ * @param array The array to be copied into the TLV structure.
+ * @param offset Start copying from the array at the specified offset.
+ * @param length Copy the specified number (length) of bytes from the
+ * array.
+ * @return The constructor to facilitate chaining
+ * {@code ctr.putXXX(..).putXXX(..)}.
+ */
+ public TlvConstructor putByteArray(int type, byte[] array, int offset, int length) {
+ checkLength(length);
+ addHeader(type, length);
+ System.arraycopy(array, offset, mArray, mPosition, length);
+ mPosition += length;
+ return this;
+ }
+
+ /**
+ * Copies a byte array into the TLV with the indicated type. For an LV
+ * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+ * TlvConstructor(int, int)} ) the type field is ignored.
+ *
+ * @param type The value to be placed into the Type field.
+ * @param array The array to be copied (in full) into the TLV structure.
+ * @return The constructor to facilitate chaining
+ * {@code ctr.putXXX(..).putXXX(..)}.
+ */
+ public TlvConstructor putByteArray(int type, byte[] array) {
+ return putByteArray(type, array, 0, array.length);
+ }
+
+ /**
+ * Places a zero length element (i.e. Length field = 0) into the TLV.
+ * For an LV formatted structure (i.e. typeLength=0 in
+ * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is
+ * ignored.
+ *
+ * @param type The value to be placed into the Type field.
+ * @return The constructor to facilitate chaining
+ * {@code ctr.putXXX(..).putXXX(..)}.
+ */
+ public TlvConstructor putZeroLengthElement(int type) {
+ checkLength(0);
+ addHeader(type, 0);
+ return this;
+ }
+
+ /**
+ * Copies short into the TLV with the indicated type. For an LV
+ * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+ * TlvConstructor(int, int)} ) the type field is ignored.
+ *
+ * @param type The value to be placed into the Type field.
+ * @param data The short to be inserted into the structure.
+ * @return The constructor to facilitate chaining
+ * {@code ctr.putXXX(..).putXXX(..)}.
+ */
+ public TlvConstructor putShort(int type, short data) {
+ checkLength(2);
+ addHeader(type, 2);
+ Memory.pokeShort(mArray, mPosition, data, ByteOrder.BIG_ENDIAN);
+ mPosition += 2;
+ return this;
+ }
+
+ /**
+ * Copies integer into the TLV with the indicated type. For an LV
+ * formatted structure (i.e. typeLength=0 in {@link TlvConstructor
+ * TlvConstructor(int, int)} ) the type field is ignored.
+ *
+ * @param type The value to be placed into the Type field.
+ * @param data The integer to be inserted into the structure.
+ * @return The constructor to facilitate chaining
+ * {@code ctr.putXXX(..).putXXX(..)}.
+ */
+ public TlvConstructor putInt(int type, int data) {
+ checkLength(4);
+ addHeader(type, 4);
+ Memory.pokeInt(mArray, mPosition, data, ByteOrder.BIG_ENDIAN);
+ mPosition += 4;
+ return this;
+ }
+
+ /**
+ * Copies a String's byte representation into the TLV with the indicated
+ * type. For an LV formatted structure (i.e. typeLength=0 in
+ * {@link TlvConstructor TlvConstructor(int, int)} ) the type field is
+ * ignored.
+ *
+ * @param type The value to be placed into the Type field.
+ * @param data The string whose bytes are to be inserted into the
+ * structure.
+ * @return The constructor to facilitate chaining
+ * {@code ctr.putXXX(..).putXXX(..)}.
+ */
+ public TlvConstructor putString(int type, String data) {
+ return putByteArray(type, data.getBytes(), 0, data.length());
+ }
+
+ /**
+ * Returns the constructed TLV formatted byte-array. Note that the
+ * returned array is the fully wrapped (
+ * {@link TlvConstructor#wrap(byte[])}) or allocated (
+ * {@link TlvConstructor#allocate(int)}) array - which isn't necessarily
+ * the actual size of the formatted data. Use
+ * {@link TlvConstructor#getActualLength()} to obtain the size of the
+ * formatted data.
+ *
+ * @return The byte array containing the TLV formatted structure.
+ */
+ public byte[] getArray() {
+ return mArray;
+ }
+
+ /**
+ * Returns the size of the TLV formatted portion of the wrapped or
+ * allocated byte array. The array itself is returned with
+ * {@link TlvConstructor#getArray()}.
+ *
+ * @return The size of the TLV formatted portion of the byte array.
+ */
+ public int getActualLength() {
+ return mPosition;
+ }
+
+ private void checkLength(int dataLength) {
+ if (mPosition + mTypeSize + mLengthSize + dataLength > mArrayLength) {
+ throw new BufferOverflowException();
+ }
+ }
+
+ private void addHeader(int type, int length) {
+ if (mTypeSize == 1) {
+ mArray[mPosition] = (byte) type;
+ } else if (mTypeSize == 2) {
+ Memory.pokeShort(mArray, mPosition, (short) type, ByteOrder.BIG_ENDIAN);
+ }
+ mPosition += mTypeSize;
+
+ if (mLengthSize == 1) {
+ mArray[mPosition] = (byte) length;
+ } else if (mLengthSize == 2) {
+ Memory.pokeShort(mArray, mPosition, (short) length, ByteOrder.BIG_ENDIAN);
+ }
+ mPosition += mLengthSize;
+ }
+ }
+
+ /**
+ * Utility class used when iterating over a TLV formatted byte-array. Use
+ * {@link TlvIterable} to iterate over array. A {@link TlvElement}
+ * represents each entry in a TLV formatted byte-array.
+ */
+ public static class TlvElement {
+ /**
+ * The Type (T) field of the current TLV element. Note that for LV
+ * formatted byte-arrays (i.e. TLV whose Type/T size is 0) the value of
+ * this field is undefined.
+ */
+ public int mType;
+
+ /**
+ * The Length (L) field of the current TLV element.
+ */
+ public int mLength;
+
+ /**
+ * The Value (V) field - a raw byte array representing the current TLV
+ * element where the entry starts at {@link TlvElement#mOffset}.
+ */
+ public byte[] mRefArray;
+
+ /**
+ * The offset to be used into {@link TlvElement#mRefArray} to access the
+ * raw data representing the current TLV element.
+ */
+ public int mOffset;
+
+ private TlvElement(int type, int length, byte[] refArray, int offset) {
+ mType = type;
+ mLength = length;
+ mRefArray = refArray;
+ mOffset = offset;
+ }
+
+ /**
+ * Utility function to return a byte representation of a TLV element of
+ * length 1. Note: an attempt to call this function on a TLV item whose
+ * {@link TlvElement#mLength} is != 1 will result in an exception.
+ *
+ * @return byte representation of current TLV element.
+ */
+ public byte getByte() {
+ if (mLength != 1) {
+ throw new IllegalArgumentException(
+ "Accesing a byte from a TLV element of length " + mLength);
+ }
+ return mRefArray[mOffset];
+ }
+
+ /**
+ * Utility function to return a short representation of a TLV element of
+ * length 2. Note: an attempt to call this function on a TLV item whose
+ * {@link TlvElement#mLength} is != 2 will result in an exception.
+ *
+ * @return short representation of current TLV element.
+ */
+ public short getShort() {
+ if (mLength != 2) {
+ throw new IllegalArgumentException(
+ "Accesing a short from a TLV element of length " + mLength);
+ }
+ return Memory.peekShort(mRefArray, mOffset, ByteOrder.BIG_ENDIAN);
+ }
+
+ /**
+ * Utility function to return an integer representation of a TLV element
+ * of length 4. Note: an attempt to call this function on a TLV item
+ * whose {@link TlvElement#mLength} is != 4 will result in an exception.
+ *
+ * @return integer representation of current TLV element.
+ */
+ public int getInt() {
+ if (mLength != 4) {
+ throw new IllegalArgumentException(
+ "Accesing an int from a TLV element of length " + mLength);
+ }
+ return Memory.peekInt(mRefArray, mOffset, ByteOrder.BIG_ENDIAN);
+ }
+
+ /**
+ * Utility function to return a String representation of a TLV element.
+ *
+ * @return String repersentation of the current TLV element.
+ */
+ public String getString() {
+ return new String(mRefArray, mOffset, mLength);
+ }
+ }
+
+ /**
+ * Utility class to iterate over a TLV formatted byte-array.
+ */
+ public static class TlvIterable implements Iterable<TlvElement> {
+ private int mTypeSize;
+ private int mLengthSize;
+ private byte[] mArray;
+ private int mArrayLength;
+
+ /**
+ * Constructs a TlvIterable object - specifying the format of the TLV
+ * (the sizes of the Type and Length fields), and the byte array whose
+ * data is to be parsed.
+ *
+ * @param typeSize Number of bytes used for the Type (T) field. Valid
+ * values are 0 (i.e. indicating the format is LV rather than
+ * TLV), 1, and 2 bytes.
+ * @param lengthSize Number of bytes sued for the Length (L) field.
+ * Values values are 1 or 2 bytes.
+ * @param array The TLV formatted byte-array to parse.
+ * @param length The number of bytes of the array to be used in the
+ * parsing.
+ */
+ public TlvIterable(int typeSize, int lengthSize, byte[] array, int length) {
+ if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) {
+ throw new IllegalArgumentException(
+ "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize);
+ }
+ mTypeSize = typeSize;
+ mLengthSize = lengthSize;
+ mArray = array;
+ mArrayLength = length;
+ }
+
+ /**
+ * Prints out a parsed representation of the TLV-formatted byte array.
+ * Whenever possible bytes, shorts, and integer are printed out (for
+ * fields whose length is 1, 2, or 4 respectively).
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("[");
+ boolean first = true;
+ for (TlvElement tlv : this) {
+ if (!first) {
+ builder.append(",");
+ }
+ first = false;
+ builder.append(" (");
+ if (mTypeSize != 0) {
+ builder.append("T=" + tlv.mType + ",");
+ }
+ builder.append("L=" + tlv.mLength + ") ");
+ if (tlv.mLength == 0) {
+ builder.append("<null>");
+ } else if (tlv.mLength == 1) {
+ builder.append(tlv.getByte());
+ } else if (tlv.mLength == 2) {
+ builder.append(tlv.getShort());
+ } else if (tlv.mLength == 4) {
+ builder.append(tlv.getInt());
+ } else {
+ builder.append("<bytes>");
+ }
+ if (tlv.mLength != 0) {
+ builder.append(" (S='" + tlv.getString() + "')");
+ }
+ }
+ builder.append("]");
+
+ return builder.toString();
+ }
+
+ /**
+ * Returns an iterator to step through a TLV formatted byte-array. The
+ * individual elements returned by the iterator are {@link TlvElement}.
+ */
+ @Override
+ public Iterator<TlvElement> iterator() {
+ return new Iterator<TlvElement>() {
+ private int mOffset = 0;
+
+ @Override
+ public boolean hasNext() {
+ return mOffset < mArrayLength;
+ }
+
+ @Override
+ public TlvElement next() {
+ int type = 0;
+ if (mTypeSize == 1) {
+ type = mArray[mOffset];
+ } else if (mTypeSize == 2) {
+ type = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN);
+ }
+ mOffset += mTypeSize;
+
+ int length = 0;
+ if (mLengthSize == 1) {
+ length = mArray[mOffset];
+ } else if (mLengthSize == 2) {
+ length = Memory.peekShort(mArray, mOffset, ByteOrder.BIG_ENDIAN);
+ }
+ mOffset += mLengthSize;
+
+ TlvElement tlv = new TlvElement(type, length, mArray, mOffset);
+ mOffset += length;
+ return tlv;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
new file mode 100644
index 0000000..eae0a55
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * Base class for NAN events callbacks. Should be extended by applications
+ * wanting notifications. These are callbacks applying to the NAN connection as
+ * a whole - not to specific publish or subscribe sessions - for that see
+ * {@link WifiNanSessionListener}.
+ * <p>
+ * During registration specify which specific events are desired using a set of
+ * {@code NanEventListener.LISTEN_*} flags OR'd together. Only those events will
+ * be delivered to the registered listener. Override those callbacks
+ * {@code NanEventListener.on*} for the registered events.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanEventListener {
+ private static final String TAG = "WifiNanEventListener";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ /**
+ * Configuration completion callback event registration flag. Corresponding
+ * callback is {@link WifiNanEventListener#onConfigCompleted(ConfigRequest)}.
+ */
+ public static final int LISTEN_CONFIG_COMPLETED = 0x1 << 0;
+
+ /**
+ * Configuration failed callback event registration flag. Corresponding
+ * callback is {@link WifiNanEventListener#onConfigFailed(int)}.
+ */
+ public static final int LISTEN_CONFIG_FAILED = 0x1 << 1;
+
+ /**
+ * NAN cluster is down callback event registration flag. Corresponding
+ * callback is {@link WifiNanEventListener#onNanDown(int)}.
+ */
+ public static final int LISTEN_NAN_DOWN = 0x1 << 2;
+
+ /**
+ * NAN identity has changed event registration flag. This may be due to
+ * joining a cluster, starting a cluster, or discovery interface change. The
+ * implication is that peers you've been communicating with may no longer
+ * recognize you and you need to re-establish your identity. Corresponding
+ * callback is {@link WifiNanEventListener#onIdentityChanged()}.
+ */
+ public static final int LISTEN_IDENTITY_CHANGED = 0x1 << 3;
+
+ private final Handler mHandler;
+
+ /**
+ * Constructs a {@link WifiNanEventListener} using the looper of the current
+ * thread. I.e. all callbacks will be delivered on the current thread.
+ */
+ public WifiNanEventListener() {
+ this(Looper.myLooper());
+ }
+
+ /**
+ * Constructs a {@link WifiNanEventListener} using the specified looper. I.e.
+ * all callbacks will delivered on the thread of the specified looper.
+ *
+ * @param looper The looper on which to execute the callbacks.
+ */
+ public WifiNanEventListener(Looper looper) {
+ if (VDBG) Log.v(TAG, "ctor: looper=" + looper);
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
+ switch (msg.what) {
+ case LISTEN_CONFIG_COMPLETED:
+ WifiNanEventListener.this.onConfigCompleted((ConfigRequest) msg.obj);
+ break;
+ case LISTEN_CONFIG_FAILED:
+ WifiNanEventListener.this.onConfigFailed(msg.arg1);
+ break;
+ case LISTEN_NAN_DOWN:
+ WifiNanEventListener.this.onNanDown(msg.arg1);
+ break;
+ case LISTEN_IDENTITY_CHANGED:
+ WifiNanEventListener.this.onIdentityChanged();
+ break;
+ }
+ }
+ };
+ }
+
+ /**
+ * Called when NAN configuration is completed. Event will only be delivered
+ * if registered using {@link WifiNanEventListener#LISTEN_CONFIG_COMPLETED}. A
+ * dummy (empty implementation printing out a warning). Make sure to
+ * override if registered.
+ *
+ * @param completedConfig The actual configuration request which was
+ * completed. Note that it may be different from that requested
+ * by the application. The service combines configuration
+ * requests from all applications.
+ */
+ public void onConfigCompleted(ConfigRequest completedConfig) {
+ Log.w(TAG, "onConfigCompleted: called in stub - override if interested or disable");
+ }
+
+ /**
+ * Called when NAN configuration failed. Event will only be delivered if
+ * registered using {@link WifiNanEventListener#LISTEN_CONFIG_FAILED}. A dummy
+ * (empty implementation printing out a warning). Make sure to override if
+ * registered.
+ *
+ * @param reason Failure reason code, see {@code NanSessionListener.FAIL_*}.
+ */
+ public void onConfigFailed(int reason) {
+ Log.w(TAG, "onConfigFailed: called in stub - override if interested or disable");
+ }
+
+ /**
+ * Called when NAN cluster is down. Event will only be delivered if
+ * registered using {@link WifiNanEventListener#LISTEN_NAN_DOWN}. A dummy (empty
+ * implementation printing out a warning). Make sure to override if
+ * registered.
+ *
+ * @param reason Reason code for event, see {@code NanSessionListener.FAIL_*}.
+ */
+ public void onNanDown(int reason) {
+ Log.w(TAG, "onNanDown: called in stub - override if interested or disable");
+ }
+
+ /**
+ * Called when NAN identity has changed. This may be due to joining a
+ * cluster, starting a cluster, or discovery interface change. The
+ * implication is that peers you've been communicating with may no longer
+ * recognize you and you need to re-establish your identity. Event will only
+ * be delivered if registered using
+ * {@link WifiNanEventListener#LISTEN_IDENTITY_CHANGED}. A dummy (empty
+ * implementation printing out a warning). Make sure to override if
+ * registered.
+ */
+ public void onIdentityChanged() {
+ if (VDBG) Log.v(TAG, "onIdentityChanged: called in stub - override if interested");
+ }
+
+ /**
+ * {@hide}
+ */
+ public IWifiNanEventListener callback = new IWifiNanEventListener.Stub() {
+ @Override
+ public void onConfigCompleted(ConfigRequest completedConfig) {
+ if (VDBG) Log.v(TAG, "onConfigCompleted: configRequest=" + completedConfig);
+
+ Message msg = mHandler.obtainMessage(LISTEN_CONFIG_COMPLETED);
+ msg.obj = completedConfig;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onConfigFailed(int reason) {
+ if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason);
+
+ Message msg = mHandler.obtainMessage(LISTEN_CONFIG_FAILED);
+ msg.arg1 = reason;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onNanDown(int reason) {
+ if (VDBG) Log.v(TAG, "onNanDown: reason=" + reason);
+
+ Message msg = mHandler.obtainMessage(LISTEN_NAN_DOWN);
+ msg.arg1 = reason;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onIdentityChanged() {
+ if (VDBG) Log.v(TAG, "onIdentityChanged");
+
+ Message msg = mHandler.obtainMessage(LISTEN_IDENTITY_CHANGED);
+ mHandler.sendMessage(msg);
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
new file mode 100644
index 0000000..877f993
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * This class provides the primary API for managing Wi-Fi NAN operation:
+ * including discovery and data-links. Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(String)
+ * Context.getSystemService(Context.WIFI_NAN_SERVICE)}.
+ * <p>
+ * The class provides access to:
+ * <ul>
+ * <li>Configure a NAN connection and register for events.
+ * <li>Create publish and subscribe sessions.
+ * <li>Create NAN network specifier to be used to create a NAN network.
+ * </ul>
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanManager {
+ private static final String TAG = "WifiNanManager";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ private IBinder mBinder;
+
+ private IWifiNanManager mService;
+
+ /**
+ * {@hide}
+ */
+ public WifiNanManager(IWifiNanManager service) {
+ mService = service;
+ }
+
+ /**
+ * Re-connect to the Wi-Fi NAN service - enabling the application to execute
+ * {@link WifiNanManager} APIs. Application don't normally need to call this
+ * API since it is executed in the constructor. However, applications which
+ * have explicitly {@link WifiNanManager#disconnect()} need to call this
+ * function to re-connect.
+ *
+ * @param listener A listener extended from {@link WifiNanEventListener}.
+ * @param events The set of events to be delivered to the {@code listener}.
+ * OR'd event flags from {@link WifiNanEventListener
+ * NanEventListener.LISTEN*}.
+ */
+ public void connect(WifiNanEventListener listener, int events) {
+ try {
+ if (VDBG) Log.v(TAG, "connect()");
+ if (listener == null) {
+ throw new IllegalArgumentException("Invalid listener - must not be null");
+ }
+ if (mBinder == null) {
+ mBinder = new Binder();
+ }
+ mService.connect(mBinder, listener.callback, events);
+ } catch (RemoteException e) {
+ Log.w(TAG, "connect RemoteException (FYI - ignoring): " + e);
+ }
+ }
+
+ /**
+ * Disconnect from the Wi-Fi NAN service and destroy all outstanding
+ * operations - i.e. all publish and subscribes are terminated, any
+ * outstanding data-link is shut-down, and all requested NAN configurations
+ * are cancelled.
+ * <p>
+ * An application may then re-connect using
+ * {@link WifiNanManager#connect(WifiNanEventListener, int)} .
+ */
+ public void disconnect() {
+ try {
+ if (VDBG) Log.v(TAG, "disconnect()");
+ mService.disconnect(mBinder);
+ mBinder = null;
+ } catch (RemoteException e) {
+ Log.w(TAG, "disconnect RemoteException (FYI - ignoring): " + e);
+ }
+ }
+
+ /**
+ * Requests a NAN configuration, specified by {@link ConfigRequest}. Note
+ * that NAN is a shared resource and the device can only be a member of a
+ * single cluster. Thus the service may merge configuration requests from
+ * multiple applications and configure NAN differently from individual
+ * requests.
+ * <p>
+ * The {@link WifiNanEventListener#onConfigCompleted(ConfigRequest)} will be
+ * called when configuration is completed (if a listener is registered for
+ * this specific event).
+ *
+ * @param configRequest The requested NAN configuration.
+ */
+ public void requestConfig(ConfigRequest configRequest) {
+ if (VDBG) Log.v(TAG, "requestConfig(): configRequest=" + configRequest);
+ try {
+ mService.requestConfig(configRequest);
+ } catch (RemoteException e) {
+ Log.w(TAG, "requestConfig RemoteException (FYI - ignoring): " + e);
+ }
+ }
+
+ /**
+ * Request a NAN publish session. The results of the publish session
+ * operation will result in callbacks to the indicated listener:
+ * {@link WifiNanSessionListener NanSessionListener.on*}.
+ *
+ * @param publishData The {@link PublishData} specifying the contents of the
+ * publish session.
+ * @param publishSettings The {@link PublishSettings} specifying the
+ * settings for the publish session.
+ * @param listener The {@link WifiNanSessionListener} derived objects to be used
+ * for the event callbacks specified by {@code events}.
+ * @param events The list of events to be delivered to the {@code listener}
+ * object. An OR'd value of {@link WifiNanSessionListener
+ * NanSessionListener.LISTEN_*}.
+ * @return The {@link WifiNanPublishSession} which can be used to further
+ * control the publish session.
+ */
+ public WifiNanPublishSession publish(PublishData publishData, PublishSettings publishSettings,
+ WifiNanSessionListener listener, int events) {
+ return publishRaw(publishData, publishSettings, listener,
+ events | WifiNanSessionListener.LISTEN_HIDDEN_FLAGS);
+ }
+
+ /**
+ * Same as publish(*) but does not modify the event flag
+ *
+ * @hide
+ */
+ public WifiNanPublishSession publishRaw(PublishData publishData,
+ PublishSettings publishSettings, WifiNanSessionListener listener, int events) {
+ if (VDBG) Log.v(TAG, "publish(): data='" + publishData + "', settings=" + publishSettings);
+
+ if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_UNSOLICITED
+ && publishData.mRxFilterLength != 0) {
+ throw new IllegalArgumentException("Invalid publish data & settings: UNSOLICITED "
+ + "publishes (active) can't have an Rx filter");
+ }
+ if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_SOLICITED
+ && publishData.mTxFilterLength != 0) {
+ throw new IllegalArgumentException("Invalid publish data & settings: SOLICITED "
+ + "publishes (passive) can't have a Tx filter");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("Invalid listener - must not be null");
+ }
+
+ int sessionId;
+
+ try {
+ sessionId = mService.createSession(listener.callback, events);
+ if (DBG) Log.d(TAG, "publish: session created - sessionId=" + sessionId);
+ mService.publish(sessionId, publishData, publishSettings);
+ } catch (RemoteException e) {
+ Log.w(TAG, "createSession/publish RemoteException: " + e);
+ return null;
+ }
+
+ return new WifiNanPublishSession(this, sessionId);
+ }
+
+ /**
+ * {@hide}
+ */
+ public void publish(int sessionId, PublishData publishData, PublishSettings publishSettings) {
+ if (VDBG) Log.v(TAG, "publish(): data='" + publishData + "', settings=" + publishSettings);
+
+ if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_UNSOLICITED
+ && publishData.mRxFilterLength != 0) {
+ throw new IllegalArgumentException("Invalid publish data & settings: UNSOLICITED "
+ + "publishes (active) can't have an Rx filter");
+ }
+ if (publishSettings.mPublishType == PublishSettings.PUBLISH_TYPE_SOLICITED
+ && publishData.mTxFilterLength != 0) {
+ throw new IllegalArgumentException("Invalid publish data & settings: SOLICITED "
+ + "publishes (passive) can't have a Tx filter");
+ }
+
+ try {
+ mService.publish(sessionId, publishData, publishSettings);
+ } catch (RemoteException e) {
+ Log.w(TAG, "publish RemoteException: " + e);
+ }
+ }
+ /**
+ * Request a NAN subscribe session. The results of the subscribe session
+ * operation will result in callbacks to the indicated listener:
+ * {@link WifiNanSessionListener NanSessionListener.on*}.
+ *
+ * @param subscribeData The {@link SubscribeData} specifying the contents of
+ * the subscribe session.
+ * @param subscribeSettings The {@link SubscribeSettings} specifying the
+ * settings for the subscribe session.
+ * @param listener The {@link WifiNanSessionListener} derived objects to be used
+ * for the event callbacks specified by {@code events}.
+ * @param events The list of events to be delivered to the {@code listener}
+ * object. An OR'd value of {@link WifiNanSessionListener
+ * NanSessionListener.LISTEN_*}.
+ * @return The {@link WifiNanSubscribeSession} which can be used to further
+ * control the subscribe session.
+ */
+ public WifiNanSubscribeSession subscribe(SubscribeData subscribeData,
+ SubscribeSettings subscribeSettings,
+ WifiNanSessionListener listener, int events) {
+ return subscribeRaw(subscribeData, subscribeSettings, listener,
+ events | WifiNanSessionListener.LISTEN_HIDDEN_FLAGS);
+ }
+
+ /**
+ * Same as subscribe(*) but does not modify the event flag
+ *
+ * @hide
+ */
+ public WifiNanSubscribeSession subscribeRaw(SubscribeData subscribeData,
+ SubscribeSettings subscribeSettings, WifiNanSessionListener listener, int events) {
+ if (VDBG) {
+ Log.v(TAG, "subscribe(): data='" + subscribeData + "', settings=" + subscribeSettings);
+ }
+
+ if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_ACTIVE
+ && subscribeData.mRxFilterLength != 0) {
+ throw new IllegalArgumentException(
+ "Invalid subscribe data & settings: ACTIVE subscribes can't have an Rx filter");
+ }
+ if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE
+ && subscribeData.mTxFilterLength != 0) {
+ throw new IllegalArgumentException(
+ "Invalid subscribe data & settings: PASSIVE subscribes can't have a Tx filter");
+ }
+
+ int sessionId;
+
+ try {
+ sessionId = mService.createSession(listener.callback, events);
+ if (DBG) Log.d(TAG, "subscribe: session created - sessionId=" + sessionId);
+ mService.subscribe(sessionId, subscribeData, subscribeSettings);
+ } catch (RemoteException e) {
+ Log.w(TAG, "createSession/subscribe RemoteException: " + e);
+ return null;
+ }
+
+ return new WifiNanSubscribeSession(this, sessionId);
+ }
+
+ /**
+ * {@hide}
+ */
+ public void subscribe(int sessionId, SubscribeData subscribeData,
+ SubscribeSettings subscribeSettings) {
+ if (VDBG) {
+ Log.v(TAG, "subscribe(): data='" + subscribeData + "', settings=" + subscribeSettings);
+ }
+
+ if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_ACTIVE
+ && subscribeData.mRxFilterLength != 0) {
+ throw new IllegalArgumentException(
+ "Invalid subscribe data & settings: ACTIVE subscribes can't have an Rx filter");
+ }
+ if (subscribeSettings.mSubscribeType == SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE
+ && subscribeData.mTxFilterLength != 0) {
+ throw new IllegalArgumentException(
+ "Invalid subscribe data & settings: PASSIVE subscribes can't have a Tx filter");
+ }
+
+ try {
+ mService.subscribe(sessionId, subscribeData, subscribeSettings);
+ } catch (RemoteException e) {
+ Log.w(TAG, "subscribe RemoteException: " + e);
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public void stopSession(int sessionId) {
+ if (DBG) Log.d(TAG, "Stop NAN session #" + sessionId);
+
+ try {
+ mService.stopSession(sessionId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "stopSession RemoteException (FYI - ignoring): " + e);
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public void destroySession(int sessionId) {
+ if (DBG) Log.d(TAG, "Destroy NAN session #" + sessionId);
+
+ try {
+ mService.destroySession(sessionId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "destroySession RemoteException (FYI - ignoring): " + e);
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength) {
+ try {
+ if (VDBG) {
+ Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId
+ + ", messageLength=" + messageLength);
+ }
+ mService.sendMessage(sessionId, peerId, message, messageLength);
+ } catch (RemoteException e) {
+ Log.w(TAG, "subscribe RemoteException (FYI - ignoring): " + e);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java b/wifi/java/android/net/wifi/nan/WifiNanPublishSession.java
new file mode 100644
index 0000000..81b38f4
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanPublishSession.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 android.net.wifi.nan;
+
+/**
+ * A representation of a NAN publish session. Created when
+ * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
+ * is executed. The object can be used to stop and re-start (re-configure) the
+ * publish session.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanPublishSession extends WifiNanSession {
+ /**
+ * {@hide}
+ */
+ public WifiNanPublishSession(WifiNanManager manager, int sessionId) {
+ super(manager, sessionId);
+ }
+
+ /**
+ * Restart/re-configure the publish session. Note that the
+ * {@link WifiNanSessionListener} is not replaced - the same listener used at
+ * creation is still used.
+ *
+ * @param publishData The data ({@link PublishData}) to publish.
+ * @param publishSettings The settings ({@link PublishSettings}) of the
+ * publish session.
+ */
+ public void publish(PublishData publishData, PublishSettings publishSettings) {
+ mManager.publish(mSessionId, publishData, publishSettings);
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
new file mode 100644
index 0000000..c6b384e
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.util.Log;
+
+/**
+ * A representation of a single publish or subscribe NAN session. This object
+ * will not be created directly - only its child classes are available:
+ * {@link WifiNanPublishSession} and {@link WifiNanSubscribeSession}.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanSession {
+ private static final String TAG = "WifiNanSession";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ /**
+ * {@hide}
+ */
+ protected WifiNanManager mManager;
+
+ /**
+ * {@hide}
+ */
+ protected int mSessionId;
+
+ /**
+ * {@hide}
+ */
+ private boolean mDestroyed;
+
+ /**
+ * {@hide}
+ */
+ public WifiNanSession(WifiNanManager manager, int sessionId) {
+ if (VDBG) Log.v(TAG, "New client created: manager=" + manager + ", sessionId=" + sessionId);
+
+ mManager = manager;
+ mSessionId = sessionId;
+ mDestroyed = false;
+ }
+
+ /**
+ * Terminate the current publish or subscribe session - i.e. stop
+ * transmitting packet on-air (for an active session) or listening for
+ * matches (for a passive session). Note that the session may still receive
+ * incoming messages and may be re-configured/re-started at a later time.
+ */
+ public void stop() {
+ mManager.stopSession(mSessionId);
+ }
+
+ /**
+ * Destroy the current publish or subscribe session. Performs a
+ * {@link WifiNanSession#stop()} function but in addition destroys the session -
+ * it will not be able to receive any messages or to be restarted at a later
+ * time.
+ */
+ public void destroy() {
+ mManager.destroySession(mSessionId);
+ mDestroyed = true;
+ }
+
+ /**
+ * {@hide}
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ if (!mDestroyed) {
+ Log.w(TAG, "WifiNanSession mSessionId=" + mSessionId
+ + " was not explicitly destroyed. The session may use resources until "
+ + "destroyed so step should be done explicitly");
+ }
+ destroy();
+ }
+
+ /**
+ * Sends a message to the specified destination. Message transmission is
+ * part of the current discovery session - i.e. executed subsequent to a
+ * publish/subscribe
+ * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}
+ * event.
+ *
+ * @param peerId The peer's ID for the message. Must be a result of an
+ * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}
+ * event.
+ * @param message The message to be transmitted.
+ * @param messageLength The number of bytes from the {@code message} to be
+ * transmitted.
+ */
+ public void sendMessage(int peerId, byte[] message, int messageLength) {
+ mManager.sendMessage(mSessionId, peerId, message, messageLength);
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
new file mode 100644
index 0000000..c9d08c7
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.nan;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * Base class for NAN session events callbacks. Should be extended by
+ * applications wanting notifications. The callbacks are registered when a
+ * publish or subscribe session is created using
+ * {@link WifiNanManager#publish(PublishData, PublishSettings, WifiNanSessionListener, int)}
+ * or
+ * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
+ * . These are callbacks applying to a specific NAN session. Events
+ * corresponding to the NAN link are delivered using {@link WifiNanEventListener}.
+ * <p>
+ * A single listener is registered at session creation - it cannot be replaced.
+ * <p>
+ * During registration specify which specific events are desired using a set of
+ * {@code NanSessionListener.LISTEN_*} flags OR'd together. Only those events
+ * will be delivered to the registered listener. Override those callbacks
+ * {@code NanSessionListener.on*} for the registered events.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanSessionListener {
+ private static final String TAG = "WifiNanSessionListener";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ /**
+ * Publish fail callback event registration flag. Corresponding callback is
+ * {@link WifiNanSessionListener#onPublishFail(int)}.
+ *
+ * @hide
+ */
+ public static final int LISTEN_PUBLISH_FAIL = 0x1 << 0;
+
+ /**
+ * Publish terminated callback event registration flag. Corresponding
+ * callback is {@link WifiNanSessionListener#onPublishTerminated(int)}.
+ */
+ public static final int LISTEN_PUBLISH_TERMINATED = 0x1 << 1;
+
+ /**
+ * Subscribe fail callback event registration flag. Corresponding callback
+ * is {@link WifiNanSessionListener#onSubscribeFail(int)}.
+ *
+ * @hide
+ */
+ public static final int LISTEN_SUBSCRIBE_FAIL = 0x1 << 2;
+
+ /**
+ * Subscribe terminated callback event registration flag. Corresponding
+ * callback is {@link WifiNanSessionListener#onSubscribeTerminated(int)}.
+ */
+ public static final int LISTEN_SUBSCRIBE_TERMINATED = 0x1 << 3;
+
+ /**
+ * Match (discovery: publish or subscribe) callback event registration flag.
+ * Corresponding callback is
+ * {@link WifiNanSessionListener#onMatch(int, byte[], int, byte[], int)}.
+ *
+ * @hide
+ */
+ public static final int LISTEN_MATCH = 0x1 << 4;
+
+ /**
+ * Message sent successfully callback event registration flag. Corresponding
+ * callback is {@link WifiNanSessionListener#onMessageSendSuccess()}.
+ *
+ * @hide
+ */
+ public static final int LISTEN_MESSAGE_SEND_SUCCESS = 0x1 << 5;
+
+ /**
+ * Message sending failure callback event registration flag. Corresponding
+ * callback is {@link WifiNanSessionListener#onMessageSendFail(int)}.
+ *
+ * @hide
+ */
+ public static final int LISTEN_MESSAGE_SEND_FAIL = 0x1 << 6;
+
+ /**
+ * Message received callback event registration flag. Corresponding callback
+ * is {@link WifiNanSessionListener#onMessageReceived(int, byte[], int)}.
+ *
+ * @hide
+ */
+ public static final int LISTEN_MESSAGE_RECEIVED = 0x1 << 7;
+
+ /**
+ * List of hidden events: which are mandatory - i.e. they will be added to
+ * every request.
+ *
+ * @hide
+ */
+ public static final int LISTEN_HIDDEN_FLAGS = LISTEN_PUBLISH_FAIL | LISTEN_SUBSCRIBE_FAIL
+ | LISTEN_MATCH | LISTEN_MESSAGE_SEND_SUCCESS | LISTEN_MESSAGE_SEND_FAIL
+ | LISTEN_MESSAGE_RECEIVED;
+
+ /**
+ * Failure reason flag for {@link WifiNanEventListener} and
+ * {@link WifiNanSessionListener} callbacks. Indicates no resources to execute
+ * the requested operation.
+ */
+ public static final int FAIL_REASON_NO_RESOURCES = 0;
+
+ /**
+ * Failure reason flag for {@link WifiNanEventListener} and
+ * {@link WifiNanSessionListener} callbacks. Indicates invalid argument in the
+ * requested operation.
+ */
+ public static final int FAIL_REASON_INVALID_ARGS = 1;
+
+ /**
+ * Failure reason flag for {@link WifiNanEventListener} and
+ * {@link WifiNanSessionListener} callbacks. Indicates a message is transmitted
+ * without a match (i.e. a discovery) occurring first.
+ */
+ public static final int FAIL_REASON_NO_MATCH_SESSION = 2;
+
+ /**
+ * Failure reason flag for {@link WifiNanEventListener} and
+ * {@link WifiNanSessionListener} callbacks. Indicates an unspecified error
+ * occurred during the operation.
+ */
+ public static final int FAIL_REASON_OTHER = 3;
+
+ /**
+ * Failure reason flag for
+ * {@link WifiNanSessionListener#onPublishTerminated(int)} and
+ * {@link WifiNanSessionListener#onSubscribeTerminated(int)} callbacks.
+ * Indicates that publish or subscribe session is done - i.e. all the
+ * requested operations (per {@link PublishSettings} or
+ * {@link SubscribeSettings}) have been executed.
+ */
+ public static final int TERMINATE_REASON_DONE = 0;
+
+ /**
+ * Failure reason flag for
+ * {@link WifiNanSessionListener#onPublishTerminated(int)} and
+ * {@link WifiNanSessionListener#onSubscribeTerminated(int)} callbacks.
+ * Indicates that publish or subscribe session is terminated due to a
+ * failure.
+ */
+ public static final int TERMINATE_REASON_FAIL = 1;
+
+ private static final String MESSAGE_BUNDLE_KEY_PEER_ID = "peer_id";
+ private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
+ private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
+
+ private final Handler mHandler;
+
+ /**
+ * Constructs a {@link WifiNanSessionListener} using the looper of the current
+ * thread. I.e. all callbacks will be delivered on the current thread.
+ */
+ public WifiNanSessionListener() {
+ this(Looper.myLooper());
+ }
+
+ /**
+ * Constructs a {@link WifiNanSessionListener} using the specified looper. I.e.
+ * all callbacks will delivered on the thread of the specified looper.
+ *
+ * @param looper The looper on which to execute the callbacks.
+ */
+ public WifiNanSessionListener(Looper looper) {
+ if (VDBG) Log.v(TAG, "ctor: looper=" + looper);
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
+ switch (msg.what) {
+ case LISTEN_PUBLISH_FAIL:
+ WifiNanSessionListener.this.onPublishFail(msg.arg1);
+ break;
+ case LISTEN_PUBLISH_TERMINATED:
+ WifiNanSessionListener.this.onPublishTerminated(msg.arg1);
+ break;
+ case LISTEN_SUBSCRIBE_FAIL:
+ WifiNanSessionListener.this.onSubscribeFail(msg.arg1);
+ break;
+ case LISTEN_SUBSCRIBE_TERMINATED:
+ WifiNanSessionListener.this.onSubscribeTerminated(msg.arg1);
+ break;
+ case LISTEN_MATCH:
+ WifiNanSessionListener.this.onMatch(
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_PEER_ID),
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), msg.arg1,
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2), msg.arg2);
+ break;
+ case LISTEN_MESSAGE_SEND_SUCCESS:
+ WifiNanSessionListener.this.onMessageSendSuccess();
+ break;
+ case LISTEN_MESSAGE_SEND_FAIL:
+ WifiNanSessionListener.this.onMessageSendFail(msg.arg1);
+ break;
+ case LISTEN_MESSAGE_RECEIVED:
+ WifiNanSessionListener.this.onMessageReceived(msg.arg2,
+ msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), msg.arg1);
+ break;
+ }
+ }
+ };
+ }
+
+ /**
+ * Called when a publish operation fails. It is dummy method (empty
+ * implementation printing out a log message). Override to implement your
+ * custom response.
+ *
+ * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
+ * codes.
+ */
+ public void onPublishFail(int reason) {
+ if (VDBG) Log.v(TAG, "onPublishFail: called in stub - override if interested");
+ }
+
+ /**
+ * Called when a publish operation terminates. Event will only be delivered
+ * if registered using {@link WifiNanSessionListener#LISTEN_PUBLISH_TERMINATED}.
+ * A dummy (empty implementation printing out a warning). Make sure to
+ * override if registered.
+ *
+ * @param reason The termination reason using
+ * {@code NanSessionListener.TERMINATE_*} codes.
+ */
+ public void onPublishTerminated(int reason) {
+ Log.w(TAG, "onPublishTerminated: called in stub - override if interested or disable");
+ }
+
+ /**
+ * Called when a subscribe operation fails. It is dummy method (empty
+ * implementation printing out a log message). Override to implement your
+ * custom response.
+ *
+ * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
+ * codes.
+ */
+ public void onSubscribeFail(int reason) {
+ if (VDBG) Log.v(TAG, "onSubscribeFail: called in stub - override if interested");
+ }
+
+ /**
+ * Called when a subscribe operation terminates. Event will only be
+ * delivered if registered using
+ * {@link WifiNanSessionListener#LISTEN_SUBSCRIBE_TERMINATED}. A dummy (empty
+ * implementation printing out a warning). Make sure to override if
+ * registered.
+ *
+ * @param reason The termination reason using
+ * {@code NanSessionListener.TERMINATE_*} codes.
+ */
+ public void onSubscribeTerminated(int reason) {
+ Log.w(TAG, "onSubscribeTerminated: called in stub - override if interested or disable");
+ }
+
+ /**
+ * Called when a discovery (publish or subscribe) operation results in a
+ * match - i.e. when a peer is discovered. It is dummy method (empty
+ * implementation printing out a log message). Override to implement your
+ * custom response.
+ *
+ * @param peerId The ID of the peer matching our discovery operation.
+ * @param serviceSpecificInfo The service specific information (arbitrary
+ * byte array) provided by the peer as part of its discovery
+ * packet.
+ * @param serviceSpecificInfoLength The length of the service specific
+ * information array.
+ * @param matchFilter The filter (Tx on advertiser and Rx on listener) which
+ * resulted in this match.
+ * @param matchFilterLength The length of the match filter array.
+ */
+ public void onMatch(int peerId, byte[] serviceSpecificInfo,
+ int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
+ if (VDBG) Log.v(TAG, "onMatch: called in stub - override if interested");
+ }
+
+ /**
+ * Called when a message is transmitted successfully - i.e. when we know
+ * that it was received successfully (corresponding to an ACK being
+ * received). It is dummy method (empty implementation printing out a log
+ * message). Override to implement your custom response.
+ * <p>
+ * Note that either this callback or
+ * {@link WifiNanSessionListener#onMessageSendFail(int)} will be received -
+ * never both.
+ */
+ public void onMessageSendSuccess() {
+ if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested");
+ }
+
+ /**
+ * Called when a message transmission fails - i.e. when no ACK is received.
+ * The hardware will usually attempt to re-transmit several times - this
+ * event is received after all retries are exhausted. There is a possibility
+ * that message was received by the destination successfully but the ACK was
+ * lost. It is dummy method (empty implementation printing out a log
+ * message). Override to implement your custom response.
+ * <p>
+ * Note that either this callback or
+ * {@link WifiNanSessionListener#onMessageSendSuccess()} will be received -
+ * never both
+ *
+ * @param reason The failure reason using {@code NanSessionListener.FAIL_*}
+ * codes.
+ */
+ public void onMessageSendFail(int reason) {
+ if (VDBG) Log.v(TAG, "onMessageSendFail: called in stub - override if interested");
+ }
+
+ /**
+ * Called when a message is received from a discovery session peer. It is
+ * dummy method (empty implementation printing out a log message). Override
+ * to implement your custom response.
+ *
+ * @param peerId The ID of the peer sending the message.
+ * @param message A byte array containing the message.
+ * @param messageLength The length of the byte array containing the relevant
+ * message bytes.
+ */
+ public void onMessageReceived(int peerId, byte[] message, int messageLength) {
+ if (VDBG) Log.v(TAG, "onMessageReceived: called in stub - override if interested");
+ }
+
+ /**
+ * {@hide}
+ */
+ public IWifiNanSessionListener callback = new IWifiNanSessionListener.Stub() {
+ @Override
+ public void onPublishFail(int reason) {
+ if (VDBG) Log.v(TAG, "onPublishFail: reason=" + reason);
+
+ Message msg = mHandler.obtainMessage(LISTEN_PUBLISH_FAIL);
+ msg.arg1 = reason;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onPublishTerminated(int reason) {
+ if (VDBG) Log.v(TAG, "onPublishResponse: reason=" + reason);
+
+ Message msg = mHandler.obtainMessage(LISTEN_PUBLISH_TERMINATED);
+ msg.arg1 = reason;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onSubscribeFail(int reason) {
+ if (VDBG) Log.v(TAG, "onSubscribeFail: reason=" + reason);
+
+ Message msg = mHandler.obtainMessage(LISTEN_SUBSCRIBE_FAIL);
+ msg.arg1 = reason;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onSubscribeTerminated(int reason) {
+ if (VDBG) Log.v(TAG, "onSubscribeTerminated: reason=" + reason);
+
+ Message msg = mHandler.obtainMessage(LISTEN_SUBSCRIBE_TERMINATED);
+ msg.arg1 = reason;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onMatch(int peerId, byte[] serviceSpecificInfo,
+ int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
+ if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
+
+ Bundle data = new Bundle();
+ data.putInt(MESSAGE_BUNDLE_KEY_PEER_ID, peerId);
+ data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo);
+ data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter);
+
+ Message msg = mHandler.obtainMessage(LISTEN_MATCH);
+ msg.arg1 = serviceSpecificInfoLength;
+ msg.arg2 = matchFilterLength;
+ msg.setData(data);
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onMessageSendSuccess() {
+ if (VDBG) Log.v(TAG, "onMessageSendSuccess");
+
+ Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_SUCCESS);
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onMessageSendFail(int reason) {
+ if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason);
+
+ Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_SEND_FAIL);
+ msg.arg1 = reason;
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onMessageReceived(int peerId, byte[] message, int messageLength) {
+ if (VDBG) {
+ Log.v(TAG, "onMessageReceived: peerId='" + peerId + "', messageLength="
+ + messageLength);
+ }
+
+ Bundle data = new Bundle();
+ data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message);
+
+ Message msg = mHandler.obtainMessage(LISTEN_MESSAGE_RECEIVED);
+ msg.arg1 = messageLength;
+ msg.arg2 = peerId;
+ msg.setData(data);
+ mHandler.sendMessage(msg);
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java b/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.java
new file mode 100644
index 0000000..7dfdd32
--- /dev/null
+++ b/wifi/java/android/net/wifi/nan/WifiNanSubscribeSession.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 android.net.wifi.nan;
+
+/**
+ * A representation of a NAN subscribe session. Created when
+ * {@link WifiNanManager#subscribe(SubscribeData, SubscribeSettings, WifiNanSessionListener, int)}
+ * is executed. The object can be used to stop and re-start (re-configure) the
+ * subscribe session.
+ *
+ * @hide PROPOSED_NAN_API
+ */
+public class WifiNanSubscribeSession extends WifiNanSession {
+ /**
+ * {@hide}
+ */
+ public WifiNanSubscribeSession(WifiNanManager manager, int sessionId) {
+ super(manager, sessionId);
+ }
+
+ /**
+ * Restart/re-configure the subscribe session. Note that the
+ * {@link WifiNanSessionListener} is not replaced - the same listener used at
+ * creation is still used.
+ *
+ * @param subscribeData The data ({@link SubscribeData}) to subscribe.
+ * @param subscribeSettings The settings ({@link SubscribeSettings}) of the
+ * subscribe session.
+ */
+ public void subscribe(SubscribeData subscribeData, SubscribeSettings subscribeSettings) {
+ mManager.subscribe(mSessionId, subscribeData, subscribeSettings);
+ }
+}