diff options
757 files changed, 19135 insertions, 3842 deletions
diff --git a/Android.mk b/Android.mk index c1c74ea04e55..282b2af68ae4 100644 --- a/Android.mk +++ b/Android.mk @@ -209,6 +209,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/IBatteryPropertiesRegistrar.aidl \ core/java/android/os/ICancellationSignal.aidl \ core/java/android/os/IDeviceIdleController.aidl \ + core/java/android/os/IMaintenanceActivityListener.aidl \ core/java/android/os/IMessenger.aidl \ core/java/android/os/INetworkActivityListener.aidl \ core/java/android/os/INetworkManagementService.aidl \ @@ -341,10 +342,12 @@ LOCAL_SRC_FILES += \ 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 +410,14 @@ LOCAL_SRC_FILES += \ 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 +428,9 @@ LOCAL_SRC_FILES += \ 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 +488,11 @@ aidl_files := \ 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 +535,6 @@ aidl_files := \ 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 +642,7 @@ aidl_files := \ 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 e7c25e469adf..c7bd93bbaf91 100644 --- a/api/current.txt +++ b/api/current.txt @@ -524,6 +524,7 @@ package android { 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 @@ package android.accounts { 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 @@ package android.accounts { 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 @@ package android.accounts { 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 @@ package android.accounts { 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 @@ package android.app { 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 @@ package android.app { 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 @@ package android.app.admin { 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(); @@ -5850,6 +5846,7 @@ package android.app.admin { 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); @@ -5905,6 +5902,7 @@ package android.app.admin { 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 +7201,15 @@ package android.bluetooth { 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 { @@ -20371,8 +20378,6 @@ package android.media { 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"; @@ -22559,7 +22564,7 @@ package android.mtp { 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); @@ -29383,6 +29388,22 @@ package android.provider { 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); @@ -33504,6 +33525,7 @@ package android.service.dreams { 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(); @@ -33598,6 +33620,7 @@ package android.service.notification { 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"; } @@ -33725,17 +33748,24 @@ package android.service.quicksettings { 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(); @@ -33744,6 +33774,8 @@ package android.service.quicksettings { 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 @@ -38100,9 +38132,11 @@ package android.text.style { public class LocaleSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan { ctor public LocaleSpan(java.util.Locale); + ctor public LocaleSpan(android.util.LocaleList); ctor public LocaleSpan(android.os.Parcel); method public int describeContents(); method public java.util.Locale getLocale(); + method public android.util.LocaleList getLocales(); method public int getSpanTypeId(); method public void updateDrawState(android.text.TextPaint); method public void updateMeasureState(android.text.TextPaint); @@ -38208,7 +38242,8 @@ package android.text.style { 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); @@ -40392,6 +40427,27 @@ package android.view { 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); @@ -42393,6 +42449,7 @@ package android.view { 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); @@ -43593,6 +43650,7 @@ package android.view.inputmethod { } 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(); diff --git a/api/system-current.txt b/api/system-current.txt index 237690566cd6..3267b95b787a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -619,6 +619,7 @@ package android { 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 @@ package android.app { 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 @@ package android.app { 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 @@ package android.app.admin { 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(); @@ -5985,6 +5989,7 @@ package android.app.admin { 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); @@ -6040,6 +6045,7 @@ package android.app.admin { 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"; @@ -7427,6 +7433,15 @@ package android.bluetooth { 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 { @@ -21670,8 +21685,6 @@ package android.media { 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"; @@ -24106,7 +24119,7 @@ package android.mtp { 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); @@ -31388,6 +31401,22 @@ package android.provider { 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); @@ -35642,6 +35671,7 @@ package android.service.dreams { 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(); @@ -35736,6 +35766,7 @@ package android.service.notification { 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"; } @@ -35895,17 +35926,24 @@ package android.service.quicksettings { 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(); @@ -35915,6 +35953,8 @@ package android.service.quicksettings { 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 @@ -40481,9 +40521,11 @@ package android.text.style { public class LocaleSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan { ctor public LocaleSpan(java.util.Locale); + ctor public LocaleSpan(android.util.LocaleList); ctor public LocaleSpan(android.os.Parcel); method public int describeContents(); method public java.util.Locale getLocale(); + method public android.util.LocaleList getLocales(); method public int getSpanTypeId(); method public void updateDrawState(android.text.TextPaint); method public void updateMeasureState(android.text.TextPaint); @@ -40589,7 +40631,8 @@ package android.text.style { 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); @@ -42773,6 +42816,27 @@ package android.view { 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); @@ -44775,6 +44839,7 @@ package android.view { 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); @@ -45977,6 +46042,7 @@ package android.view.inputmethod { } 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(); diff --git a/api/test-current.txt b/api/test-current.txt index 5f1f63f37745..92b16809e46c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -524,6 +524,7 @@ package android { 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 @@ package android.accounts { 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 @@ package android.accounts { 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 @@ package android.accounts { 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 @@ package android.accounts { 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 @@ package android.app { 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 @@ package android.app { 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 @@ package android.app.admin { 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(); @@ -5852,6 +5848,7 @@ package android.app.admin { 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); @@ -5907,6 +5904,7 @@ package android.app.admin { 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"; @@ -7205,6 +7203,15 @@ package android.bluetooth { 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 { @@ -20379,8 +20386,6 @@ package android.media { 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"; @@ -22567,7 +22572,7 @@ package android.mtp { 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); @@ -29395,6 +29400,22 @@ package android.provider { 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); @@ -33518,6 +33539,7 @@ package android.service.dreams { 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(); @@ -33612,6 +33634,7 @@ package android.service.notification { 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"; } @@ -33739,17 +33762,24 @@ package android.service.quicksettings { 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(); @@ -33758,6 +33788,8 @@ package android.service.quicksettings { 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 @@ -38116,9 +38148,11 @@ package android.text.style { public class LocaleSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan { ctor public LocaleSpan(java.util.Locale); + ctor public LocaleSpan(android.util.LocaleList); ctor public LocaleSpan(android.os.Parcel); method public int describeContents(); method public java.util.Locale getLocale(); + method public android.util.LocaleList getLocales(); method public int getSpanTypeId(); method public void updateDrawState(android.text.TextPaint); method public void updateMeasureState(android.text.TextPaint); @@ -38224,7 +38258,8 @@ package android.text.style { 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); @@ -40408,6 +40443,27 @@ package android.view { 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); @@ -42409,6 +42465,7 @@ package android.view { 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); @@ -43609,6 +43666,7 @@ package android.view.inputmethod { } 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(); diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index a312e3f5b6a7..690e674a4812 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -25,6 +25,7 @@ import android.content.pm.PackageManager; 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 @@ public abstract class AbstractAccountAuthenticator { * @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 @@ public abstract class AbstractAccountAuthenticator { * @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 @@ public abstract class AbstractAccountAuthenticator { * </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 ada1ac268fc0..2449ee51c682 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -19,6 +19,7 @@ package android.accounts; 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 @@ public class AccountManager { * 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 @@ public class AccountManager { * trouble * </ul> * @see #finishSession + * @hide */ + @SystemApi public AccountManagerFuture<Bundle> startAddAccountSession( final String accountType, final String authTokenType, @@ -2749,7 +2756,9 @@ public class AccountManager { * trouble * </ul> * @see #finishSession + * @hide */ + @SystemApi public AccountManagerFuture<Bundle> startUpdateCredentialsSession( final Account account, final String authTokenType, @@ -2818,7 +2827,9 @@ public class AccountManager { * 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 7f9a5d3144ad..e721de96540f 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.AccelerateDecelerateInterpolator; 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 class ValueAnimator extends Animator implements AnimationHandler.Animatio * 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 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * * @param value {@link #RESTART} or {@link #REVERSE} */ - public void setRepeatMode(int value) { + public void setRepeatMode(@RepeatMode int value) { mRepeatMode = value; } @@ -816,6 +824,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * * @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 a18bfa5a5702..434a9c700d12 100644 --- a/core/java/android/annotation/IntDef.java +++ b/core/java/android/annotation/IntDef.java @@ -42,7 +42,7 @@ import static java.lang.annotation.RetentionPolicy.CLASS; * 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 34527c2623c6..e31259692501 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -62,6 +62,7 @@ import android.database.Cursor; 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.Handler; 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.Selection; 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.ViewGroup.LayoutParams; 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.ArrayList; 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 class Activity extends ContextThemeWrapper 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 9540ae1cfbf3..2175a9eed90f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -592,6 +592,14 @@ public class ActivityManager { 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 624131ee036f..94b4e7f54ff0 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2842,6 +2842,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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 @@ class ActivityManagerProxy implements IActivityManager 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 81e00ff80e11..4e55c89fa739 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2015,7 +2015,7 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 e28fb2010942..af840d0760e4 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -894,7 +894,9 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { 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/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java index 8d7f347024b2..bbf1607ad529 100644 --- a/core/java/android/app/DatePickerDialog.java +++ b/core/java/android/app/DatePickerDialog.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.os.Bundle; -import android.text.format.DateUtils; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; @@ -49,19 +48,16 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, private static final String DAY = "day"; private final DatePicker mDatePicker; - private final Calendar mCalendar; private OnDateSetListener mDateSetListener; - private boolean mTitleNeedsUpdate = true; - /** * Creates a new date picker dialog for the current date using the parent * context's default date picker dialog theme. * * @param context the parent context */ - public DatePickerDialog(Context context) { + public DatePickerDialog(@NonNull Context context) { this(context, 0); } @@ -73,7 +69,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, * this dialog, or {@code 0} to use the parent * {@code context}'s default alert dialog theme */ - public DatePickerDialog(Context context, @StyleRes int themeResId) { + public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId) { super(context, resolveDialogTheme(context, themeResId)); final Context themeContext = getContext(); @@ -85,11 +81,10 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this); setButtonPanelLayoutHint(LAYOUT_HINT_SIDE); - mCalendar = Calendar.getInstance(); - - final int year = mCalendar.get(Calendar.YEAR); - final int monthOfYear = mCalendar.get(Calendar.MONTH); - final int dayOfMonth = mCalendar.get(Calendar.DAY_OF_MONTH); + final Calendar calendar = Calendar.getInstance(); + final int year = calendar.get(Calendar.YEAR); + final int monthOfYear = calendar.get(Calendar.MONTH); + final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); mDatePicker = (DatePicker) view.findViewById(R.id.datePicker); mDatePicker.init(year, monthOfYear, dayOfMonth, this); mDatePicker.setValidationCallback(mValidationCallback); @@ -107,7 +102,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, * @param dayOfMonth the initially selected day of month (1-31, depending * on month) */ - public DatePickerDialog(@Nullable Context context, @Nullable OnDateSetListener listener, + public DatePickerDialog(@NonNull Context context, @Nullable OnDateSetListener listener, int year, int month, int dayOfMonth) { this(context, 0, listener, year, month, dayOfMonth); } @@ -135,7 +130,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, mDatePicker.updateDate(year, month, dayOfMonth); } - static int resolveDialogTheme(@NonNull Context context, @StyleRes int themeResId) { + static @StyleRes int resolveDialogTheme(@NonNull Context context, @StyleRes int themeResId) { if (themeResId == 0) { final TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true); @@ -146,9 +141,8 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, } @Override - public void onDateChanged(DatePicker view, int year, int month, int dayOfMonth) { + public void onDateChanged(@NonNull DatePicker view, int year, int month, int dayOfMonth) { mDatePicker.init(year, month, dayOfMonth, this); - updateTitle(year, month, dayOfMonth); } /** @@ -161,7 +155,7 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, } @Override - public void onClick(DialogInterface dialog, int which) { + public void onClick(@NonNull DialogInterface dialog, int which) { switch (which) { case BUTTON_POSITIVE: if (mDateSetListener != null) { @@ -200,29 +194,6 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener, mDatePicker.updateDate(year, month, dayOfMonth); } - private void updateTitle(int year, int month, int dayOfMonth) { - if (!mDatePicker.getCalendarViewShown()) { - mCalendar.set(Calendar.YEAR, year); - mCalendar.set(Calendar.MONTH, month); - mCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); - - final String title = DateUtils.formatDateTime(mContext, - mCalendar.getTimeInMillis(), - DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_SHOW_WEEKDAY - | DateUtils.FORMAT_SHOW_YEAR - | DateUtils.FORMAT_ABBREV_MONTH - | DateUtils.FORMAT_ABBREV_WEEKDAY); - setTitle(title); - - mTitleNeedsUpdate = true; - } else if (mTitleNeedsUpdate) { - setTitle(R.string.date_picker_dialog_title); - - mTitleNeedsUpdate = false; - } - } - @Override public Bundle onSaveInstanceState() { final Bundle state = super.onSaveInstanceState(); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 6e8e2c44ce37..79461b4863ef 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -21,9 +21,8 @@ import android.annotation.DrawableRes; 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.ContextMenu.ContextMenuInfo; 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 android.view.accessibility.AccessibilityEvent; 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 @@ public class Dialog implements DialogInterface, Window.Callback, } /** + * {@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/IActivityManager.java b/core/java/android/app/IActivityManager.java index 1ae91a65bba2..10885f2c134f 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -589,6 +589,8 @@ public interface IActivityManager extends IInterface { public void setVrMode(IBinder token, boolean enabled) throws RemoteException; + public boolean isAppForeground(int uid) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -960,4 +962,5 @@ public interface IActivityManager extends IInterface { 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 633f6995b986..368b8ef17cc9 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -103,6 +103,7 @@ interface INotificationManager 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 9a3c82036399..faf5b11951a6 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -380,6 +380,18 @@ public class NotificationManager } /** + * @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 89d52f248bde..f3b1175e720e 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -78,6 +78,8 @@ import android.net.wifi.IWifiScanner; 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 @@ final class SystemServiceRegistry { 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 13e27e29ffba..bd10267bc0f5 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -28,6 +28,7 @@ import android.os.ParcelFileDescriptor; 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 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { 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 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { 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 44164158c7b4..56b4249b619d 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 @@ public class UiModeManager { * 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 @@ public class UiModeManager { } /** - * 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 @@ public class UiModeManager { } /** - * @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 @@ public class UiModeManager { } /** - * @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 4de3ceb4792e..45b23d0d1609 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -620,6 +620,21 @@ public class DevicePolicyManager { "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 @@ public class DevicePolicyManager { * 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 @@ public class DevicePolicyManager { } /** + * 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 @@ public class DevicePolicyManager { } /** + * 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. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index d3c32c51798a..7771440dd9f2 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -144,6 +144,9 @@ interface IDevicePolicyManager { 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); @@ -256,4 +259,6 @@ interface IDevicePolicyManager { 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 b899710c4feb..9ad35d4c1556 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -91,6 +91,7 @@ public class JobInfo implements Parcelable { 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 @@ public class JobInfo implements Parcelable { return service; } + /** @hide */ + public int getPriority() { + return priority; + } + /** * Whether this job needs the device to be plugged in. */ @@ -237,6 +243,7 @@ public class JobInfo implements Parcelable { backoffPolicy = in.readInt(); hasEarlyConstraint = in.readInt() == 1; hasLateConstraint = in.readInt() == 1; + priority = in.readInt(); } private JobInfo(JobInfo.Builder b) { @@ -256,6 +263,7 @@ public class JobInfo implements Parcelable { backoffPolicy = b.mBackoffPolicy; hasEarlyConstraint = b.mHasEarlyConstraint; hasLateConstraint = b.mHasLateConstraint; + priority = b.mPriority; } @Override @@ -281,6 +289,7 @@ public class JobInfo implements Parcelable { 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 @@ public class JobInfo implements Parcelable { 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 @@ public class JobInfo implements Parcelable { } /** + * @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 7ee39f5cd939..a0a60e8c53a0 100644 --- a/core/java/android/app/job/JobParameters.java +++ b/core/java/android/app/job/JobParameters.java @@ -28,10 +28,22 @@ import android.os.PersistableBundle; */ 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 @@ public class JobParameters implements Parcelable { } /** + * 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 @@ public class JobParameters implements Parcelable { 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 @@ public class JobParameters implements Parcelable { 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 ee591d37e5f7..88ba874083b5 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 @@ public class TrustManager { * 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 index 2e273459add8..74302f27ec1d 100644..100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -371,6 +371,89 @@ public final class BluetoothA2dpSink implements BluetoothProfile { } /** + * 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 b53a8fc0f6fc..444e4293fe2b 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -20,6 +20,8 @@ import android.content.ComponentName; 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.ArrayList; 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 @@ import java.util.List; */ 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 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * 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 @@ public final class BluetoothAvrcpController implements BluetoothProfile { 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 @@ public final class BluetoothAvrcpController implements BluetoothProfile { 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 000000000000..590fd63eda8c --- /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 000000000000..927cb5666515 --- /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 index 54bf4afa93ff..4a38287e7ca9 100644..100755 --- a/core/java/android/bluetooth/BluetoothClass.java +++ b/core/java/android/bluetooth/BluetoothClass.java @@ -283,6 +283,8 @@ public final class BluetoothClass implements Parcelable { 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 @@ public final class BluetoothClass implements Parcelable { 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 cd5c205d14dc..f43fb30abb18 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -879,18 +879,16 @@ public final class BluetoothDevice implements Parcelable { * * <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 66f3418c620f..74cb0f6cd25c 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -20,6 +20,7 @@ import android.bluetooth.IBluetoothCallback; 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 @@ interface IBluetooth 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 index b7c647677000..d1458246dfe9 100644..100755 --- a/core/java/android/bluetooth/IBluetoothA2dpSink.aidl +++ b/core/java/android/bluetooth/IBluetoothA2dpSink.aidl @@ -31,4 +31,7 @@ interface IBluetoothA2dpSink { 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 f917a50860b7..f1288d022290 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 @@ interface IBluetoothAvrcpController { 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 000000000000..d831c647769b --- /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 000000000000..01f72efb22ac --- /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 84f6f3da3edb..fb2791080636 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -329,6 +329,15 @@ public abstract class Context { 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 @@ public abstract class Context { 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 abstract class Context { 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 35cd2bef6093..c6274367f333 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2537,6 +2537,16 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON"; + /** + * Broadcast Action: The "Picture-in-picture (PIP) Button" was pressed. + * Includes a single extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that + * caused the broadcast. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_PICTURE_IN_PICTURE_BUTTON = + "android.intent.action.PICTURE_IN_PICTURE_BUTTON"; + /** * Broadcast Action: The "Camera Button" was pressed. Includes a single * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that @@ -3034,6 +3044,24 @@ public class Intent implements Parcelable, Cloneable { 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 +4105,34 @@ public class Intent implements Parcelable, Cloneable { * 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/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index f6119917c994..90a1198e3132 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -294,6 +294,8 @@ interface IPackageManager { void restoreDefaultApps(in byte[] backup, int userId); byte[] getIntentFilterVerificationBackup(int userId); void restoreIntentFilterVerification(in byte[] backup, int userId); + byte[] getPermissionGrantBackup(int userId); + void restorePermissionGrants(in byte[] backup, int userId); /** * Report the set of 'Home' activity candidates, plus (if any) which of them diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 5113e1979812..ba4d14c65be4 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1902,6 +1902,16 @@ public abstract class PackageManager { /** * 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 diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index a0df610c7428..a6fec9f3f67b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -51,6 +51,7 @@ import android.util.Log; 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; @@ -1022,11 +1023,56 @@ public class PackageParser { 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 @@ -1036,6 +1082,16 @@ public class PackageParser { "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); @@ -3886,6 +3942,11 @@ public class PackageParser { 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/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java index 74e5c2a65982..eecf0de5afa8 100644 --- a/core/java/android/content/pm/ServiceInfo.java +++ b/core/java/android/content/pm/ServiceInfo.java @@ -49,6 +49,14 @@ public class ServiceInfo extends ComponentInfo 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 e87586424232..6935174f58d6 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -534,6 +534,24 @@ public final class Sensor { 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 @@ public final class Sensor { 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 @@ public final class Sensor { 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 906c2a1941d5..9937b2cb2d26 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -483,6 +483,20 @@ public class SensorEvent { * 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 6e02df1f7131..c3a4954cdbb8 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -89,7 +89,6 @@ public class CameraDeviceImpl extends CameraDevice { 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 @@ public class CameraDeviceImpl extends CameraDevice { 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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 176e923c81b2..c4f0847e694b 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -17,6 +17,7 @@ package android.net; 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; @@ -686,6 +687,47 @@ public class ConnectivityManager { } /** + * 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. diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index ef911373d4c1..569468e19687 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -117,6 +117,8 @@ interface IConnectivityManager VpnInfo[] getAllVpnInfo(); boolean updateLockdownVpn(); + boolean setAlwaysOnVpnPackage(int userId, String packageName); + String getAlwaysOnVpnPackage(int userId); int checkMobileProvisioning(int suggestedTimeOutMs); diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index fdd34f586e8c..e58744bfd905 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -130,6 +130,9 @@ public final class Debug /** 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 @@ public final class Debug /** 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 @@ public final class Debug /** 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 final class Debug 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 final class Debug 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 @@ public final class Debug * Return total PSS memory usage in kB. */ public int getTotalPss() { - return dalvikPss + nativePss + otherPss; + return dalvikPss + nativePss + otherPss + getTotalSwappedOutPss(); } /** @@ -274,7 +289,8 @@ public final class Debug } /** - * 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 @@ public final class Debug 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 @@ public final class Debug } /** @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 @@ public final class Debug * 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 @@ public final class Debug dest.writeInt(otherPrivateClean); dest.writeInt(otherSharedClean); dest.writeInt(otherSwappedOut); + dest.writeInt(hasSwappedOutPss ? 1 : 0); + dest.writeInt(otherSwappedOutPss); dest.writeIntArray(otherStats); } @@ -689,6 +732,8 @@ public final class Debug otherPrivateClean = source.readInt(); otherSharedClean = source.readInt(); otherSwappedOut = source.readInt(); + hasSwappedOutPss = source.readInt() != 0; + otherSwappedOutPss = source.readInt(); otherStats = source.createIntArray(); } @@ -1563,11 +1608,12 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * 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/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl index 7a1a6a29ad12..838279bf38f9 100644 --- a/core/java/android/os/IDeviceIdleController.aidl +++ b/core/java/android/os/IDeviceIdleController.aidl @@ -16,6 +16,7 @@ package android.os; +import android.os.IMaintenanceActivityListener; import android.os.UserHandle; /** @hide */ @@ -37,4 +38,6 @@ interface IDeviceIdleController { void exitIdle(String reason); void downloadServiceActive(IBinder token); void downloadServiceInactive(); + boolean registerMaintenanceActivityListener(IMaintenanceActivityListener listener); + void unregisterMaintenanceActivityListener(IMaintenanceActivityListener listener); } diff --git a/core/java/android/os/IMaintenanceActivityListener.aidl b/core/java/android/os/IMaintenanceActivityListener.aidl new file mode 100644 index 000000000000..6a2581faaa58 --- /dev/null +++ b/core/java/android/os/IMaintenanceActivityListener.aidl @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** @hide */ +oneway interface IMaintenanceActivityListener { + void onMaintenanceActivityChanged(boolean active); +} diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java index 83efe8020d98..ff9c0df58f91 100644 --- a/core/java/android/print/PrinterId.java +++ b/core/java/android/print/PrinterId.java @@ -20,16 +20,17 @@ import android.annotation.NonNull; 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 @@ public final class PrinterId implements Parcelable { * * @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 @@ public final class PrinterId implements Parcelable { * * @hide */ - public ComponentName getServiceName() { + public @NonNull ComponentName getServiceName() { return mServiceName; } @@ -93,14 +94,10 @@ public final class PrinterId implements Parcelable { 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 final class PrinterId implements Parcelable { 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 afef9c0dac27..0d2d9f4946b6 100644 --- a/core/java/android/print/PrinterInfo.java +++ b/core/java/android/print/PrinterInfo.java @@ -33,6 +33,8 @@ import android.os.Parcel; 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 @@ import java.lang.annotation.RetentionPolicy; * 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 @@ public final class PrinterInfo implements Parcelable { /** 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 String mName; + private final @Nullable PendingIntent mInfoIntent; - private int mStatus; + private final @NonNull String mName; - private String mDescription; + private final @Status int mStatus; - private PrinterCapabilitiesInfo mCapabilities; + private final @Nullable String mDescription; - private PrinterInfo() { - /* do nothing */ - } + private final @Nullable PrinterCapabilitiesInfo mCapabilities; - 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 @@ public final class PrinterInfo implements Parcelable { * * @return The printer name. */ - public @Nullable String getName() { + public @NonNull String getName() { return mName; } @@ -227,10 +213,51 @@ public final class PrinterInfo implements Parcelable { 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 final class PrinterInfo implements Parcelable { 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 @@ public final class PrinterInfo implements Parcelable { * @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 @@ public final class PrinterInfo implements Parcelable { * 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 @@ public final class PrinterInfo implements Parcelable { * 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 @@ public final class PrinterInfo implements Parcelable { * @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 @@ public final class PrinterInfo implements Parcelable { * * @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 @@ public final class PrinterInfo implements Parcelable { * @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 @@ public final class PrinterInfo implements Parcelable { * @return This builder. */ public @NonNull Builder setHasCustomPrinterIcon() { - mPrototype.mHasCustomPrinterIcon = true; + mHasCustomPrinterIcon = true; return this; } @@ -454,7 +482,7 @@ public final class PrinterInfo implements Parcelable { * @return This builder. */ public @NonNull Builder setName(@NonNull String name) { - mPrototype.mName = name; + mName = checkName(name); return this; } @@ -466,7 +494,7 @@ public final class PrinterInfo implements Parcelable { * @return This builder. */ public @NonNull Builder setDescription(@NonNull String description) { - mPrototype.mDescription = description; + mDescription = description; return this; } @@ -478,7 +506,7 @@ public final class PrinterInfo implements Parcelable { * @return This builder. */ public @NonNull Builder setInfoIntent(@NonNull PendingIntent infoIntent) { - mPrototype.mInfoIntent = infoIntent; + mInfoIntent = infoIntent; return this; } @@ -489,7 +517,7 @@ public final class PrinterInfo implements Parcelable { * @return This builder. */ public @NonNull Builder setCapabilities(@NonNull PrinterCapabilitiesInfo capabilities) { - mPrototype.mCapabilities = capabilities; + mCapabilities = capabilities; return this; } @@ -499,13 +527,9 @@ public final class PrinterInfo implements Parcelable { * @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 @@ public final class PrinterInfo implements Parcelable { * @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 d0037b762560..310449222a0b 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -30,6 +30,8 @@ import android.print.PrintJobInfo; 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 abstract class PrintService extends Service { */ 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 000000000000..2a9d4ae762e9 --- /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 10432b548c27..b5474329e3e5 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -409,6 +409,20 @@ public final class ContactsContract { "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 index a6485e4d7e4e..3042aa9b6552 100644..100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4429,6 +4429,13 @@ public final class Settings { 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 @@ public final class Settings { 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 @@ public final class Settings { * 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 @@ public final class Settings { } /** + * 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 bb46e8302de6..816ecde84377 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.IBinder; 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 @@ public class DreamService extends Service implements Window.Callback { @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 88bd283d06a1..eff09d62e56e 100644 --- a/core/java/android/service/notification/ConditionProviderService.java +++ b/core/java/android/service/notification/ConditionProviderService.java @@ -85,6 +85,13 @@ public abstract class ConditionProviderService extends Service { "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 9991d416bb5a..4bfc94898f52 100644 --- a/core/java/android/service/quicksettings/IQSService.aidl +++ b/core/java/android/service/quicksettings/IQSService.aidl @@ -27,5 +27,9 @@ interface IQSService { 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 4997f754d2a7..bfde8702338a 100644 --- a/core/java/android/service/quicksettings/IQSTileService.aidl +++ b/core/java/android/service/quicksettings/IQSTileService.aidl @@ -29,4 +29,5 @@ oneway interface IQSTileService { 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 6104913642f8..85f1955e8ebd 100644 --- a/core/java/android/service/quicksettings/Tile.java +++ b/core/java/android/service/quicksettings/Tile.java @@ -36,10 +36,37 @@ public final class Tile implements Parcelable { 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 @@ public final class Tile implements Parcelable { } /** + * 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 @@ public final class Tile implements Parcelable { } else { dest.writeByte((byte) 0); } + dest.writeInt(mState); TextUtils.writeToParcel(mLabel, dest, flags); TextUtils.writeToParcel(mContentDescription, dest, flags); } @@ -180,6 +231,7 @@ public final class Tile implements Parcelable { } 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 6b1219335cb8..c02465b3f555 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -118,6 +118,7 @@ public class TileService extends Service { private Tile mTile; private IBinder mToken; private IQSService mService; + private Runnable mUnlockRunnable; @Override public void onDestroy() { @@ -199,6 +200,8 @@ public class TileService extends Service { * 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 @@ public class TileService extends Service { } /** + * 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 class TileService extends Service { 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 @@ public class TileService extends Service { 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 @@ public class TileService extends Service { 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 f9387b39616f..2c4241b7eceb 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1116,24 +1116,30 @@ public abstract class Layout { 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 @@ public abstract class Layout { low = here + 1; if (low < there) { - low = getOffsetAtStartOf(low); - - 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; + 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); + + 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 @@ public abstract class Layout { best = max; } + TextLine.recycle(tl); return best; } diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index c42c791769e6..2a52961984f7 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -688,13 +688,14 @@ class TextLine { * @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 @@ class TextLine { 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 @@ class TextLine { 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 @@ class TextLine { } 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 @@ class TextLine { 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/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java index d286231bb91f..117de77442fb 100644 --- a/core/java/android/text/style/LocaleSpan.java +++ b/core/java/android/text/style/LocaleSpan.java @@ -16,30 +16,56 @@ package android.text.style; +import com.android.internal.util.Preconditions; + +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Paint; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; import android.text.TextUtils; +import android.util.LocaleList; + import java.util.Locale; /** * Changes the {@link Locale} of the text to which the span is attached. */ public class LocaleSpan extends MetricAffectingSpan implements ParcelableSpan { - private final Locale mLocale; + @NonNull + private final LocaleList mLocales; + + /** + * Creates a {@link LocaleSpan} from a well-formed {@link Locale}. Note that only + * {@link Locale} objects that can be created by {@link Locale#forLanguageTag(String)} are + * supported. + * + * <p><b>Caveat:</b> Do not specify any {@link Locale} object that cannot be created by + * {@link Locale#forLanguageTag(String)}. {@code new Locale(" a ", " b c", " d")} is an + * example of such a malformed {@link Locale} object.</p> + * + * @param locale The {@link Locale} of the text to which the span is attached. + * + * @see #LocaleSpan(LocaleList) + */ + public LocaleSpan(@Nullable Locale locale) { + mLocales = new LocaleList(locale); + } /** - * Creates a LocaleSpan. - * @param locale The {@link Locale} of the text to which the span is - * attached. + * Creates a {@link LocaleSpan} from {@link LocaleList}. + * + * @param locales The {@link LocaleList} of the text to which the span is attached. + * @throws NullPointerException if {@code locales} is null */ - public LocaleSpan(Locale locale) { - mLocale = locale; + public LocaleSpan(@NonNull LocaleList locales) { + Preconditions.checkNotNull(locales, "locales cannot be null"); + mLocales = locales; } - public LocaleSpan(Parcel src) { - mLocale = new Locale(src.readString(), src.readString(), src.readString()); + public LocaleSpan(Parcel source) { + mLocales = LocaleList.CREATOR.createFromParcel(source); } @Override @@ -64,31 +90,40 @@ public class LocaleSpan extends MetricAffectingSpan implements ParcelableSpan { /** @hide */ public void writeToParcelInternal(Parcel dest, int flags) { - dest.writeString(mLocale.getLanguage()); - dest.writeString(mLocale.getCountry()); - dest.writeString(mLocale.getVariant()); + mLocales.writeToParcel(dest, flags); } /** - * Returns the {@link Locale}. + * @return The {@link Locale} for this span. If multiple locales are associated with this + * span, only the first locale is returned. {@code null} if no {@link Locale} is specified. * - * @return The {@link Locale} for this span. + * @see LocaleList#getPrimary() + * @see #getLocales() */ + @Nullable public Locale getLocale() { - return mLocale; + return mLocales.getPrimary(); + } + + /** + * @return The entire list of locales that are associated with this span. + */ + @NonNull + public LocaleList getLocales() { + return mLocales; } @Override public void updateDrawState(TextPaint ds) { - apply(ds, mLocale); + apply(ds, mLocales); } @Override public void updateMeasureState(TextPaint paint) { - apply(paint, mLocale); + apply(paint, mLocales); } - private static void apply(Paint paint, Locale locale) { - paint.setTextLocale(locale); + private static void apply(@NonNull Paint paint, @NonNull LocaleList locales) { + paint.setTextLocales(locales); } } diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index 6b449f9fcb16..1b00db2e4ad3 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 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { 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 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { 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 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { } else { mNotificationTargetClassName = ""; } - mHashCode = hashCodeInternal(mSuggestions, mLocaleString, mNotificationTargetClassName); + mHashCode = hashCodeInternal(mSuggestions, mLanguageTag, mLocaleStringForCompatibility, + mNotificationTargetClassName); initStyle(context); } @@ -194,7 +209,8 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { 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 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { } /** - * @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 class SuggestionSpan extends CharacterStyle implements ParcelableSpan { 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 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { 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 b10f6a035370..817541cb91aa 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 @@ public class SidePropagation extends VisibilityPropagation { * {@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 9063b439ade0..9af65e403679 100644 --- a/core/java/android/transition/Slide.java +++ b/core/java/android/transition/Slide.java @@ -17,6 +17,7 @@ package android.transition; 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.AccelerateInterpolator; 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 @@ public class Slide extends Visibility { 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 @@ public class Slide extends Visibility { * {@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 @@ public class Slide extends Visibility { * {@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 e711812c0138..eb95a02dd75b 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 @@ public abstract class Visibility extends Transition { 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 @@ public abstract class Visibility extends Transition { * {@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 @@ public abstract class Visibility extends Transition { * {@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 000000000000..81c8c458beab --- /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 000000000000..a383d5c151dd --- /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 fd5780612649..302a08d58dd3 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.Set; 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 @@ public final class StrictJarFile { 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 @@ public final class StrictJarFile { // 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 ca2aec105bcf..0546a5f724d6 100644 --- a/core/java/android/util/jar/StrictJarVerifier.java +++ b/core/java/android/util/jar/StrictJarVerifier.java @@ -32,8 +32,12 @@ import java.util.Hashtable; 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 @@ class StrictJarVerifier { 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 9e478c1d1e73..923139406088 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -25,6 +25,8 @@ import android.view.DragEvent; 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 @@ oneway interface IWindow { * 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 84d312d59b64..b045c17a9295 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 @@ interface IWindowManager * @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 f037958f5cfc..51e1f4baae97 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1856,6 +1856,9 @@ public class KeyEvent extends InputEvent implements Parcelable { 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 000000000000..013255b9a612 --- /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 000000000000..2c9006deb189 --- /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 68f1ac3c1108..0b8018b22f1b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -79,10 +79,11 @@ import android.util.StateSet; 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private int[] mDrawableState = null; - /** Whether draw() is currently being called. */ - private boolean mInDraw = false; - ViewOutlineProvider mOutlineProvider = ViewOutlineProvider.BACKGROUND; /** @@ -16473,8 +16471,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, onDrawForeground(canvas); // we're done... - mInDraw = false; return; } @@ -16667,8 +16662,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Step 6, draw decorations (foreground, scrollbars) onDrawForeground(canvas); - - mInDraw = false; } /** @@ -17113,8 +17106,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3eb2e37be781..f8e96d6543c1 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.view.inputmethod.InputMethodManager; 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, 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,17 +1498,21 @@ public final class ViewRootImpl implements ViewParent, 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 - || lp.type == WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)) { + 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); + } } } @@ -3235,6 +3252,7 @@ public final class ViewRootImpl implements ViewParent, 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 @@ -3511,7 +3529,11 @@ public final class ViewRootImpl implements ViewParent, } break; case MSG_DISPATCH_WINDOW_SHOWN: { handleDispatchWindowShown(); - } + } break; + case MSG_REQUEST_KEYBOARD_SHORTCUTS: { + IResultReceiver receiver = (IResultReceiver) msg.obj; + handleRequestKeyboardShortcuts(receiver); + } break; } } } @@ -5404,6 +5426,19 @@ public final class ViewRootImpl implements ViewParent, 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; @@ -6333,6 +6368,10 @@ public final class ViewRootImpl implements ViewParent, } } + 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. @@ -6906,6 +6945,14 @@ public final class ViewRootImpl implements ViewParent, 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 0b06d1591731..d89369bef170 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -42,6 +42,8 @@ import android.transition.Transition; 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 @@ public abstract class Window { * @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 8ce1f8c7e310..bed74e95ae0a 100644 --- a/core/java/android/view/WindowCallbackWrapper.java +++ b/core/java/android/view/WindowCallbackWrapper.java @@ -19,6 +19,8 @@ package android.view; 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 class WindowCallbackWrapper implements Window.Callback { 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 251f4c802ce6..772eeec880ee 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -29,6 +29,8 @@ import android.os.Parcelable; 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 interface WindowManager extends ViewManager { */ 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 98e9f54c13f9..6e11671016b4 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 @@ public final class WindowManagerImpl implements WindowManager { } @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 a78b56ab194c..4a1142f7204f 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1333,9 +1333,9 @@ public interface WindowManagerPolicy { * 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 b6570cc37467..9e7905759f2d 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -58,5 +58,5 @@ interface IAccessibilityManager { 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 e0dbe2f63487..1536c29bee5b 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -850,7 +850,7 @@ public abstract class Animation implements Cloneable { 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 @@ public abstract class Animation implements Cloneable { } if (expired) { - if (mRepeatCount == mRepeated) { + if (mRepeatCount == mRepeated || isCanceled()) { if (!mEnded) { mEnded = true; guard.close(); @@ -905,6 +905,10 @@ public abstract class Animation implements Cloneable { 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 6e5e5915bb6f..bdf89e9f02bc 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -195,7 +195,6 @@ public class BaseInputConnection implements InputConnection { 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 class BaseInputConnection implements InputConnection { 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 @@ public class BaseInputConnection implements InputConnection { * 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 5e0734742e36..9647345c986f 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.IInputMethodSession; 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 @@ public final class InputMethodManager { } } } - + private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper { private final InputMethodManager mParentInputMethodManager; private boolean mActive; @@ -549,13 +551,23 @@ public final class InputMethodManager { } @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 @@ public final class InputMethodManager { 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/webkit/TokenBindingService.java b/core/java/android/webkit/TokenBindingService.java new file mode 100644 index 000000000000..a6d7b4ab0b07 --- /dev/null +++ b/core/java/android/webkit/TokenBindingService.java @@ -0,0 +1,90 @@ +/* + * 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.webkit; + +import android.annotation.SystemApi; +import android.net.Uri; + +import java.security.KeyPair; +import java.security.spec.AlgorithmParameterSpec; + +/** + * Enables the token binding procotol, and provides access to the keys. See + * https://tools.ietf.org/html/draft-ietf-tokbind-protocol-03 + * + * All methods are required to be called on the UI thread where WebView is + * attached to the View hierarchy. + * @hide + */ +public abstract class TokenBindingService { + + public static final String KEY_ALGORITHM_RSA2048_PKCS_1_5 = "RSA2048_PKCS_1.5"; + public static final String KEY_ALGORITHM_RSA2048_PSS = "RSA2048PSS"; + public static final String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256"; + + /** + * Returns the default TokenBinding service instance. At present there is + * only one token binding service instance for all WebView instances, + * however this restriction may be relaxed in the future. + * + * @return The default TokenBindingService instance. + */ + public static TokenBindingService getInstance() { + return WebViewFactory.getProvider().getTokenBindingService(); + } + + /** + * Enables the token binding protocol. The token binding protocol + * has to be enabled before creating any WebViews. + * + * @throws IllegalStateException if a WebView was already created. + */ + public abstract void enableTokenBinding(); + + /** + * Retrieves the key pair for a given origin from the internal + * TokenBinding key store asynchronously. + * Will create a key pair if one does not exist. + * + * @param origin The origin for the server. + * @param algorithm The algorithm for generating the token binding key. + * @param callback The callback that will be called when key is available. + * Cannot be null. + */ + public abstract void getKey(Uri origin, + String algorithm, + ValueCallback<KeyPair> callback); + /** + * Deletes specified key (for use when associated cookie is cleared). + * + * @param origin The origin of the server. + * @param callback The callback that will be called when key is deleted. The + * callback parameter (Boolean) will indicate if operation is + * successful or if failed. The callback can be null. + */ + public abstract void deleteKey(Uri origin, + ValueCallback<Boolean> callback); + + /** + * Deletes all the keys (for use when cookies are cleared). + * + * @param callback The callback that will be called when keys are deleted. + * The callback parameter (Boolean) will indicate if operation is + * successful or if failed. The callback can be null. + */ + public abstract void deleteAllKeys(ValueCallback<Boolean> callback); +} diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index 91053940f3cd..02c911f399a2 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -103,6 +103,15 @@ public interface WebViewFactoryProvider { CookieManager getCookieManager(); /** + * Gets the TokenBindingService instance for this WebView implementation. The + * implementation must return the same instance on subsequent calls. + * + * @return the TokenBindingService instance + * @hide + */ + TokenBindingService getTokenBindingService(); + + /** * Gets the singleton WebIconDatabase instance for this WebView implementation. The * implementation must return the same instance on subsequent calls. * diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 831481ddba45..4ca8971b34ab 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -142,6 +142,7 @@ public abstract class AbsSeekBar extends ProgressBar { } applyThumbTint(); + applyTickMarkTint(); mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java index 48d1d2b1b32f..ac8d578adae8 100644 --- a/core/java/android/widget/ActionMenuPresenter.java +++ b/core/java/android/widget/ActionMenuPresenter.java @@ -59,7 +59,6 @@ import java.util.List; */ 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 @@ public class ActionMenuPresenter extends BaseMenuPresenter 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 class ActionMenuPresenter extends BaseMenuPresenter 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 @@ public class ActionMenuPresenter extends BaseMenuPresenter } 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 2aaa356cb00a..cde7604b7e7c 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.Locale; 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 @@ public class CalendarView extends FrameLayout { * @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 @@ public class CalendarView extends FrameLayout { * * @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 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_weekDayTextAppearance */ - public int getWeekDayTextAppearance() { + public @StyleRes int getWeekDayTextAppearance() { return mDelegate.getWeekDayTextAppearance(); } @@ -344,7 +353,7 @@ public class CalendarView extends FrameLayout { * * @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 @@ public class CalendarView extends FrameLayout { * * @attr ref android.R.styleable#CalendarView_dateTextAppearance */ - public int getDateTextAppearance() { + public @StyleRes int getDateTextAppearance() { return mDelegate.getDateTextAppearance(); } @@ -552,36 +561,29 @@ public class CalendarView extends FrameLayout { 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/DropDownListView.java b/core/java/android/widget/DropDownListView.java index 2fb210124f11..02f7e7a78307 100644 --- a/core/java/android/widget/DropDownListView.java +++ b/core/java/android/widget/DropDownListView.java @@ -19,18 +19,10 @@ package android.widget; import com.android.internal.widget.AutoScrollHelper.AbsListViewAutoScroller; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; +import android.annotation.NonNull; import android.content.Context; -import android.graphics.drawable.Drawable; -import android.util.IntProperty; import android.view.MotionEvent; import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.widget.TextView; -import android.widget.ListView; - /** * Wrapper class for a ListView. This wrapper can hijack the focus to @@ -41,26 +33,6 @@ import android.widget.ListView; * @hide */ public class DropDownListView extends ListView { - /** Duration in milliseconds of the drag-to-open click animation. */ - private static final long CLICK_ANIM_DURATION = 150; - - /** Target alpha value for drag-to-open click animation. */ - private static final int CLICK_ANIM_ALPHA = 0x80; - - /** Wrapper around Drawable's <code>alpha</code> property. */ - private static final IntProperty<Drawable> DRAWABLE_ALPHA = - new IntProperty<Drawable>("alpha") { - @Override - public void setValue(Drawable object, int value) { - object.setAlpha(value); - } - - @Override - public Integer get(Drawable object) { - return object.getAlpha(); - } - }; - /* * WARNING: This is a workaround for a touch mode issue. * @@ -99,9 +71,6 @@ public class DropDownListView extends ListView { /** Whether to force drawing of the pressed state selector. */ private boolean mDrawsInPressedState; - /** Current drag-to-open click animation, if any. */ - private Animator mClickAnimation; - /** Helper for drag-to-open auto scrolling. */ private AbsListViewAutoScroller mScrollHelper; @@ -110,7 +79,7 @@ public class DropDownListView extends ListView { * * @param context this view's context */ - public DropDownListView(Context context, boolean hijackFocus) { + public DropDownListView(@NonNull Context context, boolean hijackFocus) { this(context, hijackFocus, com.android.internal.R.attr.dropDownListViewStyle); } @@ -119,7 +88,7 @@ public class DropDownListView extends ListView { * * @param context this view's context */ - public DropDownListView(Context context, boolean hijackFocus, int defStyleAttr) { + public DropDownListView(@NonNull Context context, boolean hijackFocus, int defStyleAttr) { super(context, null, defStyleAttr); mHijackFocus = hijackFocus; // TODO: Add an API to control this @@ -132,7 +101,7 @@ public class DropDownListView extends ListView { } @Override - public boolean onHoverEvent(MotionEvent ev) { + public boolean onHoverEvent(@NonNull MotionEvent ev) { // Allow the super class to handle hover state management first. final boolean handled = super.onHoverEvent(ev); @@ -169,7 +138,7 @@ public class DropDownListView extends ListView { * @param activePointerId id of the pointer that activated forwarding * @return whether the event was handled */ - public boolean onForwardedEvent(MotionEvent event, int activePointerId) { + public boolean onForwardedEvent(@NonNull MotionEvent event, int activePointerId) { boolean handledEvent = true; boolean clearPressedItem = false; @@ -201,7 +170,8 @@ public class DropDownListView extends ListView { handledEvent = true; if (actionMasked == MotionEvent.ACTION_UP) { - clickPressedItem(child, position); + final long id = getItemIdAtPosition(position); + performItemClick(child, position, id); } break; } @@ -234,30 +204,6 @@ public class DropDownListView extends ListView { this.mListSelectionHidden = listSelectionHidden; } - /** - * Starts an alpha animation on the selector. When the animation ends, - * the list performs a click on the item. - */ - private void clickPressedItem(final View child, final int position) { - final long id = getItemIdAtPosition(position); - final Animator anim = ObjectAnimator.ofInt( - mSelector, DRAWABLE_ALPHA, 0xFF, CLICK_ANIM_ALPHA, 0xFF); - anim.setDuration(CLICK_ANIM_DURATION); - anim.setInterpolator(new AccelerateDecelerateInterpolator()); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - performItemClick(child, position, id); - } - }); - anim.start(); - - if (mClickAnimation != null) { - mClickAnimation.cancel(); - } - mClickAnimation = anim; - } - private void clearPressedItem() { mDrawsInPressedState = false; setPressed(false); @@ -267,14 +213,9 @@ public class DropDownListView extends ListView { if (motionView != null) { motionView.setPressed(false); } - - if (mClickAnimation != null) { - mClickAnimation.cancel(); - mClickAnimation = null; - } } - private void setPressedItem(View child, int position, float x, float y) { + private void setPressedItem(@NonNull View child, int position, float x, float y) { mDrawsInPressedState = true; // Ordering is essential. First, update the container's pressed state. @@ -311,11 +252,6 @@ public class DropDownListView extends ListView { // Refresh the drawable state to reflect the new pressed state, // which will also update the selector state. refreshDrawableState(); - - if (mClickAnimation != null) { - mClickAnimation.cancel(); - mClickAnimation = null; - } } @Override diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 15cea772d418..1576877a6ed5 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -82,6 +82,7 @@ import android.view.Menu; 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 @@ public class Editor { 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 @@ public class Editor { 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 @@ public class Editor { 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 @@ public class Editor { 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 @@ public class Editor { } }; + 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 @@ public class Editor { } } + 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 @@ public class Editor { 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 class Editor { public SuggestionsPopupWindow() { mCursorWasVisibleBeforeSuggestions = mCursorVisible; - mSuggestionSpanComparator = new SuggestionSpanComparator(); - mSpansLengths = new HashMap<SuggestionSpan, Integer>(); } @Override @@ -2987,24 +3212,6 @@ public class Editor { 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 @@ public class Editor { } 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 @@ public class Editor { private boolean updateSuggestions() { Spannable spannable = (Spannable) mTextView.getText(); - SuggestionSpan[] suggestionSpans = getSuggestionSpans(); - - final int nbSpans = suggestionSpans.length; - // Suggestions are shown after a delay: the underlying spans may have been removed - if (nbSpans == 0) return false; + mNumberOfSuggestions = + mSuggestionHelper.getSuggestionInfo(mSuggestionInfos); + if (mNumberOfSuggestions == 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 @@ public class Editor { 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 @@ public class Editor { 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 @@ public class Editor { 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 7ddeff91c1e0..b383e1c55aad 100644 --- a/core/java/android/widget/ForwardingListener.java +++ b/core/java/android/widget/ForwardingListener.java @@ -53,12 +53,6 @@ public abstract class ForwardingListener /** 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 @@ public abstract class ForwardingListener switch (actionMasked) { case MotionEvent.ACTION_DOWN: mActivePointerId = srcEvent.getPointerId(0); - mWasLongPress = false; if (mDisallowIntercept == null) { mDisallowIntercept = new DisallowIntercept(); @@ -243,7 +236,6 @@ public abstract class ForwardingListener e.recycle(); mForwarding = true; - mWasLongPress = true; } /** diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index b95bc283d11a..595adc218e65 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.View.OnTouchListener; 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 @@ public class ListPopupWindow implements ShowableListMenu { private boolean mModal; - private int mLayoutDirection; - PopupWindow mPopup; /** @@ -174,7 +168,7 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { * @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 @@ public class ListPopupWindow implements ShowableListMenu { * @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 @@ public class ListPopupWindow implements ShowableListMenu { * @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 @@ public class ListPopupWindow implements ShowableListMenu { 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 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { /** * @return The background drawable for the popup window. */ - public Drawable getBackground() { + public @Nullable Drawable getBackground() { return mPopup.getBackground(); } @@ -380,7 +373,7 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { * * @param animationStyle Animation style to use. */ - public void setAnimationStyle(int animationStyle) { + public void setAnimationStyle(@StyleRes int animationStyle) { mPopup.setAnimationStyle(animationStyle); } @@ -399,7 +392,7 @@ public class ListPopupWindow implements ShowableListMenu { * * @return Animation style that will be used. */ - public int getAnimationStyle() { + public @StyleRes int getAnimationStyle() { return mPopup.getAnimationStyle(); } @@ -408,7 +401,7 @@ public class ListPopupWindow implements ShowableListMenu { * * @return The popup's anchor view */ - public View getAnchorView() { + public @Nullable View getAnchorView() { return mDropDownAnchorView; } @@ -418,7 +411,7 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { /** * @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 @@ public class ListPopupWindow implements ShowableListMenu { * * @see ListView#getSelectedView() */ - public View getSelectedView() { + public @Nullable View getSelectedView() { if (!isShowing()) { return null; } @@ -846,11 +839,11 @@ public class ListPopupWindow implements ShowableListMenu { * 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 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { * * @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 @@ public class ListPopupWindow implements ShowableListMenu { 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/Toolbar.java b/core/java/android/widget/Toolbar.java index 8e711b07b213..06daf61937c1 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -1946,6 +1946,9 @@ public class Toolbar extends ViewGroup { 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 ce4fc0633a6b..b0b25d31b9de 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -63,6 +63,22 @@ public class MetricsLogger implements MetricsConstants { 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/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index cea9867b8d88..7ae0efb6030b 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.BackgroundFallback; 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.ContextThemeWrapper; 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_NAVIGATIO 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 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind 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 e79f1b8a3133..59a1e4a78299 100644 --- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java +++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java @@ -62,6 +62,7 @@ public class DividerSnapAlgorithm { 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 @@ public class DividerSnapAlgorithm { 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 @@ public class DividerSnapAlgorithm { } 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 25a060e0a0a7..d06e2bbb7722 100644 --- a/core/java/com/android/internal/policy/DockedDividerUtils.java +++ b/core/java/com/android/internal/policy/DockedDividerUtils.java @@ -19,6 +19,11 @@ package com.android.internal.policy; 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 @@ public class DockedDividerUtils { 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/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index b692a189a6a7..381e71f7f333 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 @@ public class Preconditions { } /** + * 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 @@ public class Preconditions { * @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 @@ public class Preconditions { * @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 @@ public class Preconditions { * @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 @@ public class Preconditions { } /** + * 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 8699843eb83b..aa4b564c3800 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.DragEvent; 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 @@ public class BaseIWindow extends IWindow.Stub { @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 85ec29c4634c..0e7f06bc77b9 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -30,7 +30,7 @@ import android.view.inputmethod.InputConnection; 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 class IInputConnectionWrapper extends IInputContext.Stub { } 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 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return; } ic.commitText((CharSequence)msg.obj, msg.arg1); + onUserAction(); return; } case DO_SET_SELECTION: { @@ -338,6 +349,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return; } ic.setComposingText((CharSequence)msg.obj, msg.arg1); + onUserAction(); return; } case DO_SET_COMPOSING_REGION: { @@ -369,6 +381,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { return; } ic.sendKeyEvent((KeyEvent)msg.obj); + onUserAction(); return; } case DO_CLEAR_META_KEY_STATES: { @@ -413,7 +426,9 @@ public class IInputConnectionWrapper extends IInputContext.Stub { 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 c2adc42cb824..a67e43a1c647 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.Gravity; 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 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On 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 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On 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 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On 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 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On mPopup.setDropDownGravity(mDropDownGravity); if (!mHasContentWidth) { - mContentWidth = measureIndividualMenuWidth( - mAdapter, mMeasureParent, mContext, mPopupMaxWidth); + mContentWidth = measureIndividualMenuWidth(mAdapter, null, mContext, mPopupMaxWidth); mHasContentWidth = true; } @@ -177,7 +189,9 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On 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 e38d82fdca39..4e8f19cd0f95 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.app.trust.TrustManager; 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.ServiceManager; 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 @@ public class LockPatternUtils { 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 @@ public class LockPatternUtils { private final ContentResolver mContentResolver; private DevicePolicyManager mDevicePolicyManager; private ILockSettings mLockSettingsService; + private UserManager mUserManager; public static final class RequestThrottledException extends Exception { @@ -173,6 +178,13 @@ public class LockPatternUtils { 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 @@ public class LockPatternUtils { } /** + * 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 class LockPatternUtils { } } - public static boolean isSeparateWorkChallengeEnabled() { - return StorageManager.isFileBasedEncryptionEnabled(); - } - public void registerStrongAuthTracker(final StrongAuthTracker strongAuthTracker) { try { getLockSettings().registerStrongAuthTracker(strongAuthTracker.mStub); diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java new file mode 100644 index 000000000000..ff0e63da8289 --- /dev/null +++ b/core/java/com/android/server/backup/PermissionBackupHelper.java @@ -0,0 +1,78 @@ +/* + * 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.backup; + +import android.app.AppGlobals; +import android.app.backup.BlobBackupHelper; +import android.content.pm.IPackageManager; +import android.os.UserHandle; +import android.util.Slog; + +public class PermissionBackupHelper extends BlobBackupHelper { + private static final String TAG = "PermissionBackup"; + private static final boolean DEBUG = false; + + // current schema of the backup state blob + private static final int STATE_VERSION = 1; + + // key under which the permission-grant state blob is committed to backup + private static final String KEY_PERMISSIONS = "permissions"; + + public PermissionBackupHelper() { + super(STATE_VERSION, KEY_PERMISSIONS); + } + + @Override + protected byte[] getBackupPayload(String key) { + IPackageManager pm = AppGlobals.getPackageManager(); + if (DEBUG) { + Slog.d(TAG, "Handling backup of " + key); + } + try { + switch (key) { + case KEY_PERMISSIONS: + return pm.getPermissionGrantBackup(UserHandle.USER_SYSTEM); + + default: + Slog.w(TAG, "Unexpected backup key " + key); + } + } catch (Exception e) { + Slog.e(TAG, "Unable to store payload " + key); + } + return null; + } + + @Override + protected void applyRestoredPayload(String key, byte[] payload) { + IPackageManager pm = AppGlobals.getPackageManager(); + if (DEBUG) { + Slog.d(TAG, "Handling restore of " + key); + } + try { + switch (key) { + case KEY_PERMISSIONS: + pm.restorePermissionGrants(payload, UserHandle.USER_SYSTEM); + break; + + default: + Slog.w(TAG, "Unexpected restore key " + key); + } + } catch (Exception e) { + Slog.w(TAG, "Unable to restore key " + key); + } + } +} diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 234815fc901d..3f76e13eb70c 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -46,6 +46,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final String SYNC_SETTINGS_HELPER = "account_sync_settings"; private static final String PREFERRED_HELPER = "preferred_activities"; private static final String NOTIFICATION_HELPER = "notifications"; + private static final String PERMISSION_HELPER = "permissions"; // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are @@ -94,6 +95,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper()); addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); + addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); super.onBackup(oldState, data, newState); } @@ -128,6 +130,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper()); addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); + addHelper(PERMISSION_HELPER, new PermissionBackupHelper()); try { super.onRestore(data, appVersionCode, newState); diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 30593f2eb67e..fc1ba4439443 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -7,6 +7,7 @@ LOCAL_CFLAGS += -U__APPLE__ 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 63f193d57419..40af22a2adc0 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -1257,6 +1257,7 @@ static const RegJNIRec gRegJNI[] = { 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 @@ static const RegJNIRec gRegJNI[] = { 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 a805b6d1d151..80ccb614ad73 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -774,11 +774,14 @@ static jobject Bitmap_copyAshmem(JNIEnv* env, jobject, jlong srcHandle) { 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 @@ static const JNINativeMethod gBitmapMethods[] = { (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 83fd07382fe0..1a86e5fc6a15 100644 --- a/core/jni/android/graphics/ColorFilter.cpp +++ b/core/jni/android/graphics/ColorFilter.cpp @@ -42,7 +42,7 @@ public: } 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 0a25a0a8ae38..98f8ce3e93c9 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -76,9 +76,12 @@ namespace PaintGlue { 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 { }; // 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 fda0ffa8bbe5..2507e4ddea7e 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -60,6 +60,27 @@ static jlong Shader_setLocalMatrix(JNIEnv* env, jobject o, jlong shaderHandle, j // as all the data needed is contained within the newly created LocalMatrixShader. SkASSERT(shaderHandle); SkAutoTUnref<SkShader> currentShader(reinterpret_cast<SkShader*>(shaderHandle)); + + // 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 (proxyMatrix == *matrix) { + return reinterpret_cast<jlong>(currentShader.detach()); + } + return reinterpret_cast<jlong>(baseShader->newWithLocalMatrix(*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 e4e73a4b4c8a..34877e054e55 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -37,8 +37,12 @@ static Canvas* get_canvas(jlong canvasHandle) { 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 @@ static void freeTextLayoutCaches(JNIEnv* env, jobject) { }; // 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 e162810fd802..8e8f6c371204 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -152,7 +152,8 @@ static struct { static struct { jmethodID postDynPolicyEventFromNative; -} gDynPolicyEventHandlerMethods; + jmethodID postRecordConfigEventFromNative; +} gAudioPolicyEventHandlerMethods; static Mutex gLock; @@ -378,12 +379,26 @@ android_media_AudioSystem_dyn_policy_callback(int event, String8 regId, int val) 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 @@ android_media_AudioSystem_registerDynPolicyCallback(JNIEnv *env, jobject thiz) 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 @@ static const JNINativeMethod gMethods[] = { (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 @@ int register_android_media_AudioSystem(JNIEnv *env) 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 2488111247a2..03a1e718d2ea 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -84,6 +84,7 @@ struct stat_fields { jfieldID privateClean_field; jfieldID sharedClean_field; jfieldID swappedOut_field; + jfieldID swappedOutPss_field; }; struct stat_field_names { @@ -94,20 +95,22 @@ struct stat_field_names { 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 @@ struct stats_t { int privateClean; int sharedClean; int swappedOut; + int swappedOutPss; }; #define BINDER_STATS "/proc/binder/stats" @@ -206,7 +210,7 @@ static int read_memtrack_memory(int pid, struct graphics_memory_pss* graphics_me 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 @@ static void read_mapinfo(FILE *fp, stats_t* stats) 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 @@ static void read_mapinfo(FILE *fp, stats_t* stats) int subHeap = HEAP_UNKNOWN; int prevHeap = HEAP_UNKNOWN; + *foundSwapPss = false; + if(fgets(line, sizeof(line), fp) == 0) return; while (!done) { @@ -340,6 +346,7 @@ static void read_mapinfo(FILE *fp, stats_t* stats) 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 @@ static void read_mapinfo(FILE *fp, stats_t* stats) /* 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 @@ static void read_mapinfo(FILE *fp, stats_t* stats) 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 @@ static void read_mapinfo(FILE *fp, stats_t* stats) 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 @@ static void load_maps(int pid, stats_t* stats) 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 @@ static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, 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 @@ static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, 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 @@ static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz, 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 @@ static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject o 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 @@ static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jl } 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 @@ int register_android_os_Debug(JNIEnv *env) } 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 @@ int register_android_os_Debug(JNIEnv *env) 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 75077df213b4..665c417cba43 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -220,6 +220,8 @@ <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTABLE" /> <protected-broadcast android:name="android.intent.action.MEDIA_EJECT" /> + <protected-broadcast android:name="android.intent.action.PICTURE_IN_PICTURE_BUTTON" /> + <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL" /> <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <!-- @deprecated. Only {@link android.net.ConnectivityManager.CONNECTIVITY_ACTION} is sent. --> @@ -385,6 +387,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 +2831,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 000000000000..d17dfc1ec8cf --- /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 000000000000..276907401d5a --- /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 36c167ea8056..c94545905445 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 ec16add34a26..17b5a290688a 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 12ef759d60ad..efde61f4b278 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 7f0e6d7a3cf7..0e4771ea984f 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 ac5ce6629593..fd9ddece5f0d 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 301e1e33b221..955727b6119a 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 9c88d44349c5..6e7a77f5f7b5 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 8554b84394a5..bbe98872c2b1 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 2eca49a6f7bf..f5763f91167d 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 2a719b011c0e..3e8a86ded2e4 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 d116a24ee817..e2fb6ffda6cc 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 8116b16ea4fa..3e277c342404 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 930c310b9267..0edc4380005e 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 930c310b9267..0edc4380005e 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 930c310b9267..0edc4380005e 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 258ef33fe993..bd2f91b97b4a 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 9848bb624c26..e73b4257cd7d 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 466bd5dddfe7..e6a122c6172e 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 cdb8622e9a01..31a073bf174d 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 b0da5fe89d48..f0271d6fd122 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 f7db67853a4a..3e0c1722bad7 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 50aed95e9594..8450e5cd488a 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 33d20169adfc..84c43fac2c37 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 7947825f75bf..36594e045cba 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 aa9478bd6158..9d098fc62a7b 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 d3d1e2c2aa10..5741d5cf30f0 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 dc55c57b5001..f635b1540e5d 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 3176c6579399..64a674c0350f 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 d72ee8dcfb12..8d6272a2fd86 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 024eea4c2ca1..898f6d100793 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 240c71558b66..ebcde5de2ef9 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 ece503d74a38..d0646849853c 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 144afc09bb50..375bdf0e9e0d 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 edfab2281b72..a32a8ed3fe12 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 ffdd31c14b5d..f2fc95339863 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 ff58eaadc82d..ab82fd4ee40d 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 261212fa79bf..ffb35995b8fe 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 f083702d66ae..ab7ad8224b5d 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 c8f08c77cf8b..4c478aae1614 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 4d8493395b07..e9b01d154bbc 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 12d67bb2d2bf..cf21944521bf 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 e35edd6aa539..f1143fe26950 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 de02a4f86796..f864400450ed 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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 000000000000..526d08b45c7c --- /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 96a29a59bf08..484e5a6d9bb1 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 f6869e38a169..42a9a82e2b46 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 d982eceb6aac..91b9ea7e329d 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 408719926ba9..0cb37dae386b 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 0923e912b215..d40c7d1e70f9 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 3ef7e6cb8d8c..28a4d0f64c55 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 cd65d85255e9..7e909e4433d3 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 301094b75d03..15277a25f811 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 1407991d68bf..b81b46e17307 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 39f20f5fba52..192c1815f7d3 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 f3e50b56815f..38437faf5c42 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 89bbbe82d714..91821a4147b6 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 f559dbb3ef1e..f37d3a6d87e6 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 89bbbe82d714..91821a4147b6 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 4086e69e2e2f..b235b7adde45 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 ee39b83a7166..0c5fb09b4894 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 5364c98dfd7e..17f0f97da0b9 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 90ccd543a99c..d9c79f29fa63 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 4e9b1c55c03f..8e293e191c71 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 be9c709cd398..a24e82f40d6c 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 a3618ab5e2f2..c706264f1bcc 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 b07dc285e128..ecc6d4eaf2f6 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 cf0063c7254a..d1e6d974629e 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 ecefb7755f4e..eacc9a94aa8c 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 b1bccd2987d7..0440134c87a9 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 0b8e60d68987..5d2794324645 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 02a3587759d9..479efdf25f41 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 283bcebb887d..b0c6d5aadbe9 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 fce4cc3909b0..af1354a285c6 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 519f42b67272..82bf7ab3a2f1 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 e90d921dc40b..c4be0b953c8d 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 31e090bc6c99..69b0b9947f6a 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 c5cd59af735e..4c7bfd340850 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 36ba2ad610f2..d6cacb7fa625 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 72ea52395ed2..a47f73d4d3ac 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 bee7eeaa55c9..b34594f36d7f 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 58a77e809235..6eba78a4ce2b 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 58c4046dde82..3f6a0c194d61 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> @@ -974,6 +981,7 @@ 0 - Nothing 1 - Recent apps view in SystemUI 2 - Launch assist intent + 3 - Start picture-in-picture (PIP) or launch PIP UI This needs to match the constants in policy/src/com/android/internal/policy/impl/PhoneWindowManager.java --> @@ -2421,4 +2429,10 @@ <!-- List of comma separated package names for which we the system will not show crash, ANR, etc. dialogs. --> <string translatable="false" name="config_appsNotReportingCrashes"></string> + + <!-- Inactivity threshold (in milliseconds) used in JobScheduler. JobScheduler will consider + the device to be "idle" after being inactive for this long. --> + <integer name="config_jobSchedulerInactivityIdleThreshold">4260000</integer> + <!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state --> + <integer name="config_jobSchedulerIdleWindowSlop">300000</integer> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index acea461f44a1..c883b1f8a7db 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 706dd206fcb2..e65ce5e7a181 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" /> @@ -2252,6 +2258,9 @@ <java-symbol type="integer" name="config_defaultNightMode" /> + <java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" /> + <java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" /> + <java-symbol type="style" name="Animation.ImmersiveModeConfirmation" /> <java-symbol type="integer" name="config_screen_magnification_multi_tap_adjustment" /> diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml index 5f1f9273c5e1..77d2ada83e04 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 000000000000..2e0e6dc37f18 --- /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 48590c1ec463..1966313a41f0 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.test.suitebuilder.annotation.SmallTest; 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.Window; 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 @@ public final class PhoneWindowActionModeTest @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 b5b1a25cc88b..169ef0b8cd00 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -25,14 +25,14 @@ import android.os.Trace; 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 final class Bitmap implements Parcelable { */ 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 @@ public final class Bitmap implements Parcelable { // Convenience for JNI access private final long mNativePtr; - private final BitmapFinalizer mFinalizer; private final boolean mIsMutable; @@ -125,9 +128,13 @@ public final class Bitmap implements Parcelable { } 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 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { * 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 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 final class Bitmap implements Parcelable { */ 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 final class Bitmap implements Parcelable { */ 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 @@ public final class Bitmap implements Parcelable { } 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 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 final class Bitmap implements Parcelable { */ 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 @@ public final class Bitmap implements Parcelable { 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 class Bitmap implements Parcelable { */ public final void setHasMipMap(boolean hasMipMap) { checkRecycled("setHasMipMap called on a recycled bitmap"); - nativeSetHasMipMap(mFinalizer.mNativeBitmap, hasMipMap); + nativeSetHasMipMap(mNativePtr, hasMipMap); } /** @@ -1358,7 +1365,7 @@ public final class Bitmap implements Parcelable { if (!isMutable()) { throw new IllegalStateException("cannot erase immutable bitmaps"); } - nativeErase(mFinalizer.mNativeBitmap, c); + nativeErase(mNativePtr, c); } /** @@ -1375,7 +1382,7 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { throw new IllegalStateException(); } checkPixelAccess(x, y); - nativeSetPixel(mFinalizer.mNativeBitmap, x, y, color); + nativeSetPixel(mNativePtr, x, y, color); } /** @@ -1525,7 +1532,7 @@ public final class Bitmap implements Parcelable { 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 final class Bitmap implements Parcelable { */ 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 final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 @@ public final class Bitmap implements Parcelable { 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 1cc53469a2f7..d4f745d818bd 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -31,6 +31,8 @@ import java.lang.annotation.RetentionPolicy; 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 @@ public class Canvas { /** * 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 @@ public class Canvas { // (see SkCanvas.cpp, SkDraw.cpp) private static final int MAXMIMUM_BITMAP_SIZE = 32766; - // This field is used to finalize the native Canvas properly - private final CanvasFinalizer mFinalizer; - - private static final class CanvasFinalizer { - private long mNativeCanvasWrapper; + // The approximate size of the native allocation associated with + // a Canvas object. + private static final long NATIVE_ALLOCATION_SIZE = 525; - public CanvasFinalizer(long nativeCanvas) { - mNativeCanvasWrapper = nativeCanvas; - } - - @Override - protected void finalize() throws Throwable { - try { - dispose(); - } finally { - super.finalize(); - } - } + private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( + getNativeFinalizer(), NATIVE_ALLOCATION_SIZE); - public void dispose() { - if (mNativeCanvasWrapper != 0) { - finalizer(mNativeCanvasWrapper); - mNativeCanvasWrapper = 0; - } - } - } + // This field is used to finalize the native Canvas properly + private Runnable mFinalizer; /** * Construct an empty raster canvas. Use setBitmap() to specify a bitmap to @@ -122,7 +107,7 @@ public class Canvas { 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 @@ public class Canvas { } throwIfCannotDraw(bitmap); mNativeCanvasWrapper = initRaster(bitmap); - mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper); + mFinalizer = sRegistry.registerNativeAllocation(this, mNativeCanvasWrapper); mBitmap = bitmap; mDensity = bitmap.mDensity; } @@ -154,7 +139,7 @@ public class Canvas { throw new IllegalStateException(); } mNativeCanvasWrapper = nativeCanvas; - mFinalizer = new CanvasFinalizer(mNativeCanvasWrapper); + mFinalizer = sRegistry.registerNativeAllocation(this, mNativeCanvasWrapper); mDensity = Bitmap.getDefaultDensity(); } @@ -1980,7 +1965,11 @@ public class Canvas { * @hide */ public void release() { - mFinalizer.dispose(); + mNativeCanvasWrapper = 0; + if (mFinalizer != null) { + mFinalizer.run(); + mFinalizer = null; + } } /** @@ -2148,5 +2137,5 @@ public class Canvas { 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 90522f71a845..dfb8bb86f1e5 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -30,6 +30,8 @@ import com.android.internal.annotations.GuardedBy; 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 @@ public class Paint { 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 class Paint { */ 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 class Paint { */ 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 @@ public class Paint { * 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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * {@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 @@ public class Paint { * {@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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * false to clear it. */ public void setLinearText(boolean linearText) { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); nSetLinearText(mNativePaint, linearText); } @@ -728,7 +727,6 @@ public class Paint { * 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 @@ public class Paint { * 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 @@ public class Paint { * 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 @@ public class Paint { * 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 @@ public class Paint { * 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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { */ @ColorInt public int getColor() { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); return nGetColor(mNativePaint); } @@ -877,7 +868,6 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * Stroke or StrokeAndFill. */ public float getStrokeWidth() { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); return nGetStrokeWidth(mNativePaint); } @@ -948,7 +935,6 @@ public class Paint { * 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 @@ public class Paint { * Stroke or StrokeAndFill. */ public float getStrokeMiter() { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); return nGetStrokeMiter(mNativePaint); } @@ -976,7 +961,6 @@ public class Paint { * 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 @@ public class Paint { * style is Stroke or StrokeAndFill. */ public Cap getStrokeCap() { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); return sCapArray[nGetStrokeCap(mNativePaint)]; } @@ -1001,7 +984,6 @@ public class Paint { * 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 @@ public class Paint { * @return the paint's Join. */ public Join getStrokeJoin() { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); return sJoinArray[nGetStrokeJoin(mNativePaint)]; } @@ -1022,7 +1003,6 @@ public class Paint { * Stroke or StrokeAndFill. */ public void setStrokeJoin(Join join) { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); nSetStrokeJoin(mNativePaint, join.nativeInt); } @@ -1038,7 +1018,6 @@ public class Paint { * 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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { */ @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 @@ public class Paint { * 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 @@ public class Paint { * @hide */ public boolean hasShadowLayer() { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); return nHasShadowLayer(mNativePaint); } @@ -1293,7 +1269,6 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @return the paint's text size. */ public float getTextSize() { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); return nGetTextSize(mNativePaint); } @@ -1446,7 +1415,6 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @hide */ public int getHyphenEdit() { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); return nGetHyphenEdit(mNativePaint); } @@ -1579,7 +1539,6 @@ public class Paint { * @hide */ public void setHyphenEdit(int hyphen) { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); nSetHyphenEdit(mNativePaint, hyphen); } @@ -1591,7 +1550,6 @@ public class Paint { * current typeface and text size. */ public float ascent() { - if (mNativePaint == 0) throw new NullPointerException("Already finalized!"); return nAscent(mNativePaint, mNativeTypeface); } @@ -1605,7 +1563,6 @@ public class Paint { * 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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 @@ public class Paint { * @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 class Paint { */ 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 class Paint { 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 class Paint { */ 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 class Paint { */ 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 @@ public class Paint { * @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 @@ public class Paint { 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 class Paint { 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 class Paint { */ 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 class Paint { */ 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 class Paint { */ 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 class Paint { */ 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 class Paint { */ 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 @@ public class Paint { * 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 @@ public class Paint { * 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 @@ public class Paint { * @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 class Paint { */ 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 class Paint { */ 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 class Paint { */ 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 class Paint { */ 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 @@ public class Paint { 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 @@ public class Paint { 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/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java index 78424e3663de..c0dfe77cf4f3 100644 --- a/graphics/java/android/graphics/drawable/RotateDrawable.java +++ b/graphics/java/android/graphics/drawable/RotateDrawable.java @@ -204,11 +204,13 @@ public class RotateDrawable extends DrawableWrapper { /** * 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 @@ public class RotateDrawable extends DrawableWrapper { /** * 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 44c5e2f35f6e..fa7c8aa02c7c 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir) 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 @@ hwui_src_files := \ FrameInfoVisualizer.cpp \ GammaFontRenderer.cpp \ GlopBuilder.cpp \ + GpuMemoryTracker.cpp \ GradientCache.cpp \ Image.cpp \ Interpolator.cpp \ @@ -225,11 +226,14 @@ LOCAL_SRC_FILES += \ $(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/LeakCheckTests.cpp \ tests/unit/VectorDrawableTests.cpp \ tests/unit/OffscreenBufferPoolTests.cpp \ tests/unit/StringUtilsTests.cpp diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp index 41411a98a4bf..6afff1b7158e 100644 --- a/libs/hwui/AssetAtlas.cpp +++ b/libs/hwui/AssetAtlas.cpp @@ -39,40 +39,22 @@ void AssetAtlas::init(sp<GraphicBuffer> buffer, int64_t* map, int count) { 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 @@ void AssetAtlas::updateTextureId() { /////////////////////////////////////////////////////////////////////////////// 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 @@ Texture* AssetAtlas::getEntryTexture(const SkPixelRef* pixelRef) const { * 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 @@ private: }; // 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 @@ void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) { 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 a037725b1c6c..75400ff494c3 100644 --- a/libs/hwui/AssetAtlas.h +++ b/libs/hwui/AssetAtlas.h @@ -17,18 +17,16 @@ #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 "Texture.h" +#include "UvMapper.h" #include <cutils/compiler.h> - +#include <GLES2/gl2.h> +#include <ui/GraphicBuffer.h> #include <SkBitmap.h> -#include "Texture.h" -#include "UvMapper.h" +#include <memory> +#include <unordered_map> namespace android { namespace uirenderer { @@ -71,6 +69,10 @@ public: return texture->blend ? &atlas.mBlendKey : &atlas.mOpaqueKey; } + ~Entry() { + delete texture; + } + private: /** * The pixel ref that generated this atlas entry. @@ -90,10 +92,6 @@ public: , atlas(atlas) { } - ~Entry() { - delete texture; - } - friend class AssetAtlas; }; @@ -127,7 +125,7 @@ public: * 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 @@ public: * 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 @@ public: * 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 @@ public: private: void createEntries(Caches& caches, int64_t* map, int count); - void updateTextureId(); Texture* mTexture; Image* mImage; @@ -168,7 +165,7 @@ private: 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 5b34f6b79199..7ecc743b700c 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -217,7 +217,7 @@ static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRender .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 renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& stat 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 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op .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 @@ void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRe 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; @@ -784,6 +784,7 @@ void BakedOpDispatcher::onCopyFromLayerOp(BakedOpRenderer& renderer, const CopyF .build(); renderer.renderGlop(state, glop); } + renderer.renderState().layerPool().putOrDelete(*op.layerHandle); } } // namespace uirenderer diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 42fb66fe9845..4fbff0d107ba 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -48,7 +48,7 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const // 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 @@ OffscreenBuffer* BakedOpRenderer::copyToLayer(const Rect& area) { 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, @@ -272,7 +272,7 @@ void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* cli 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/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h index e857f6b2a506..10c469875150 100644 --- a/libs/hwui/BakedOpRenderer.h +++ b/libs/hwui/BakedOpRenderer.h @@ -45,9 +45,15 @@ public: * Position agnostic shadow lighting info. Used with all shadow ops in scene. */ struct LightInfo { - float lightRadius = 0; - uint8_t ambientShadowAlpha = 0; - uint8_t spotShadowAlpha = 0; + LightInfo() : LightInfo(0, 0, 0) {} + LightInfo(float lightRadius, uint8_t ambientShadowAlpha, + uint8_t spotShadowAlpha) + : lightRadius(lightRadius) + , ambientShadowAlpha(ambientShadowAlpha) + , spotShadowAlpha(spotShadowAlpha) {} + float lightRadius; + uint8_t ambientShadowAlpha; + uint8_t spotShadowAlpha; }; BakedOpRenderer(Caches& caches, RenderState& renderState, bool opaque, const LightInfo& lightInfo) diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index 166656cbd72b..c4c655b75d22 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -51,20 +51,25 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, // 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(); - - // 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); - - if (layerNode->getDisplayList()) { - deferNodeOps(*layerNode); + // 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); + + saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0, + layerDamage, lightCenter, nullptr, layerNode); + + if (layerNode->getDisplayList()) { + deferNodeOps(*layerNode); + } + restoreForLayer(); } - restoreForLayer(); } // Defer Fbo0 diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp new file mode 100644 index 000000000000..4fb57019264d --- /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 000000000000..851aeae8352c --- /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 8c4645092c97..522aa96132dd 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -110,9 +110,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) { 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 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, 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 @@ void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float am 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 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* 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 7534c5d11164..b762ca7d5e5c 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -143,7 +143,8 @@ private: 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 0fe20ad1b662..83692664d76f 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -36,7 +36,8 @@ namespace android { 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 @@ Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint // 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 @@ Layer::~Layer() { 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 @@ Layer::~Layer() { void Layer::onGlContextLost() { removeFbo(); - deleteTexture(); + texture.deleteTexture(); } uint32_t Layer::computeIdealWidth(uint32_t layerWidth) { @@ -179,8 +179,8 @@ void Layer::setColorFilter(SkColorFilter* filter) { } 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::bindStencilRenderBuffer() const { } 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 e90f055b667b..e00ae66997a5 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 @@ struct DeferStateStruct; /** * 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: } inline GLuint getTextureId() const { - return texture.id; + return texture.id(); } inline Texture& getTexture() { @@ -263,7 +263,6 @@ public: void bindTexture() const; void generateTexture(); void allocateTexture(); - void deleteTexture(); /** * When the caller frees the texture itself, the caller diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index b117754347ed..f5681ce712d5 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -112,7 +112,6 @@ Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uin 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/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 92b758de8200..0cd763df78d1 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 @@ bool OpenGLRenderer::finish() { #if DEBUG_MEMORY_USAGE mCaches.dumpMemoryUsage(); + GPUMemoryTracker::dump(); #else if (Properties::debugLevel & kDebugMemory) { mCaches.dumpMemoryUsage(); @@ -1497,7 +1499,7 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { .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 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, cons 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 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, .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 @@ Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) { 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 @@ void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y, .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 06ea55ad2eac..bfabc1d4d94c 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -185,7 +185,7 @@ void PathCache::operator()(PathDescription& entry, PathTexture*& texture) { 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 @@ void PathCache::removeTexture(PathTexture* texture) { ALOGD("Shape deleted, size = %d", size); } - if (texture->id) { - Caches::getInstance().textureState().deleteTexture(texture->id); - } + texture->deleteTexture(); delete texture; } } @@ -248,8 +246,7 @@ PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *p 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 @@ void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap, // 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::clear() { 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 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { 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 302e9f856904..18f380fe3f7a 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -61,13 +61,11 @@ class Caches; */ 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/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 83652c6de21f..6f4a6839be4e 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -57,7 +57,7 @@ static inline void bindUniformColor(int slot, FloatColor color) { } 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 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model 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 51f16523966b..f1e28b7dfa40 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -187,13 +187,10 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs 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 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs } } - 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 5195b457af2b..8a6b28d86f27 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 @@ void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force 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 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for 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::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for } } -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 1c544b929e64..4e8e6dcf1a77 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,28 +60,63 @@ public: /** * 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); + } + /** - * Generation of the backing bitmap, + * 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. */ - uint32_t generation = 0; + void upload(const SkBitmap& source); + /** - * Indicates whether the texture requires blending. + * Basically glTexImage2D/glTexSubImage2D. */ - bool blend = false; + void upload(GLint internalformat, uint32_t width, uint32_t height, + GLenum format, GLenum type, const void* pixels); + /** - * Width of the backing bitmap. + * Wraps an existing texture. */ - uint32_t width = 0; + 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; + } + /** - * Height of the backing bitmap. + * Generation of the backing bitmap, */ - uint32_t height = 0; + uint32_t generation = 0; + /** + * Indicates whether the texture requires blending. + */ + bool blend = false; /** * Indicates whether this texture should be cleaned up after use. */ @@ -100,6 +142,19 @@ public: 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 @@ private: class AutoTexture { public: - AutoTexture(const Texture* texture) + AutoTexture(Texture* texture) : texture(texture) {} ~AutoTexture() { if (texture && texture->cleanup) { @@ -129,7 +184,7 @@ public: } } - const Texture *const texture; + Texture* const texture; }; // class AutoTexture }; // namespace uirenderer diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 21901cf4414b..31bfa3a1ada4 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -166,7 +166,8 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a 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 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a } 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 @@ Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType 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 191c8a81fec6..463450c81714 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 56cb10476118..793df92dae45 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -407,8 +407,9 @@ void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter, 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 d2685daa1711..8ba4761c1b2e 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -111,11 +111,11 @@ CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) 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 @@ void CacheTexture::releasePixelBuffer() { 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 @@ void CacheTexture::allocatePixelBuffer() { 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 6dabc768ce6b..5510666eef86 100644 --- a/libs/hwui/font/CacheTexture.h +++ b/libs/hwui/font/CacheTexture.h @@ -92,11 +92,11 @@ public: 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 @@ public: GLuint getTextureId() { allocatePixelBuffer(); - return mTexture.id; + return mTexture.id(); } inline bool isDirty() const { @@ -183,6 +183,7 @@ private: 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 227b6409b893..98c94dfab1c5 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.cpp +++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp @@ -34,29 +34,22 @@ namespace uirenderer { 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 @@ void OffscreenBuffer::updateMeshFromRegion() { 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::get(RenderState& renderState, 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 2d8d5295e1f8..94155efcbb0a 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 @@ class RenderState; * 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 @@ public: 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 @@ private: 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 4fa820058fb2..b6dba02cb01d 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 @@ RenderState::~RenderState() { 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 @@ void RenderState::onGLContextDestroyed() { mScissor = nullptr; delete mStencil; mStencil = nullptr; + + GpuMemoryTracker::onGLContextDestroyed(); } void RenderState::flush(Caches::FlushMode mode) { @@ -205,17 +209,6 @@ void RenderState::debugOverdraw(bool enable, bool clear) { } } -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 @@ private: }; 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 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { 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 dcd5ea69a9fe..e5d3e79ddfcb 100644 --- a/libs/hwui/renderstate/RenderState.h +++ b/libs/hwui/renderstate/RenderState.h @@ -86,8 +86,6 @@ public: 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 @@ public: 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 1f50f712c267..26ebdee06c08 100644 --- a/libs/hwui/renderstate/TextureState.cpp +++ b/libs/hwui/renderstate/TextureState.cpp @@ -34,134 +34,6 @@ const GLenum kTextureUnits[] = { 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 3a2b85ae2886..ec94d7e9e267 100644 --- a/libs/hwui/renderstate/TextureState.h +++ b/libs/hwui/renderstate/TextureState.h @@ -76,13 +76,6 @@ public: */ 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 644f3565216b..dd48a836f858 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" @@ -497,6 +498,8 @@ void CanvasContext::draw() { mJankTracker.addFrame(*mCurrentFrameInfo); mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); + + GpuMemoryTracker::onFrameCompleted(); } // Called by choreographer to do an RT-driven animation @@ -512,11 +515,11 @@ void CanvasContext::doFrame() { 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); @@ -578,7 +581,7 @@ void CanvasContext::buildLayer(RenderNode* node) { 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 43282c99803a..72c7e4eedef1 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -450,6 +450,11 @@ CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) { } 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 624f3bd1e710..d56693ce7009 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 @@ void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text, 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 edde31e502e0..ae0814241055 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -171,21 +171,23 @@ public: syncHierarchyPropertiesAndDisplayListImpl(node.get()); } + static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) { + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + std::vector<sp<RenderNode>> vec; + vec.emplace_back(node); + return vec; + } + 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 +199,10 @@ public: 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 1616a95857d8..02a39501e647 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/unit/CrashHandlerInjector.cpp b/libs/hwui/tests/unit/CrashHandlerInjector.cpp new file mode 100644 index 000000000000..685c26490a1a --- /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/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index bded50a2e0fd..b51bd2ff99cf 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -32,13 +32,6 @@ namespace uirenderer { const LayerUpdateQueue sEmptyLayerUpdateQueue; const Vector3 sLightCenter = {100, 100, 100}; -static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) { - TestUtils::syncHierarchyPropertiesAndDisplayList(node); - std::vector<sp<RenderNode>> vec; - vec.emplace_back(node); - return vec; -} - /** * Virtual class implemented by each test to redirect static operation / state transitions to * virtual methods. @@ -139,7 +132,7 @@ TEST(FrameBuilder, simple) { canvas.drawBitmap(bitmap, 10, 10, nullptr); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); SimpleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end @@ -165,7 +158,7 @@ TEST(FrameBuilder, simpleStroke) { canvas.drawPoint(50, 50, strokedPaint); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); SimpleStrokeTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()); @@ -180,7 +173,7 @@ TEST(FrameBuilder, simpleRejection) { canvas.restore(); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); FailRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); @@ -215,7 +208,7 @@ TEST(FrameBuilder, simpleBatching) { }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); SimpleBatchingTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2 * LOOPS, renderer.getIndex()) @@ -256,7 +249,7 @@ TEST(FrameBuilder, clippedMerging) { }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); ClippedMergingTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); @@ -284,7 +277,7 @@ TEST(FrameBuilder, textMerging) { TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); TextMergingTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops"; @@ -315,7 +308,7 @@ TEST(FrameBuilder, textStrikethrough) { } }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); TextStrikethroughTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2 * LOOPS, renderer.getIndex()) @@ -349,7 +342,7 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer) { canvas.restore(); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); TextureLayerTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()); @@ -394,7 +387,7 @@ TEST(FrameBuilder, renderNode) { }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(parent), sLightCenter); + TestUtils::createSyncedNodeList(parent), sLightCenter); RenderNodeTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); } @@ -418,7 +411,7 @@ TEST(FrameBuilder, clipped) { FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver - 200, 200, createSyncedNodeList(node), sLightCenter); + 200, 200, TestUtils::createSyncedNodeList(node), sLightCenter); ClippedTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); } @@ -460,7 +453,7 @@ TEST(FrameBuilder, saveLayer_simple) { canvas.restore(); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); SaveLayerSimpleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); @@ -532,7 +525,7 @@ TEST(FrameBuilder, saveLayer_nested) { }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); SaveLayerNestedTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(10, renderer.getIndex()); @@ -552,7 +545,7 @@ TEST(FrameBuilder, saveLayer_contentRejection) { canvas.restore(); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); FailRenderer renderer; // should see no ops, even within the layer, since the layer should be rejected @@ -590,12 +583,12 @@ TEST(FrameBuilder, saveLayerUnclipped_simple) { auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { - canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kMatrixClip_SaveFlag); + canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0)); canvas.drawRect(0, 0, 200, 200, SkPaint()); canvas.restore(); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); SaveLayerUnclippedSimpleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); @@ -649,7 +642,7 @@ TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { canvas.restoreToCount(restoreTo); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); SaveLayerUnclippedMergedClearsTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(10, renderer.getIndex()) @@ -711,7 +704,7 @@ TEST(FrameBuilder, saveLayerUnclipped_complex) { canvas.restore(); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); SaveLayerUnclippedComplexTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(12, renderer.getIndex()); @@ -762,7 +755,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); *layerHandle = &layer; - auto syncedNodeList = createSyncedNodeList(node); + auto syncedNodeList = TestUtils::createSyncedNodeList(node); // only enqueue partial damage LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid @@ -863,7 +856,7 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); *(parent->getLayerHandle()) = &parentLayer; - auto syncedList = createSyncedNodeList(parent); + auto syncedList = TestUtils::createSyncedNodeList(parent); LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); @@ -919,7 +912,7 @@ TEST(FrameBuilder, zReorder) { drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, - createSyncedNodeList(parent), sLightCenter); + TestUtils::createSyncedNodeList(parent), sLightCenter); ZReorderTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(10, renderer.getIndex()); @@ -1002,7 +995,7 @@ TEST(FrameBuilder, projectionReorder) { }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, - createSyncedNodeList(parent), sLightCenter); + TestUtils::createSyncedNodeList(parent), sLightCenter); ProjectionReorderTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(3, renderer.getIndex()); @@ -1045,7 +1038,7 @@ TEST(FrameBuilder, shadow) { }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(parent), sLightCenter); + TestUtils::createSyncedNodeList(parent), sLightCenter); ShadowTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()); @@ -1086,7 +1079,7 @@ TEST(FrameBuilder, shadowSaveLayer) { }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(parent), (Vector3) { 100, 100, 100 }); + TestUtils::createSyncedNodeList(parent), (Vector3) { 100, 100, 100 }); ShadowSaveLayerTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(5, renderer.getIndex()); @@ -1132,7 +1125,7 @@ RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { layer.setWindowTransform(windowTransform); *layerHandle = &layer; - auto syncedList = createSyncedNodeList(parent); + auto syncedList = TestUtils::createSyncedNodeList(parent); LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, @@ -1165,7 +1158,7 @@ TEST(FrameBuilder, shadowLayering) { }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - createSyncedNodeList(parent), sLightCenter); + TestUtils::createSyncedNodeList(parent), sLightCenter); ShadowLayeringTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); @@ -1193,7 +1186,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, - createSyncedNodeList(node), sLightCenter); + TestUtils::createSyncedNodeList(node), sLightCenter); PropertyTestRenderer renderer(opValidateCallback); frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; @@ -1332,7 +1325,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 10000, 10000, paint); }); - auto nodes = createSyncedNodeList(node); // sync before querying height + auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter); SaveLayerAlphaClipTestRenderer renderer(outObservedData); diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp new file mode 100644 index 000000000000..aa1dcb2ea51b --- /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/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp new file mode 100644 index 000000000000..41e44fc88a6b --- /dev/null +++ b/libs/hwui/tests/unit/LeakCheckTests.cpp @@ -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. + */ + +#include "BakedOpRenderer.h" +#include "BakedOpDispatcher.h" +#include "FrameBuilder.h" +#include "LayerUpdateQueue.h" +#include "RecordingCanvas.h" +#include "tests/common/TestUtils.h" + +#include <gtest/gtest.h> + +using namespace android; +using namespace android::uirenderer; + +const LayerUpdateQueue sEmptyLayerUpdateQueue; +const Vector3 sLightCenter = {100, 100, 100}; + +RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) { + auto node = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SkCanvas::SaveFlags)(0)); + canvas.drawRect(0, 0, 200, 200, SkPaint()); + canvas.restore(); + }); + BakedOpRenderer::LightInfo lightInfo = {50.0f, 128, 128}; + RenderState& renderState = renderThread.renderState(); + Caches& caches = Caches::getInstance(); + + FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, + TestUtils::createSyncedNodeList(node), sLightCenter); + BakedOpRenderer renderer(caches, renderState, true, lightInfo); + frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); +} diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp index e96e9ba55361..2fd879562265 100644 --- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp +++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp @@ -36,8 +36,8 @@ TEST(OffscreenBuffer, construct) { 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 @@ TEST(OffscreenBufferPool, resize) { 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 @@ TEST(OffscreenBufferPool, resize) { 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 6b2e265a61ff..b60e96c756ec 100644 --- a/libs/hwui/tests/unit/StringUtilsTests.cpp +++ b/libs/hwui/tests/unit/StringUtilsTests.cpp @@ -36,3 +36,18 @@ TEST(StringUtils, advancedBuildSet) { 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 055869f73c26..05a3d5931e5d 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 @@ public: 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 05b4a722d580..dcc49469b7f2 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 @@ public: 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 @@ private: 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 a09240852a38..ea1690fd2691 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -51,6 +51,7 @@ import java.util.ArrayList; 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 @@ public class AudioManager { } /** - * 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 @@ public class AudioManager { 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 @@ public class AudioManager { } + //==================================================================== + // Audio policy /** * @hide * Register the given {@link AudioPolicy}. @@ -2754,6 +2795,131 @@ public class AudioManager { } + //==================================================================== + // 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 index 94e86076308d..afe912b10b8f 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 000000000000..aefe692de651 --- /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 7bfd7ca4d56d..aa0d78dacd7e 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -227,7 +227,7 @@ public class AudioSystem } /** - * 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 @@ public class AudioSystem } } + /** + * 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 @@ public class AudioSystem // 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 dbb7661046c3..abe92c727cc5 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -20,9 +20,11 @@ import android.app.PendingIntent; 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 @@ interface IAudioService { 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 000000000000..7b4bc3962ff1 --- /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 000000000000..a5eb8b9fa787 --- /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/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 9bcb5e35938b..0fba9925b6c1 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -1735,8 +1735,7 @@ public final class MediaCodecInfo { 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 @@ public final class MediaCodecInfo { 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 a102e51de3d0..b2fa0acf7f57 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -92,8 +92,6 @@ public final class MediaFormat { 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 96c616be359d..dfe024a03ef8 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.Files.FileColumns; 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 @@ public class MediaScanner implements AutoCloseable { // 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 @@ public class MediaScanner implements AutoCloseable { 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 @@ public class MediaScanner implements AutoCloseable { } 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 @@ public class MediaScanner implements AutoCloseable { } 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 @@ public class MediaScanner implements AutoCloseable { 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 @@ public class MediaScanner implements AutoCloseable { }; // 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 @@ public class MediaScanner implements AutoCloseable { 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 @@ public class MediaScanner implements AutoCloseable { // 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 @@ public class MediaScanner implements AutoCloseable { 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 @@ public class MediaScanner implements AutoCloseable { } } - 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 d24c5e825fa7..4379a99cbb00 100644 --- a/media/java/android/mtp/MtpDevice.java +++ b/media/java/android/mtp/MtpDevice.java @@ -21,6 +21,8 @@ import android.hardware.usb.UsbDeviceConnection; import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; +import com.android.internal.util.Preconditions; + import java.io.IOException; /** @@ -164,12 +166,14 @@ public final class MtpDevice { * 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 @@ public final class MtpDevice { 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/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index 130dfe554f89..b1b3b621ee0b 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -372,12 +372,12 @@ android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jint 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) { @@ -385,6 +385,22 @@ android_mtp_MtpDevice_get_partial_object(JNIEnv *env, 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."); @@ -393,16 +409,13 @@ android_mtp_MtpDevice_get_partial_object(JNIEnv *env, 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 @@ -615,7 +628,7 @@ static const JNINativeMethod gMethods[] = { {"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}, diff --git a/packages/BackupRestoreConfirmation/res/values-de/strings.xml b/packages/BackupRestoreConfirmation/res/values-de/strings.xml index a2e24e728892..e1f797f276c9 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/DocumentsUI/res/drawable/ic_root_home.xml b/packages/DocumentsUI/res/drawable/ic_root_home.xml index 0a258ac65019..696ee0534b63 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/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index 180a48eaca99..be544967355f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -176,6 +176,12 @@ public abstract class BaseActivity extends Activity { return true; } + @Override + protected void onDestroy() { + mRoots.setOnCacheUpdateListener(null); + super.onDestroy(); + } + State buildDefaultState() { State state = new State(); @@ -518,7 +524,7 @@ public abstract class BaseActivity extends Activity { @Override protected void onPostExecute(DocumentInfo result) { - if (result != null) { + if (result != null && !isDestroyed()) { openContainerDocument(result); } } @@ -630,7 +636,7 @@ public abstract class BaseActivity extends Activity { @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/tests/src/com/android/documentsui/SearchViewUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java new file mode 100644 index 000000000000..baa7a2ee1786 --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import static com.android.documentsui.StubProvider.DEFAULT_AUTHORITY; +import static com.android.documentsui.StubProvider.ROOT_0_ID; +import static com.android.documentsui.StubProvider.ROOT_1_ID; + +import android.app.Instrumentation; +import android.content.ContentProviderClient; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.os.RemoteException; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.Configurator; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject; +import android.support.test.uiautomator.UiObjectNotFoundException; +import android.support.test.uiautomator.Until; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.documentsui.model.RootInfo; + +@LargeTest +public class SearchViewUiTest extends InstrumentationTestCase { + + private static final int TIMEOUT = 5000; + private static final String TAG = "SearchViewUiTest"; + private static final String TARGET_PKG = "com.android.documentsui"; + private static final String LAUNCHER_PKG = "com.android.launcher"; + + private UiBot mBot; + private UiDevice mDevice; + private Context mContext; + private ContentResolver mResolver; + private DocumentsProviderHelper mDocsHelper; + private ContentProviderClient mClient; + private RootInfo mRoot_0; + private RootInfo mRoot_1; + + private UiObject mSearchView; + private UiObject mSearchTextField; + private UiObject mDocsList; + private UiObject mMessageTextView; + private UiObject mSearchIcon; + + public void setUp() throws Exception { + // Initialize UiDevice instance. + Instrumentation instrumentation = getInstrumentation(); + + mDevice = UiDevice.getInstance(instrumentation); + + Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE); + + // Start from the home screen. + mDevice.pressHome(); + mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), TIMEOUT); + + // NOTE: Must be the "target" context, else security checks in content provider will fail. + mContext = instrumentation.getTargetContext(); + mResolver = mContext.getContentResolver(); + + mClient = mResolver.acquireUnstableContentProviderClient(DEFAULT_AUTHORITY); + mDocsHelper = new DocumentsProviderHelper(DEFAULT_AUTHORITY, mClient); + + // Launch app. + Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivity(intent); + // Wait for the app to appear. + mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), TIMEOUT); + mDevice.waitForIdle(); + + mBot = new UiBot(mDevice, TIMEOUT); + + resetStorage(); // Just incase a test failed and tearDown didn't happen. + + initUiObjects(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + mDevice.pressBack(); + resetStorage(); + mClient.release(); + } + + private void resetStorage() throws RemoteException { + mClient.call("clear", null, null); + // TODO: Would be nice to have an event to wait on here. + mDevice.waitForIdle(); + } + + private void initTestFiles() throws RemoteException { + mRoot_0 = mDocsHelper.getRoot(ROOT_0_ID); + mRoot_1 = mDocsHelper.getRoot(ROOT_1_ID); + + mDocsHelper.createDocument(mRoot_0, "text/plain", "file10.log"); + mDocsHelper.createDocument(mRoot_0, "image/png", "file1.png"); + mDocsHelper.createDocument(mRoot_0, "text/csv", "file2.csv"); + + mDocsHelper.createDocument(mRoot_1, "text/plain", "anotherFile0.log"); + mDocsHelper.createDocument(mRoot_1, "text/plain", "poodles.text"); + } + + private void initUiObjects() { + mSearchView = mBot.findSearchView(); + mSearchTextField = mBot.findSearchViewTextField(); + mDocsList = mBot.findDocumentsList(); + mMessageTextView = mBot.findMessageTextView(); + mSearchIcon = mBot.findSearchViewIcon(); + } + + public void testSearchViewExpandsOnClick() throws Exception { + assertTrue(mSearchIcon.exists()); + assertFalse(mSearchTextField.exists()); + + mSearchView.click(); + + assertTrue(mSearchTextField.exists()); + assertTrue(mSearchTextField.isFocused()); + assertFalse(mSearchIcon.exists()); + } + + public void testSearchViewCollapsesOnBack() throws Exception { + assertTrue(mSearchIcon.exists()); + assertFalse(mSearchTextField.exists()); + + mSearchView.click(); + + mDevice.pressBack(); + + assertTrue(mSearchIcon.exists()); + assertFalse(mSearchTextField.exists()); + } + + public void testSearchViewClearsTextOnBack() throws Exception { + assertTrue(mSearchIcon.exists()); + assertFalse(mSearchTextField.exists()); + + String query = "file2"; + mSearchView.click(); + mSearchTextField.setText(query); + + assertSearchTextField(true, query); + + mDevice.pressBack(); + + assertTrue(mSearchIcon.exists()); + assertFalse(mSearchTextField.exists()); + } + + public void testSearchFound() throws Exception { + initTestFiles(); + + mBot.openRoot(ROOT_0_ID); + + assertDefaultTestDir0(); + + String query = "file1"; + mSearchView.click(); + mSearchTextField.setText(query); + + assertTrue(mDocsList.exists()); + assertSearchTextField(true, query); + + mDevice.pressEnter(); + + assertTrue(mDocsList.exists()); + assertEquals(2, mDocsList.getChildCount()); + mBot.assertHasDocuments("file1.png", "file10.log"); + assertSearchTextField(false, query); + } + + public void testSearchFoundClearsOnBack() throws Exception { + initTestFiles(); + + mBot.openRoot(ROOT_0_ID); + + assertDefaultTestDir0(); + + String query = "file1"; + mSearchView.click(); + mSearchTextField.setText(query); + + mDevice.pressEnter(); + mDevice.pressBack(); + + assertDefaultTestDir0(); + } + + public void testSearchNoResults() throws Exception { + initTestFiles(); + + mBot.openRoot(ROOT_0_ID); + + assertDefaultTestDir0(); + + String query = "chocolate"; + mSearchView.click(); + mSearchTextField.setText(query); + + mDevice.pressEnter(); + + assertFalse(mDocsList.exists()); + assertTrue(mMessageTextView.exists()); + assertEquals(mContext.getString(R.string.empty), mMessageTextView.getText()); + assertSearchTextField(false, query); + } + + public void testSearchNoResultsClearsOnBack() throws Exception { + initTestFiles(); + + mBot.openRoot(ROOT_0_ID); + + assertDefaultTestDir0(); + + String query = "chocolate"; + mSearchView.click(); + mSearchTextField.setText(query); + + mDevice.pressEnter(); + mDevice.pressBack(); + + assertDefaultTestDir0(); + } + + public void testSearchFoundClearsDirectoryChange() throws Exception { + initTestFiles(); + + mBot.openRoot(ROOT_0_ID); + + assertDefaultTestDir0(); + + String query = "file1"; + mSearchView.click(); + mSearchTextField.setText(query); + + mDevice.pressEnter(); + + mBot.openRoot(ROOT_1_ID); + + // This assert is failing right now - fix will come with SearchManager refactoring + // assertDefaultTestDir1(); + // + // mBot.openRoot(ROOT_0_ID); + // + // assertDefaultTestDir0(); + } + + private void assertDefaultTestDir0() throws UiObjectNotFoundException { + assertTrue(mSearchIcon.exists()); + assertTrue(mDocsList.exists()); + assertFalse(mSearchTextField.exists()); + assertEquals(3, mDocsList.getChildCount()); + mBot.assertHasDocuments("file2.csv", "file1.png", "file10.log"); + } + + private void assertDefaultTestDir1() throws UiObjectNotFoundException { + assertTrue(mSearchIcon.exists()); + assertFalse(mSearchTextField.exists()); + assertTrue(mDocsList.exists()); + assertEquals(2, mDocsList.getChildCount()); + mBot.assertHasDocuments("anotherFile0.log", "poodles.txt"); + } + + private void assertSearchTextField(boolean isFocused, String query) + throws UiObjectNotFoundException { + assertFalse(mSearchIcon.exists()); + assertTrue(mSearchTextField.exists()); + assertEquals(isFocused, mSearchTextField.isFocused()); + assertEquals(query, mSearchTextField.getText()); + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java index 50f462807bfe..fb6445bd1c55 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java @@ -135,7 +135,8 @@ public class StubProvider extends DocumentsProvider { final RootInfo info = entry.getValue(); final RowBuilder row = result.newRow(); row.add(Root.COLUMN_ROOT_ID, id); - row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD); + row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD + | Root.FLAG_SUPPORTS_SEARCH); row.add(Root.COLUMN_TITLE, id); row.add(Root.COLUMN_DOCUMENT_ID, info.document.documentId); row.add(Root.COLUMN_AVAILABLE_BYTES, info.getRemainingCapacity()); @@ -270,6 +271,29 @@ public class StubProvider extends DocumentsProvider { } @Override + public Cursor querySearchDocuments(String rootId, String query, String[] projection) + throws FileNotFoundException { + + StubDocument parentDocument = mRoots.get(rootId).document; + if (parentDocument == null || parentDocument.file.isFile()) { + throw new FileNotFoundException(); + } + + final MatrixCursor result = new MatrixCursor( + projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); + + for (File file : parentDocument.file.listFiles()) { + if (file.getName().toLowerCase().contains(query)) { + StubDocument document = mStorage.get(getDocumentIdForFile(file)); + if (document != null) { + includeDocument(result, document); + } + } + } + return result; + } + + @Override public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal) throws FileNotFoundException { final StubDocument document = mStorage.get(docId); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java index 68cdf128d96c..c4def8f600eb 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UiBot.java @@ -187,4 +187,41 @@ class UiBot { mDevice.wait(Until.findObject(selector), mTimeout); return mDevice.findObject(selector); } + + private UiObject findObject(String resourceId) { + final UiSelector object = new UiSelector().resourceId(resourceId); + return mDevice.findObject(object); + } + + private UiObject findObject(String parentResourceId, String childResourceId) { + final UiSelector selector = new UiSelector() + .resourceId(parentResourceId) + .childSelector(new UiSelector().resourceId(childResourceId)); + return mDevice.findObject(selector); + } + + UiObject findDocumentsList() { + return findObject( + "com.android.documentsui:id/container_directory", + "com.android.documentsui:id/list"); + } + + UiObject findSearchView() { + return findObject("com.android.documentsui:id/menu_search"); + } + + UiObject findSearchViewTextField() { + return findObject("com.android.documentsui:id/menu_search", "android:id/search_src_text"); + } + + UiObject findSearchViewIcon() { + return findObject("com.android.documentsui:id/menu_search", "android:id/search_button"); + } + + UiObject findMessageTextView() { + return findObject( + "com.android.documentsui:id/container_directory", + "com.android.documentsui:id/message"); + } + } diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml index dea5fa08e68b..746457fa4e7b 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 b03871a4a50e..64b443babde7 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -123,39 +123,45 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout 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 29b319ff9c26..2a2f5a0c9730 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java @@ -227,22 +227,23 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit 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 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } } - 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 836c195d2a1c..232d4d298e3a 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java @@ -36,11 +36,12 @@ public interface KeyguardSecurityCallback { /** * 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 f45b9bd70f61..409f6a700ff3 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -205,7 +205,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe // 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe showWipeDialog(failedAttempts, userType); } } - monitor.reportFailedStrongAuthUnlockAttempt(); - mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser()); + monitor.reportFailedStrongAuthUnlockAttempt(userId); + mLockPatternUtils.reportFailedPasswordAttempt(userId); if (timeoutMs > 0) { showTimeoutDialog(timeoutMs); } @@ -422,14 +422,13 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe 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 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe @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 8102c34755a8..b4f9b9f4a653 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1552,12 +1552,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 f9fb85c446ad..cde28fce1cc5 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 @@ struct FuseRequest { } }; +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 @@ public: }; /** - * 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ jboolean com_android_mtp_AppFuse_start_app_fuse_loop( 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 9c726ba24ce6..0d81a30fbaaf 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java @@ -188,6 +188,15 @@ class MtpManager { } } + @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/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java index 7527f549e87f..25e990011a54 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java @@ -21,8 +21,10 @@ 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; @@ -98,6 +100,52 @@ public class MtpManagerTest extends InstrumentationTestCase { } } + 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 6179b4ba7457..57ba2b68c69c 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 2f94e1efb5eb..a2182fb54748 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 29767c34f7a4..eab1339a4d21 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 3316b30e02e8..bff477d53454 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 86baf10114e1..b28aa29304c0 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 2c02b74cb48e..e8de8ea96e90 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 d48d91d8c7ae..a1ca4945327a 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 d0fd75ede8aa..aa6f9925fda5 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 bec34aca53be..4bc22d40a1be 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 de2914614fc1..b8be624169e4 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 054454ed3b00..0b771864ee6d 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 abae9614ff1d..d9a4aeba4ed6 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 b656dcdbea63..d8a94372938f 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 b656dcdbea63..d8a94372938f 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 b656dcdbea63..d8a94372938f 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 712bb8099a00..19c695b04d20 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 dfd6a6259070..d13ccda2bff2 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 7b962afe81f7..f03eb37588ad 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 4551cc293e06..d4255e29b409 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 d85978d8c403..907123c0c753 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 44724597d184..f57b8843a72f 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 1d4531536639..949ba556d7f6 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 df7dfef0f691..1fcc0400bbac 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 eae645008870..2e60960bc997 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 8028274ecdf9..4ba969c1cc16 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 5bfcc6e02c3c..106134635bff 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 bf244c5944ba..4a7d29f41534 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 6c608f1f0ff2..5aae2e41eb3d 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 801410b5b575..179c384110e6 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 227a372bd577..7286f7aae7e4 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 f078ff84c4c7..9ea49a92a75c 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 10b3601b33fd..c19d012ec865 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 8a04c7683139..00bf27c69ebd 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 a132fb1d85ed..e0fc79a9b26f 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 675245b6acea..ad1468a6c103 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 4c7c4f4a1787..d0337a6d97c2 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 fac7c0c9ad5b..c9431e988ce4 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 61279fc46fc7..fc5149a495cb 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 fe47e55aff0d..2faff1f97837 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 79b38d1658f0..a01e4a8aad1b 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 3140a258c05f..b5d13b54fdbc 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 4f0772ed8fd5..3b8f14312d60 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 0efa50fdf646..762d0bd1d26d 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 2805dee55689..de6d3e9656e7 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 9617a7a3f65a..7a33e14e0e6a 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 59bb9bf2e136..c94e56d2fedc 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 2b6661e0f491..ab25010a7d57 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 c0666273dd0b..917ae8a7222f 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 a63e85e15805..4d4c95bd7d29 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 8128011e53fd..9efa5d1b923e 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 89b2fbb8f07c..281a65d97fb8 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 70b93e6df0ad..eef9880283df 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 da819194a582..7d7860cca1f0 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 b7613cf63b6e..6837edf77996 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 199f304d6fc6..c9713c9e7efb 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 ea94c5591b6b..9fabc0fced5f 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 199f304d6fc6..c9713c9e7efb 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 1c74aab5d2b9..7364eb0dae2c 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 e900ab9793f3..bb038602eda5 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 5731849b146f..610442d25141 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 1c7b740a046b..603d1d2242f3 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 4c794dc895be..4a0826944448 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 aff55f6fbe85..b0902efb7540 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 043048e3acb8..feb2940d7da8 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 fd8581fd21a7..cf398c78fb56 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 0950f57868a5..7e00b7046423 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 bf6feaec706e..ae0b774a5911 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 4d14b8c80b86..5fd8d60ba573 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 e1f82e1bcf9e..ebd5e2a9c7a8 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 052d8ccffa41..ebe869bc0ab2 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 7f9e6f517d6a..9cd42abd42bf 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 d34aa2a88b4a..1082147d3b6d 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 ccf6f56e6f31..56f1093ba71e 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 d1fc47fa1263..30b218e420d1 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 eb5de95776a5..32aaf6396de1 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 e39849ebd1c1..42cf3b14bf47 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 11482262cb6b..0a458ada1fcf 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 c0dd3ded7827..7a30011ac02b 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 231b1bf78043..f57b58c87609 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 9c1cf645320d..811adda74390 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 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) || (mPrinter.getCapabilities() == null && printer.getCapabilities() != null); - mPrinter.copyFrom(printer); + mPrinter = printer; } if (available) { @@ -2394,7 +2394,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mPrinterAvailabilityDetector.updatePrinter(newPrinterState); - oldPrinterState.copyFrom(newPrinterState); + mCurrentPrinter = newPrinterState; if ((isActive && gotCapab) || (becameActive && hasCapab)) { if (hasCapab && capabChanged) { diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index ebe8752777f6..30a3b5926b47 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 e33d0fa9dace..beac0a186469 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 71761b99a235..2386cb6d2ee7 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 b5a718116e30..273ea3055e3c 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 3fd4f7fb60f0..5d8b9347d575 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 cd4529f111d5..e5dbf5009e2d 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 7c598229ca4e..9e42f71d7334 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 1349a7aaf7ce..2f81098814e7 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 beb8f94abebc..f1c9e2709ec5 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 3923dbd7326b..90ea69a7f156 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 4d9d4fbbc7dd..8d95ddfb4e2e 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 c3acca719754..303f0e570cd4 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 833fb62d95ac..b938ace851a9 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 833fb62d95ac..b938ace851a9 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 833fb62d95ac..b938ace851a9 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 4d4ab24b8e18..9ddc03f813a8 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 858c1b6d31b1..f0682c2cf935 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 7f1056ca077d..ff7d7eebdb3c 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 6405ca83d857..145e8f8b8b35 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 d591426d8799..53806514ebb2 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 ce36de367c53..f9fdb63f90fd 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 3ee07759f258..9f802abb631a 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 c0098d240e19..6dabba8b2fbd 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 0efe805be218..0f0d13d161d4 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 2a0ff45f705e..9e2ec7e09dda 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 5224c380e125..b6b8eacbd98f 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 0a46b279d377..0ab13f6fa7e7 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 3559f1e23646..26d65fbaf6af 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 fb401f0e1415..7a9b16f87e40 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 1262512210dc..f2eaccf90e6b 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 3e53e94df5b8..dc105eb3b066 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 2d98a7403041..eedfa333de0c 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 54099c7364cc..ec8f1a561c3b 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 7e45fb216d1b..9a32d44d0d1a 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 a8f25c6ed936..0cfb6ecee51c 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 300aaa374492..b62952256f23 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 a414668504af..1155c0711b9a 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 50e5955f272d..948c665315bc 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 c23a8a71bfb6..7f5cee2a5b0e 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 84bfe94f0bc6..c00e827dffb8 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 4b5359a2eb53..6dd30978d3a4 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 6b2104144a2e..6010c7aad773 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 0652b054048c..d50e1ea88463 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 b588c2c986b9..03feddd2032b 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 2457847cc0bb..e47afc456388 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 622c13fab2df..01925f784734 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 ed57fb73e530..804fab080885 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 5f27232b80d1..ed4d0712e573 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 0a86cdb8cae6..ab7a8d080ef6 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 99ee44c2ba8c..e39cf30bf50a 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 a109197724c6..d9cfbccb2638 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 1dd477dd7f92..4873ac3dd561 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 85f9ffb284f4..61f187d1e3d1 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 9f7c022de110..0189fab1a031 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 98a3121bd83e..ee701a658cd2 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 0cbe5e4e7f86..2f25ae2ab1fd 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 98a3121bd83e..ee701a658cd2 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 00ff92bb2af1..29d878f8dba5 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 9a3a7e6f254f..ba21d3edf611 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 083dd05b438a..0f32a0c4aabd 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 e12f4357e92a..8df02ea68c06 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 1f665831918e..5d47af9fd7c0 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 7f4d6a864af9..8e8112f9e407 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 ab96afd6a1a8..935f44d36cb9 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 f8901c94a5cc..1bdf6b20afc7 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 8a33946ad7d6..8a5ddd0ea2db 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 d3860391fb78..42a553470141 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 80b84f06cc09..04f0ca07e87f 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 b17b516b3a14..241fd06d2ea9 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 d0e7f87770c2..d1a525b805dd 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 e6ddcdd5f733..d4c211c431ab 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 2448b2326ae7..3e89ccecbe53 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 e9fe7e40a47b..0a2adf7fc4ef 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 e53336b56daf..13494a5bc1fc 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 193f066ce84b..81b87dafc2b3 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 de8996ef5386..5ffcc0ca56eb 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 9a86f3ac88f4..8eedf6de257f 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 3ab60f5ab6ad..10d2e01f326d 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 bf3addfa3327..f42abbc21414 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/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java index f6caaa9642c4..2706e255d2af 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java @@ -190,18 +190,27 @@ public class RestrictedLockUtils { * Send the intent to trigger the {@link android.settings.ShowAdminSupportDetailsDialog}. */ public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) { - Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS); + 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); } - context.startActivityAsUser(intent, new UserHandle(adminUserId)); + return intent; } public static void setTextViewPadlock(Context context, @@ -224,6 +233,34 @@ public class RestrictedLockUtils { 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 index 569017a6af36..13a46d093549 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java @@ -79,6 +79,15 @@ public class RestrictedPreference extends Preference { 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(); diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java index f04150460309..06aba968ea9e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java @@ -45,7 +45,7 @@ public class RestrictedPreferenceHelper { private EnforcedAdmin mEnforcedAdmin; private String mAttrUserRestriction = null; - RestrictedPreferenceHelper(Context context, Preference preference, + public RestrictedPreferenceHelper(Context context, Preference preference, AttributeSet attrs) { mContext = context; mPreference = preference; @@ -54,21 +54,21 @@ public class RestrictedPreferenceHelper { mRestrictedPadlockPadding = mContext.getResources().getDimensionPixelSize( R.dimen.restricted_lock_icon_padding); - mAttrUserRestriction = attrs.getAttributeValue( - R.styleable.RestrictedPreference_userRestriction); - 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; + 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(); } - mAttrUserRestriction = data == null ? null : data.toString(); } /** @@ -100,7 +100,7 @@ public class RestrictedPreferenceHelper { /** * Disable / enable if we have been passed the restriction in the xml. */ - protected void onAttachedToHierarchy() { + public void onAttachedToHierarchy() { if (mAttrUserRestriction != null) { checkRestrictionAndSetDisabled(mAttrUserRestriction, UserHandle.myUserId()); } diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java index 308477b0c84b..84e2bff4fc40 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java @@ -79,6 +79,15 @@ public class RestrictedSwitchPreference extends SwitchPreference { 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(); diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 72df96d65637..fa2226d229be 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -2,7 +2,11 @@ package com.android.settingslib; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; +import android.content.pm.Signature; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -138,4 +142,33 @@ public class Utils { return statusString; } + + /** + * Determine whether a package is a "system package", in which case certain things (like + * disabling notifications or disabling the package altogether) should be disallowed. + */ + public static boolean isSystemPackage(PackageManager pm, PackageInfo pkg) { + if (sSystemSignature == null) { + sSystemSignature = new Signature[]{ getSystemSignature(pm) }; + } + return sSystemSignature[0] != null && sSystemSignature[0].equals(getFirstSignature(pkg)); + } + + private static Signature[] sSystemSignature; + + private static Signature getFirstSignature(PackageInfo pkg) { + if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) { + return pkg.signatures[0]; + } + return null; + } + + private static Signature getSystemSignature(PackageManager pm) { + try { + final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES); + return getFirstSignature(sys); + } catch (NameNotFoundException e) { + } + return null; + } } 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 000000000000..77f2e19abd97 --- /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 d994841d64f9..7ee53a2094f2 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 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe 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 index f935f31cad8e..9c5abf3a00dd 100644..100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java @@ -162,6 +162,10 @@ public final class LocalBluetoothAdapter { 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 index 8f5e1f199fad..b05e34c02ef6 100644..100755 --- 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 @@ public final class LocalBluetoothProfileManager { private final BluetoothEventManager mEventManager; private A2dpProfile mA2dpProfile; + private A2dpSinkProfile mA2dpSinkProfile; private HeadsetProfile mHeadsetProfile; private MapProfile mMapProfile; private final HidProfile mHidProfile; @@ -136,10 +138,10 @@ public final class LocalBluetoothProfileManager { * @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 @@ public final class LocalBluetoothProfileManager { 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 @@ public final class LocalBluetoothProfileManager { if (profile != null) { return profile.isProfileReady(); } + profile = mA2dpSinkProfile; + if (profile != null) { + return profile.isProfileReady(); + } return false; } @@ -295,6 +312,13 @@ public final class LocalBluetoothProfileManager { return mA2dpProfile; } + A2dpSinkProfile getA2dpSinkProfile() { + if ((mA2dpSinkProfile != null)&&(mA2dpSinkProfile.isProfileReady())) + return mA2dpSinkProfile; + else + return null; + } + public HeadsetProfile getHeadsetProfile() { return mHeadsetProfile; } @@ -345,6 +369,12 @@ public final class LocalBluetoothProfileManager { 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/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index cff8c236378e..2ee4b122e73a 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 class AccessPoint implements Comparable<AccessPoint> { } 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 @@ public class AccessPoint implements Comparable<AccessPoint> { 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 @@ public class AccessPoint implements Comparable<AccessPoint> { 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 @@ public class AccessPoint implements Comparable<AccessPoint> { } 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 @@ public class AccessPoint implements Comparable<AccessPoint> { 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 @@ public class AccessPoint implements Comparable<AccessPoint> { 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 @@ public class AccessPoint implements Comparable<AccessPoint> { 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 @@ public class AccessPoint implements Comparable<AccessPoint> { 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 f9f1d3f2fb33..2317dbca756b 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 @@ public class WiFiConfigurationSerializer { */ 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 @@ public class WiFiConfigurationSerializer { 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 @@ public class WiFiConfigurationSerializer { 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 @@ public class WiFiConfigurationSerializer { 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 @@ public class WiFiConfigurationSerializer { 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 @@ public class WiFiConfigurationSerializer { } /** + * + */ + 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 @@ public class WiFiConfigurationSerializer { 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 @@ public class WiFiConfigurationSerializer { 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 @@ public class WiFiConfigurationSerializer { 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 @@ public class WiFiConfigurationSerializer { 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 5c807e1bd652..df8fad4dd522 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.HandlerThread; 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 @@ import android.widget.Toast; * </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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { * 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { * 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 @@ public class BugreportProgressService extends Service { /** * 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 @@ public class BugreportProgressService extends Service { 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 @@ public class BugreportProgressService extends Service { } 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 @@ public class BugreportProgressService extends Service { + "\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 c8898b97d878..9afa9005c43b 100644 --- a/packages/Shell/src/com/android/shell/BugreportReceiver.java +++ b/packages/Shell/src/com/android/shell/BugreportReceiver.java @@ -19,7 +19,6 @@ package com.android.shell; 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 @@ import android.util.Log; * {@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 @@ public class BugreportReceiver extends BroadcastReceiver { 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 d1a07ea21fc4..52e1b56d16d7 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.text.format.DateUtils; import android.util.Log; import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener; +import com.android.shell.BugreportProgressService; /** * Integration tests for {@link BugreportReceiver}. @@ -89,6 +90,9 @@ public class BugreportReceiverTest extends InstrumentationTestCase { // 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { @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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { // 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { } /** + * 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 @@ public class BugreportReceiverTest extends InstrumentationTestCase { 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 6201fd6632cd..3dc339dbadd5 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 bc182215c0d4..2ea8c9ca9064 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 000000000000..90e0bceadbe6 --- /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 57f7e6528e61..35c7a4fe754b 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/drawable/car_ic_arrow.xml b/packages/SystemUI/res/drawable/car_ic_arrow.xml new file mode 100644 index 000000000000..9d292cc0259a --- /dev/null +++ b/packages/SystemUI/res/drawable/car_ic_arrow.xml @@ -0,0 +1,27 @@ +<!-- + ~ 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="48.0dp" + android:height="48.0dp" + android:viewportWidth="48.0" + android:viewportHeight="48.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M14.0,20.0l10.0,10.0 10.0,-10.0z"/> + <path + android:pathData="M0 0h48v48H0z" + android:fillColor="#00000000"/> +</vector> diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/SystemUI/res/layout/car_navigation_button.xml new file mode 100644 index 000000000000..87c8f04a19bf --- /dev/null +++ b/packages/SystemUI/res/layout/car_navigation_button.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2016, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.systemui.statusbar.car.CarNavigationButton + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:orientation="horizontal" + android:gravity="center"> + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/car_nav_button_icon" + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:layout_centerInParent="true" + android:animateLayoutChanges="true"> + </com.android.keyguard.AlphaOptimizedImageButton> + + <com.android.keyguard.AlphaOptimizedImageButton + android:id="@+id/car_nav_button_more_icon" + android:layout_height="match_parent" + android:layout_width="wrap_content" + android:layout_centerVertical="true" + android:layout_toRightOf="@+id/car_nav_button_icon" + android:animateLayoutChanges="true"> + </com.android.keyguard.AlphaOptimizedImageButton> +</com.android.systemui.statusbar.car.CarNavigationButton> diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml index 071b7c8a6da8..03451b43b5e2 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 000000000000..603ebbfc8ffb --- /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 af22f03c606a..a22c3603632e 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 5b44c178f872..c813818ed568 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 04f18c527c68..07ac39a3ecea 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 83cfc764066c..cce07bdaa45d 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 01879935ee6b..19fb28ed0f60 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 503a76709648..208aaffe28a5 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 d89d0362be2d..35e78c2971d7 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 55f08419dca6..864fd324771f 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 4056963c8e6a..6879d750a218 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 93c26a4dacfa..f5713c2f7acf 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 939465b64d29..bfcb258d9bba 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 e19aa0efe6e2..c7a30f013b70 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 b1cd561f63c8..df18347478dd 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 2100c57a2b48..d69e943154c5 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 1ad5b26eb543..9e17f2538bfa 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 f56d63016128..fb096697e9a6 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 f6fc9976d4fb..a96c23cb7a82 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 f6fc9976d4fb..a96c23cb7a82 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 f6fc9976d4fb..a96c23cb7a82 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 fd59418c3048..6ba8a4361f61 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 223e684e3a0f..059807837b9e 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 f50e78cba01c..8d25d4893bbd 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 c481f521362b..fc96b42e7fde 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 3dee7c9be11b..3a98e9c112ee 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 4a5c3026bd84..8c963af5d49f 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 2a4f4d9835c2..bcde17a5dfc4 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 928a8b30579e..952baab3e033 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 2a951f726808..9d490c24c032 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 f331ef90cdcb..d45167ff5944 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 b31c1e0fd251..3e2ab2a30a13 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 3bef6e993024..fbdc5fe1bd3f 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 8dc9a193ae66..9d26c36b5a9c 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 99d6980ae77c..29e3a2987e90 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 f6124db7f133..f7f3370f1d31 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 7ea1746b40e6..c1d3591ab38a 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 b395f8b2a55d..dd65970ad7a8 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 2c894f567267..95bbe20d46b5 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 580d995a4957..ed0c7e9e9622 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 7a51668f09ae..5c861f8d0779 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 eb8995e0ab7c..42e6a5de9493 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 5c102131b9e7..53f5868739fa 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 28b2de55f406..1b212f04d58f 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 110376fb04f7..ecff2ad6836f 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 ee2ef5d78e27..ad430ec3a023 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 ed103ea4d089..48d94cdd5d33 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 ba98e2b1df9e..adb5a53131b9 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 0143fea7c7bb..b8a6d01b2107 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 c57313936b6f..8ba8094de7a4 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 c6a08e600e17..240373bfb589 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 91c817a5caf4..de5c03565bbe 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 5d3a96f7721e..80dc12e1dfc2 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 f0a013c4f93a..ec4b28c13ee5 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 75008cbd5004..257dd432af1d 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 295a49a8f555..4446a7788dad 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 d0aa5103532a..f100955091bb 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 75812ad3e2ca..c6562d688d87 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 fc5a3eaa44ed..fd17dac2f1cf 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 8ef50c993d5b..3630429a699e 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 8f614781c84f..7fa28ad3c747 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 8a727a1e4024..3d29d7eb175d 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 8f614781c84f..7fa28ad3c747 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 3ab8bf2a534f..13f3ef23ec86 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 6ffd3330822e..4d7ee69034d6 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 efbc0423e1dc..f73ff6c1f3aa 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 31985b4e9624..ddf2de8a5730 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 7c8d443ba66b..249c1adb8300 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 c8add9497b31..60e2e80ad0d8 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 bec1fd4d4217..4954f05e4938 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 d4a9cdc31b3e..8f67f0edd31e 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 2ffbf2ba3c19..0f668268b869 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 168a11b4f07a..06538432d988 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 086dede50a14..f711544912e5 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 8c1a7d35f680..8c284a12692a 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 4bee3eab3b26..33e5968ce4ca 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 c0ecc780f36e..d937dc31e51a 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 31094033aa66..5503cb81bd2b 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 52aa58032c8a..f3dea976ad66 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 a849cfc6de21..c78108243c79 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 fa835ba0153e..38283ad18759 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 1e2dba35ef38..8534d825210b 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 0782354a619d..0f0ac6964dbb 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 29520f6f38fc..3139e41c312e 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 3ee46eaa1ef2..b93596a0175b 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/arrays_car.xml b/packages/SystemUI/res/values/arrays_car.xml index 230479d2c9db..8c760fc45e72 100644 --- a/packages/SystemUI/res/values/arrays_car.xml +++ b/packages/SystemUI/res/values/arrays_car.xml @@ -22,7 +22,9 @@ isn't a longpress action associated with a shortcut item, put in an empty item to make sure everything lines up. --> - <array name="car_shortcut_icons" /> - <array name="car_shortcut_intent_uris" /> - <array name="car_shortcut_longpress_intent_uris" /> + <array name="car_facet_icons" /> + <array name="car_facet_intent_uris" /> + <array name="car_facet_longpress_intent_uris" /> + <array name="car_facet_package_filters"/> + <array name="car_facet_category_filters"/> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 905da1388f4c..a80a5de3a4ec 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 955af823a44f..e98ec825f4a0 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 097c35266f98..3fb5f182c1b8 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 4136c110c649..de4967743eae 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 527b6380d2f3..4329f7830c4d 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 90cd3949bfcc..11ef735d3ac0 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 870e0af5820a..fabc7b771e19 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -86,6 +86,7 @@ public class SwipeHelper implements Gefingerpoken { 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 @@ public class SwipeHelper implements Gefingerpoken { 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 @@ public class SwipeHelper implements Gefingerpoken { } } - private float getSize(View v) { + protected float getSize(View v) { return mSwipeDirection == X ? v.getMeasuredWidth() : v.getMeasuredHeight(); } @@ -178,10 +183,12 @@ public class SwipeHelper implements Gefingerpoken { 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 @@ public class SwipeHelper implements Gefingerpoken { 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 @@ public class SwipeHelper implements Gefingerpoken { 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 19e299254aef..2d056bfc8a9a 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -80,6 +80,8 @@ public class SystemUIApplication extends Application { // 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 000000000000..681b39e1ba43 --- /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 443778e525d5..9a00b4b906c4 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.KeyguardUpdateMonitor; 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 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { } } + @Override public void keyguardDoneDrawing() { mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING); } @@ -588,8 +592,9 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { 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 @@ public class KeyguardViewMediator extends SystemUI { mHandler.removeMessages(KEYGUARD_DONE_PENDING_TIMEOUT); } + @Override public void onBootCompleted() { mUpdateMonitor.dispatchBootCompleted(); synchronized (this) { @@ -1731,10 +1738,15 @@ public class KeyguardViewMediator extends SystemUI { 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 01eb5f941fee..7651ae8c6f8f 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 @@ public class QSIconView extends ViewGroup { } } } - + 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 91f88b9b6736..fd07e50c18a6 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 @@ public class QSPanel extends FrameLayout implements Tunable { 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 @@ public class QSPanel extends FrameLayout implements Tunable { 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 @@ public class QSPanel extends FrameLayout implements Tunable { } 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 8ce6da2eede9..de7c02dddb92 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.graphics.drawable.Drawable; 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.UserInfoController; 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 @@ public abstract class QSTile<TState extends State> implements Listenable { 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 @@ public abstract class QSTile<TState extends State> implements Listenable { // 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 @@ public abstract class QSTile<TState extends State> implements Listenable { // 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 @@ public abstract class QSTile<TState extends State> implements Listenable { 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 @@ public abstract class QSTile<TState extends State> implements Listenable { } 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 @@ public abstract class QSTile<TState extends State> implements Listenable { 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 abstract class QSTile<TState extends State> implements Listenable { 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 @@ public abstract class QSTile<TState extends State> implements Listenable { 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 abstract class QSTile<TState extends State> implements Listenable { 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 @@ public abstract class QSTile<TState extends State> implements Listenable { || !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 @@ public abstract class QSTile<TState extends State> implements Listenable { 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 41ac4d9413dd..664ca39dbdb5 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 @@ package com.android.systemui.qs; 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 @@ public class QSTileView extends QSTileBaseView { 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 @@ public class QSTileView extends QSTileBaseView { 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 5fa38c6e0304..578280019830 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 @@ public class QuickQSPanel extends QSPanel { } @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 95ff611d7419..ac4f05fa148c 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 @@ package com.android.systemui.qs.customize; 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 @@ public class BlankCustomTile extends QSTile<QSTile.State> { 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 7448493b4880..eab4dca16573 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 @@ public class CustomQSPanel extends QSPanel { } @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 eefff30910a6..d398b644b9ac 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.os.RemoteException; 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 @@ public class CustomTile extends QSTile<QSTile.State> { mTile.setIcon(tile.getIcon()); mTile.setLabel(tile.getLabel()); mTile.setContentDescription(tile.getContentDescription()); + mTile.setState(tile.getState()); } public void onDialogShown() { @@ -147,6 +151,9 @@ public class CustomTile extends QSTile<QSTile.State> { @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 @@ public class CustomTile extends QSTile<QSTile.State> { @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 @@ public class CustomTile extends QSTile<QSTile.State> { 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 d41cdde37958..3830ac50c60b 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 @@ public class QSTileServiceWrapper { 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 8c5e87e1a0f6..4977d80ee030 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 @@ public class TileLifecycleManager extends BroadcastReceiver implements 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 @@ public class TileLifecycleManager extends BroadcastReceiver implements 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 @@ public class TileLifecycleManager extends BroadcastReceiver implements } @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 a831c87b9d86..44d8776bd44d 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 android.util.Log; 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 @@ public class TileServices extends IQSService.Stub { } @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 @@ public class TileServices extends IQSService.Stub { } } + @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 4d9b266a3513..39eda6b3b86e 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.Intent; 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 @@ public class DndTile extends QSTile<QSTile.BooleanState> { @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 @@ public class DndTile extends QSTile<QSTile.BooleanState> { 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 21c5c967ce96..167c61194b33 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 @@ public class UserDetailItemView extends LinearLayout { private TextView mName; private Typeface mRegularTypeface; private Typeface mActivatedTypeface; + private View mRestrictedPadlock; public UserDetailItemView(Context context) { this(context, null); @@ -59,6 +60,7 @@ public class UserDetailItemView extends LinearLayout { 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 @@ public class UserDetailItemView extends LinearLayout { 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 @@ public class UserDetailItemView extends LinearLayout { 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 d4f54b64adc2..b44ef0b46493 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 class UserDetailView extends PseudoGridView { 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 @@ public class UserDetailView extends PseudoGridView { v.bind(name, item.picture); } v.setActivated(item.isCurrent); + v.setDisabledByAdmin(item.isDisabledByAdmin); v.setTag(item); return v; } @@ -85,8 +89,14 @@ public class UserDetailView extends PseudoGridView { 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 e4d80672d30d..db55f286cf20 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } // 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD @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 class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD mIterateTrigger.poke(); } } + + MetricsLogger.action(this, MetricsLogger.ACTION_OVERVIEW_PAGE); } } @@ -701,7 +716,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD 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 67135d5621b4..61780f8bd8de 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 @@ public class RecentsConfiguration { } 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 c323522013d4..3151fd7f3e6f 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 @@ import com.android.systemui.tuner.TunerService; 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 @@ public class RecentsDebugFlags implements TunerService.Tunable { } private boolean mFastToggleRecents; + private boolean mFastToggleIndicator; private boolean mInitialStatePaging; /** @@ -58,7 +60,8 @@ public class RecentsDebugFlags implements TunerService.Tunable { 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 @@ public class RecentsDebugFlags implements TunerService.Tunable { } /** + * @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 @@ public class RecentsDebugFlags implements TunerService.Tunable { 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 ddeb8dcecf17..7c25d24326a9 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 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements /** 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 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements launchOpts.onlyLoadPausedActivities = true; loader.loadTasks(mContext, plan, launchOpts); } - */ } } @@ -375,7 +372,7 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements 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 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements 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 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements 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 @@ public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements 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 b0c8ff3c15b6..212c7f4b5e79 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 @@ public class EventBus extends BroadcastReceiver { } /** + * 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 @@ public class EventBus extends BroadcastReceiver { 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 cb5011a9970a..ad9feb6806fc 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 6e6cd8477d94..5a132c2f64e7 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 @@ import com.android.systemui.recents.events.EventBus; * 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 21321f297159..b85ddac62b19 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 @@ import com.android.systemui.recents.views.DropTarget; */ 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 171ab5e8bcca..def4ae16a59f 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 @@ import com.android.systemui.recents.events.EventBus; * 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 f0fa1da7810f..80597bc0bc14 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.View; 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 @@ public class RecentsHistoryAdapter extends RecyclerView.Adapter<RecentsHistoryAd 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 a2f5159a7ec3..39bb6cac56cd 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.WindowInsets; 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 @@ public class RecentsHistoryView extends LinearLayout { }); mAdapter.updateTasks(getContext(), stack); mIsVisible = true; + + MetricsLogger.visible(mRecyclerView.getContext(), MetricsLogger.OVERVIEW_HISTORY); } /** @@ -129,6 +132,8 @@ public class RecentsHistoryView extends LinearLayout { 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 3406da94e53c..01de60c5724e 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.util.Pair; 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 @@ public class SystemServicesProxy { } /** 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 @@ public class SystemServicesProxy { } /** - * @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 @@ public class SystemServicesProxy { 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 @@ public class SystemServicesProxy { 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 2bf2ccba77c7..33f116ba4316 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 @@ package com.android.systemui.recents.misc; 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 d8dfce568629..822ad77e71b8 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.graphics.drawable.Drawable; 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 @@ public class RecentsTaskLoadPlan { 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 @@ public class RecentsTaskLoadPlan { // 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 @@ public class RecentsTaskLoadPlan { // 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 @@ public class RecentsTaskLoadPlan { /** 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 f7e2b9db24af..29e7077ccf76 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 class Task { 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 @@ public class Task { 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 @@ public class Task { @Override public int hashCode() { - return Objects.hash(id, stackId, userId); + return mHashCode; } @Override @@ -90,6 +98,10 @@ public class Task { + "lat: " + lastActiveTime + ", " + getComponent().getPackageName(); } + + private void updateHashCode() { + mHashCode = Objects.hash(id, stackId, userId); + } } public TaskKey key; @@ -113,6 +125,7 @@ public class Task { public Bitmap thumbnail; public String title; public String contentDescription; + public String dismissDescription; public int colorPrimary; public boolean useLightOnPrimaryColor; @@ -139,9 +152,9 @@ public class Task { } 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 @@ public class Task { 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 @@ public class Task { 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 @@ public class Task { * 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 288f07c8d68b..15f6b0a671e2 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 @@ public class TaskGrouping { 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 @@ public class TaskGrouping { 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 856200df6529..21d0bb6fb264 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 com.android.systemui.recents.views.DropTarget; 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_BOTTOM_OR_RIG 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 @@ interface TaskFilter { */ 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 @@ class FilteredTaskList { /** 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 class TaskStack { 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 @@ public class TaskStack { } /** - * 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 @@ public class TaskStack { 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 @@ public class TaskStack { mCb.onHistoryTaskRemoved(this, t); } } + mRawTaskList.remove(t); } /** @@ -456,8 +549,8 @@ public class TaskStack { */ 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 @@ public class TaskStack { } /** - * 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 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 getStackTaskFreeformCount() { + public int getFreeformTaskCount() { ArrayList<Task> tasks = mStackTaskList.getTasks(); int freeformCount = 0; int taskCount = tasks.size(); @@ -664,7 +773,7 @@ public class TaskStack { */ 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 @@ public class TaskStack { 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 @@ public class TaskStack { * 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 @@ public class TaskStack { /** * 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 8ae00a7eae82..3ad368cbf60a 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 @@ public interface DropTarget { * 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 890713e5619d..d3a1e91e847f 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 @@ import java.util.List; 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 class FreeformWorkspaceLayoutAlgorithm { 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 fdb0d324a081..b363ed5ac4a6 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 @@ public class RecentsTransitionHelper { 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 { 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 e28e2b383aba..0d9a1c4bef1e 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.Task; 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 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID; */ 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,15 +89,10 @@ public class RecentsView extends FrameLayout { private boolean mAwaitingFirstLayout = true; private boolean mLastTaskLaunchedWasFreeform; private Rect mSystemInsets = new Rect(); + private int mDividerSize; private RecentsTransitionHelper mTransitionHelper; private RecentsViewTouchHandler mTouchHandler; - private TaskStack.DockState[] mVisibleDockStates = { - TaskStack.DockState.LEFT, - TaskStack.DockState.TOP, - TaskStack.DockState.RIGHT, - TaskStack.DockState.BOTTOM, - }; private final Interpolator mFastOutSlowInInterpolator; private final Interpolator mFastOutLinearInInterpolator; @@ -118,12 +113,15 @@ public class RecentsView extends FrameLayout { 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 +161,7 @@ public class RecentsView extends FrameLayout { } // Update the top level view's visibilities - if (stack.getStackTaskCount() > 0) { + if (stack.getTaskCount() > 0) { hideEmptyView(); } else { showEmptyView(); @@ -431,8 +429,9 @@ public class RecentsView extends FrameLayout { @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - for (int i = mVisibleDockStates.length - 1; i >= 0; i--) { - Drawable d = mVisibleDockStates[i].viewState.dockAreaOverlay; + ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); + for (int i = visDockStates.size() - 1; i >= 0; i--) { + Drawable d = visDockStates.get(i).viewState.dockAreaOverlay; if (d.getAlpha() > 0) { d.draw(canvas); } @@ -441,8 +440,9 @@ public class RecentsView extends FrameLayout { @Override protected boolean verifyDrawable(Drawable who) { - for (int i = mVisibleDockStates.length - 1; i >= 0; i--) { - Drawable d = mVisibleDockStates[i].viewState.dockAreaOverlay; + ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); + for (int i = visDockStates.size() - 1; i >= 0; i--) { + Drawable d = visDockStates.get(i).viewState.dockAreaOverlay; if (d == who) { return true; } @@ -470,52 +470,77 @@ public class RecentsView extends FrameLayout { 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,23 +663,34 @@ public class RecentsView extends FrameLayout { /** * 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); } - for (TaskStack.DockState dockState : mVisibleDockStates) { + ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); + for (int i = visDockStates.size() - 1; i >= 0; i--) { + TaskStack.DockState dockState = visDockStates.get(i); 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 473334b40d8d..d8698ee9177d 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 @@ package com.android.systemui.recents.views; 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,17 @@ public class RecentsViewTouchHandler { 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<>(); + private ArrayList<TaskStack.DockState> mVisibleDockStates = new ArrayList<>(); public RecentsViewTouchHandler(RecentsView rv) { mRv = rv; + mDragSlop = ViewConfiguration.get(rv.getContext()).getScaledTouchSlop(); } /** @@ -93,16 +98,23 @@ public class RecentsViewTouchHandler { return dockStates; } + /** + * Returns the set of visible dock states for this current drag. + */ + public ArrayList<TaskStack.DockState> getVisibleDockStates() { + return mVisibleDockStates; + } + /** 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 +122,9 @@ public class RecentsViewTouchHandler { 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(); @@ -124,11 +138,13 @@ public class RecentsViewTouchHandler { mTaskView.setTranslationX(x); mTaskView.setTranslationY(y); - if (!ssp.hasDockedTask()) { + mVisibleDockStates.clear(); + if (!ssp.hasDockedTask() && mRv.getTaskStack().getTaskCount() > 1) { // Add the dock state drop targets (these take priority) TaskStack.DockState[] dockStates = getDockStatesForCurrentOrientation(); for (TaskStack.DockState dockState : dockStates) { registerDropTargetForCurrentDrag(dockState); + mVisibleDockStates.add(dockState); } } @@ -137,7 +153,7 @@ public class RecentsViewTouchHandler { } public final void onBusEvent(DragEndEvent event) { - mDragging = false; + mDragRequested = false; mDragTask = null; mTaskView = null; mLastDropTarget = null; @@ -153,25 +169,45 @@ public class RecentsViewTouchHandler { 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; - - DropTarget currentDropTarget = null; - for (DropTarget target : mDropTargets) { - if (target.acceptsDrop((int) evX, (int) evY, width, height)) { - currentDropTarget = target; - break; - } + float evX = ev.getX(); + float evY = ev.getY(); + float x = evX - mTaskViewOffset.x; + float y = evY - mTaskViewOffset.y; + + 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 +217,7 @@ public class RecentsViewTouchHandler { } 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 b7c1de36279f..000000000000 --- 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 80a35de9feb7..2930f4d5c08f 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 @@ public class TaskStackAnimationHelper { Task launchTargetTask = stack.getLaunchTarget(); // Break early if there are no tasks - if (stack.getStackTaskCount() == 0) { + if (stack.getTaskCount() == 0) { return; } @@ -159,7 +159,7 @@ public class TaskStackAnimationHelper { Task launchTargetTask = stack.getLaunchTarget(); // Break early if there are no tasks - if (stack.getStackTaskCount() == 0) { + if (stack.getTaskCount() == 0) { return; } @@ -229,7 +229,7 @@ public class TaskStackAnimationHelper { 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 c2bb74567599..2fa99ce0230e 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.Context; 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.Task; 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 class TaskStackLayoutAlgorithm { 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 @@ public class TaskStackLayoutAlgorithm { 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 @@ public class TaskStackLayoutAlgorithm { * 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 @@ public class TaskStackLayoutAlgorithm { 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 @@ public class TaskStackLayoutAlgorithm { // 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 @@ public class TaskStackLayoutAlgorithm { 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 9568facf87fb..713cfc3ba79c 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.graphics.drawable.GradientDrawable; 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.Task; 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal GradientDrawable mFreeformWorkspaceBackground; ObjectAnimator mFreeformWorkspaceBackgroundAnimator; ViewPool<TaskView, Task> mViewPool; + boolean mStartTimerIndicator; ArrayList<TaskView> mTaskViews = new ArrayList<>(); ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>(); @@ -135,6 +122,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Task mFocusedTask; int mTaskCornerRadiusPx; + private int mDividerSize; boolean mTaskViewsClipDirty = true; boolean mAwaitingFirstLayout = true; @@ -142,10 +130,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mUIDozeTrigger.resetTrigger(); mStackScroller.reset(); mLayoutAlgorithm.reset(); + readSystemFlags(); requestLayout(); } @@ -346,9 +349,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** * 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal * @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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } - /** 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // 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 class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } - 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } 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 class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } 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 class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } 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 class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 c748efcc842b..a0bb0ef9caef 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -20,6 +20,7 @@ import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; +import android.util.DisplayMetrics; import android.util.Log; import android.view.InputDevice; import android.view.MotionEvent; @@ -29,6 +30,7 @@ import android.view.ViewConfiguration; 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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { // 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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { 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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { 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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { 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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { @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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { } @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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { } @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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { 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 bc441b26016c..db4db6346e88 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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, 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 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mActionButtonView.setScaleX(1f); mActionButtonView.setScaleY(1f); - mActionButtonView.setAlpha(1f); + mActionButtonView.setAlpha(0f); mActionButtonView.setTranslationZ(mActionButtonTranslationZ); } @@ -360,6 +362,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, updateDimFromTaskProgress(); } + public TaskViewHeader getHeaderView() { + return mHeaderView; + } + /** Returns the current task progress. */ public float getTaskProgress() { return mTaskProgress; @@ -455,7 +461,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, .scaleY(1f) .setDuration(fadeInDuration) .setInterpolator(PhoneStatusBar.ALPHA_IN) - .withLayer() .start(); } } else { @@ -494,7 +499,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, 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 6a474242903a..e7717ac1c871 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.Canvas; 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.FREEFORM_WORKSPACE_STACK_ID; 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 @@ public class TaskViewHeader extends FrameLayout ImageView mIconView; TextView mTitleView; int mMoveTaskTargetStackId = INVALID_STACK_ID; + ProgressBar mFocusTimerIndicator; // Header drawables Rect mTaskViewRect = new Rect(); @@ -132,9 +136,12 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout Interpolator mFastOutSlowInInterpolator; Interpolator mFastOutLinearInInterpolator; + long mFocusIndicatorProgress; + private CountDownTimer mFocusTimerCountDown; + long mFocusTimerDuration; + public TaskViewHeader(Context context) { this(context, null); } @@ -165,12 +176,15 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout 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 @@ public class TaskViewHeader extends FrameLayout } } - /** 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 @@ public class TaskViewHeader extends FrameLayout } } - /** 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 824d10a3aa19..c16703e86a10 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 @@ public class DividerView extends FrameLayout implements OnTouchListener, 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 @@ public class DividerView extends FrameLayout implements OnTouchListener, 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 3e0ea90f7694..b36fb7e65f7e 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.AlertDialog; 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/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java index 20a6e7c090a8..52326e3fc78a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java @@ -16,10 +16,11 @@ package com.android.systemui.statusbar; -import android.annotation.IdRes; import android.app.INotificationManager; import android.app.Notification; import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.RemoteException; @@ -28,13 +29,12 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.AttributeSet; import android.view.View; -import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.RadioButton; -import android.widget.RadioGroup; import android.widget.SeekBar; import android.widget.TextView; +import com.android.settingslib.Utils; import com.android.systemui.R; /** @@ -123,10 +123,27 @@ public class NotificationGuts extends LinearLayout { final TextView topicSummary = ((TextView) row.findViewById(R.id.summary)); final TextView topicTitle = ((TextView) row.findViewById(R.id.title)); mSeekBar = (SeekBar) row.findViewById(R.id.seekbar); - mSeekBar.setMax(4); + boolean systemApp = false; + try { + final PackageManager pm = BaseStatusBar.getPackageManagerForUser( + getContext(), sbn.getUser().getIdentifier()); + final PackageInfo info = + pm.getPackageInfo(sbn.getPackageName(), PackageManager.GET_SIGNATURES); + systemApp = Utils.isSystemPackage(pm, info); + } catch (PackageManager.NameNotFoundException e) { + // unlikely. + } + final int minProgress = systemApp ? + NotificationListenerService.Ranking.IMPORTANCE_LOW + : NotificationListenerService.Ranking.IMPORTANCE_NONE; + mSeekBar.setMax(NotificationListenerService.Ranking.IMPORTANCE_MAX); mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (progress < minProgress) { + seekBar.setProgress(minProgress); + progress = minProgress; + } updateTitleAndSummary(progress); if (fromUser) { if (appUsesTopics) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java new file mode 100644 index 000000000000..3e2c4c62ef74 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.car; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.support.v4.util.SimpleArrayMap; +import android.view.View; +import android.widget.LinearLayout; +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.ActivityStarter; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +/** + * A controller to populate data for CarNavigationBarView and handle user interactions. + * <p/> + * Each button inside the navigation bar is defined by data in arrays_car.xml. OEMs can customize + * the navigation buttons by updating arrays_car.xml appropriately in an overlay. + */ +class CarNavigationBarController { + + // Each facet of the navigation bar maps to a set of package names or categories defined in + // arrays_car.xml. Package names for a given facet are delimited by ";" + private static final String FACET_FILTER_DEMILITER = ";"; + + private Context mContext; + private CarNavigationBarView mNavBar; + private ActivityStarter mActivityStarter; + + // Set of categories each facet will filter on. + private List<String[]> mFacetCategories = new ArrayList<String[]>(); + // Set of package names each facet will filter on. + private List<String[]> mFacetPackages = new ArrayList<String[]>(); + + private SimpleArrayMap<String, Integer> mFacetCategoryMap + = new SimpleArrayMap<String, Integer>(); + private SimpleArrayMap<String, Integer> mFacetPackageMap + = new SimpleArrayMap<String, Integer>(); + + private List<Intent> mIntents = new ArrayList<Intent>(); + private List<Intent> mLongPressIntents = new ArrayList<Intent>(); + + private List<CarNavigationButton> mNavButtons = new ArrayList<CarNavigationButton>(); + + private int mCurrentFacetIndex; + + public CarNavigationBarController(Context context, + CarNavigationBarView navBar, + ActivityStarter activityStarter) { + mContext = context; + mNavBar = navBar; + mActivityStarter = activityStarter; + bind(); + } + + public void taskChanged(String packageName) { + // If the package name belongs to a filter, then highlight appropriate button in + // the navigation bar. + if (mFacetPackageMap.containsKey(packageName)) { + setCurrentFacet(mFacetPackageMap.get(packageName)); + } + + // Check if the package matches any of the categories for the facets + String category = getPackageCategory(packageName); + if (category != null) { + setCurrentFacet(mFacetCategoryMap.get(category)); + } + } + + private void bind() { + // Read up arrays_car.xml and populate the navigation bar here. + Resources r = mContext.getResources(); + TypedArray icons = r.obtainTypedArray(R.array.car_facet_icons); + TypedArray intents = r.obtainTypedArray(R.array.car_facet_intent_uris); + TypedArray longpressIntents = + r.obtainTypedArray(R.array.car_facet_longpress_intent_uris); + TypedArray facetPackageNames = r.obtainTypedArray(R.array.car_facet_package_filters); + + TypedArray facetCategories = r.obtainTypedArray(R.array.car_facet_category_filters); + + if (icons.length() != intents.length() + || icons.length() != longpressIntents.length() + || icons.length() != facetPackageNames.length() + || icons.length() != facetCategories.length()) { + throw new RuntimeException("car_facet array lengths do not match"); + } + + for (int i = 0; i < icons.length(); i++) { + Drawable icon = icons.getDrawable(i); + try { + mIntents.add(i, + Intent.parseUri(intents.getString(i), Intent.URI_INTENT_SCHEME)); + + String longpressUri = longpressIntents.getString(i); + boolean hasLongpress = !longpressUri.isEmpty(); + if (hasLongpress) { + mLongPressIntents.add(i, + Intent.parseUri(longpressUri, Intent.URI_INTENT_SCHEME)); + } + + CarNavigationButton button = createNavButton(icon, i, hasLongpress); + mNavButtons.add(button); + mNavBar.addButton(button, createNavButton(icon, i, hasLongpress)); + + initFacetFilterMaps(i, + facetPackageNames.getString(i).split(FACET_FILTER_DEMILITER), + facetCategories.getString(i).split(FACET_FILTER_DEMILITER)); + } catch (URISyntaxException e) { + throw new RuntimeException("Malformed intent uri", e); + } + } + } + + private void initFacetFilterMaps(int id, String[] packageNames, String[] categories){ + mFacetCategories.add(categories); + for (int i = 0; i < categories.length; i++) { + mFacetCategoryMap.put(categories[i], id); + } + + mFacetPackages.add(packageNames); + for (int i = 0; i < packageNames.length; i++) { + mFacetPackageMap.put(packageNames[i], id); + } + } + + private String getPackageCategory(String packageName) { + PackageManager pm = mContext.getPackageManager(); + int size = mFacetCategories.size(); + // For each facet, check if the given package name matches one of its categories + for (int i = 0; i < size; i++) { + String[] categories = mFacetCategories.get(i); + for (int j = 0; j < categories.length; j++) { + String category = categories[j]; + Intent intent = new Intent(); + intent.setPackage(packageName); + intent.setAction(Intent.ACTION_MAIN); + intent.addCategory(category); + List<ResolveInfo> list = pm.queryIntentActivities(intent, 0); + if (list.size() > 0) { + // Cache this package name into facetPackageMap, so we won't have to query + // all categories next time this package name shows up. + mFacetPackageMap.put(packageName, mFacetCategoryMap.get(category)); + return category; + } + } + } + return null; + } + + /** + * Helper method to check if a given facet has multiple packages associated with it. + * This can be resource defined package names or package names filtered by facet category. + */ + private boolean facetHasMultiplePackages(int index) { + PackageManager pm = mContext.getPackageManager(); + + // Check if the packages defined for the filter actually exists on the device + String[] packages = mFacetPackages.get(index); + if (packages.length > 1) { + int count = 0; + for (int i = 0; i < packages.length; i++) { + count += pm.getLaunchIntentForPackage(packages[i]) != null ? 1 : 0; + if (count > 1) { + return true; + } + } + } + + // If there weren't multiple packages defined for the facet, check the categories + // and see if they resolve to multiple package names + String categories[] = mFacetCategories.get(index); + + int count = 0; + for (int i = 0; i < categories.length; i++) { + String category = categories[i]; + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_MAIN); + intent.addCategory(category); + count += pm.queryIntentActivities(intent, 0).size(); + if (count > 1) { + return true; + } + } + return false; + } + + private void setCurrentFacet(int index) { + if (index == mCurrentFacetIndex) { + return; + } + + if (mNavButtons.get(mCurrentFacetIndex) != null) { + mNavButtons.get(mCurrentFacetIndex) + .setSelected(false /* selected */, false /* showMoreIcon */); + } + + if (mNavButtons.get(index) != null) { + mNavButtons.get(index).setSelected(true /* selected */, + facetHasMultiplePackages(index) /* showMoreIcon */); + } + mCurrentFacetIndex = index; + } + + private CarNavigationButton createNavButton(Drawable icon, final int id, + boolean longClickEnabled) { + CarNavigationButton button = (CarNavigationButton) View.inflate(mContext, + R.layout.car_navigation_button, null); + button.setResources(icon); + LinearLayout.LayoutParams lp = + new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1); + button.setLayoutParams(lp); + + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setCurrentFacet(id); + onFacetClicked(id); + } + }); + + if (longClickEnabled) { + button.setLongClickable(true); + button.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + onFacetLongClicked(id); + setCurrentFacet(id); + return true; + } + }); + } else { + button.setLongClickable(false); + } + return button; + } + + private void startActivity(Intent intent) { + if (mActivityStarter != null && intent != null) { + mActivityStarter.startActivity(intent, true); + } + } + + private void onFacetClicked(int index) { + // TODO: determine what data to pass to the trampoline, so it can start + // the default app or the lens picker. + startActivity(mIntents.get(index)); + } + + private void onFacetLongClicked(int index) { + // TODO: determine what data to pass to the trampoline, so it can start + // the default app or the lens picker. + startActivity(mLongPressIntents.get(index)); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java index e2d64b049ca0..efc3646c5015 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java @@ -17,36 +17,29 @@ package com.android.systemui.statusbar.car; import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.R.color; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; import com.android.systemui.R; -import com.android.systemui.statusbar.phone.ActivityStarter; import com.android.systemui.statusbar.phone.NavigationBarView; -import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; -import com.android.systemui.statusbar.policy.KeyButtonView; -import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; /** * A custom navigation bar for the automotive use case. * <p> - * The navigation bar in the automotive use case is more like a list of shortcuts, which we - * expect to be customizable by the car OEMs. This implementation populates the nav_buttons layout - * from resources rather than the layout file so customization would then mean updating - * arrays_car.xml appropriately in an overlay. + * The navigation bar in the automotive use case is more like a list of shortcuts, rendered + * in a linear layout. */ class CarNavigationBarView extends NavigationBarView { - private ActivityStarter mActivityStarter; + private LinearLayout mNavButtons; + private LinearLayout mLightsOutButtons; public CarNavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -54,83 +47,13 @@ class CarNavigationBarView extends NavigationBarView { @Override public void onFinishInflate() { - // Read up arrays_car.xml and populate the navigation bar here. - Context context = getContext(); - Resources r = getContext().getResources(); - TypedArray icons = r.obtainTypedArray(R.array.car_shortcut_icons); - TypedArray intents = r.obtainTypedArray(R.array.car_shortcut_intent_uris); - TypedArray longpressIntents = - r.obtainTypedArray(R.array.car_shortcut_longpress_intent_uris); - - if (icons.length() != intents.length()) { - throw new RuntimeException("car_shortcut_icons and car_shortcut_intents do not match"); - } - - LinearLayout navButtons = (LinearLayout) findViewById(R.id.nav_buttons); - LinearLayout lightsOut = (LinearLayout) findViewById(R.id.lights_out); - - for (int i = 0; i < icons.length(); i++) { - Drawable icon = icons.getDrawable(i); - - try { - Intent intent = Intent.parseUri(intents.getString(i), Intent.URI_INTENT_SCHEME); - Intent longpress = null; - String longpressUri = longpressIntents.getString(i); - if (!longpressUri.isEmpty()) { - longpress = Intent.parseUri(longpressUri, Intent.URI_INTENT_SCHEME); - } - - // nav_buttons and lights_out should match exactly. - navButtons.addView(makeButton(context, icon, intent, longpress)); - lightsOut.addView(makeButton(context, icon, intent, longpress)); - } catch (URISyntaxException e) { - throw new RuntimeException("Malformed intent uri", e); - } - } - } - - private ImageButton makeButton(Context context, Drawable icon, - final Intent intent, final Intent longpress) { - ImageButton button = new ImageButton(context); - - button.setImageDrawable(icon); - button.setScaleType(ScaleType.CENTER); - button.setBackgroundColor(color.transparent); - LinearLayout.LayoutParams lp = - new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1); - button.setLayoutParams(lp); - - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (mActivityStarter != null) { - mActivityStarter.startActivity(intent, true); - } - } - }); - - // Long click handlers are optional. - if (longpress != null) { - button.setLongClickable(true); - button.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mActivityStarter != null) { - mActivityStarter.startActivity(longpress, true); - return true; - } - return false; - } - }); - } else { - button.setLongClickable(false); - } - - return button; + mNavButtons = (LinearLayout) findViewById(R.id.nav_buttons); + mLightsOutButtons = (LinearLayout) findViewById(R.id.lights_out); } - public void setActivityStarter(ActivityStarter activityStarter) { - mActivityStarter = activityStarter; + public void addButton(CarNavigationButton button, CarNavigationButton lightsOutButton){ + mNavButtons.addView(button); + mLightsOutButtons.addView(lightsOutButton); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java new file mode 100644 index 000000000000..36b3a8ae816e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.car; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import com.android.keyguard.AlphaOptimizedImageButton; +import com.android.systemui.R; + +/** + * A wrapper view for a car navigation facet, which includes a button icon and a drop down icon. + */ +public class CarNavigationButton extends RelativeLayout { + private static final float SELECTED_ALPHA = 1; + private static final float UNSELECTED_ALPHA = 0.7f; + + private AlphaOptimizedImageButton mIcon; + private AlphaOptimizedImageButton mMoreIcon; + + public CarNavigationButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void onFinishInflate() { + super.onFinishInflate(); + mIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_icon); + mIcon.setClickable(false); + mIcon.setScaleType(ImageView.ScaleType.CENTER); + mIcon.setBackgroundColor(android.R.color.transparent); + mIcon.setAlpha(UNSELECTED_ALPHA); + + mMoreIcon = (AlphaOptimizedImageButton) findViewById(R.id.car_nav_button_more_icon); + mMoreIcon.setClickable(false); + mMoreIcon.setScaleType(ImageView.ScaleType.CENTER); + mMoreIcon.setBackgroundColor(android.R.color.transparent); + mMoreIcon.setVisibility(INVISIBLE); + mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow)); + mMoreIcon.setAlpha(UNSELECTED_ALPHA); + } + + public void setResources(Drawable icon) { + mIcon.setImageDrawable(icon); + } + + public void setSelected(boolean selected, boolean showMoreIcon) { + if (selected) { + mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : INVISIBLE); + mMoreIcon.setAlpha(SELECTED_ALPHA); + mIcon.setAlpha(SELECTED_ALPHA); + } else { + mMoreIcon.setVisibility(INVISIBLE); + mIcon.setAlpha(UNSELECTED_ALPHA); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 31631f8efa66..f6f1f94017aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -16,30 +16,52 @@ package com.android.systemui.statusbar.car; +import android.app.ActivityManager; +import android.app.ITaskStackListener; import android.content.Context; import android.graphics.PixelFormat; +import android.os.Handler; +import android.os.Looper; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import com.android.systemui.R; +import com.android.systemui.recents.Recents; +import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.statusbar.phone.PhoneStatusBar; /** * A status bar (and navigation bar) tailored for the automotive use case. */ public class CarStatusBar extends PhoneStatusBar { + private SystemServicesProxy mSystemServicesProxy; + private TaskStackListenerImpl mTaskStackListener; + private Handler mHandler; + + private CarNavigationBarView mCarNavigationBar; + private CarNavigationBarController mController; + + @Override + public void start() { + super.start(); + mHandler = new Handler(); + mTaskStackListener = new TaskStackListenerImpl(mHandler); + mSystemServicesProxy = new SystemServicesProxy(mContext); + mSystemServicesProxy.registerTaskStackListener(mTaskStackListener); + } + @Override protected void addNavigationBar() { WindowManager.LayoutParams lp = new WindowManager.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, - WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, + WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, PixelFormat.TRANSLUCENT); lp.setTitle("CarNavigationBar"); lp.windowAnimations = 0; @@ -51,11 +73,11 @@ public class CarStatusBar extends PhoneStatusBar { if (mNavigationBarView != null) { return; } - - CarNavigationBarView carNavBar = + mCarNavigationBar = (CarNavigationBarView) View.inflate(context, R.layout.car_navigation_bar, null); - carNavBar.setActivityStarter(this); - mNavigationBarView = carNavBar; + mController = new CarNavigationBarController(context, mCarNavigationBar, + this /* ActivityStarter*/); + mNavigationBarView = mCarNavigationBar; } @Override @@ -63,4 +85,40 @@ public class CarStatusBar extends PhoneStatusBar { // The navigation bar for a vehicle will not need to be repositioned, as it is always // set at the bottom. } + + /** + * An implementation of ITaskStackListener, that listens for changes in the system task + * stack and notifies the navigation bar. + */ + private class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable { + private Handler mHandler; + + public TaskStackListenerImpl(Handler handler) { + this.mHandler = handler; + } + + @Override + public void onActivityPinned() { + } + + @Override + public void onTaskStackChanged() { + mHandler.removeCallbacks(this); + mHandler.post(this); + } + + @Override + public void run() { + ensureMainThread(); + SystemServicesProxy ssp = Recents.getSystemServices(); + ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask(); + mController.taskChanged(runningTaskInfo.baseActivity.getPackageName()); + } + + private void ensureMainThread() { + if (!Looper.getMainLooper().isCurrentThread()) { + throw new RuntimeException("Must be called on the UI thread"); + } + } + } } 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 99436a19333d..347ba3da0603 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 @@ import static com.android.keyguard.KeyguardSecurityModel.SecurityMode; */ 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 class KeyguardBouncer { public void hide(boolean destroyView) { mFalsingManager.onBouncerHidden(); cancelShowRunnable(); - if (mKeyguardView != null) { + if (mKeyguardView != null) { mKeyguardView.cancelDismissAction(); mKeyguardView.cleanUp(); } @@ -184,13 +184,13 @@ public class KeyguardBouncer { 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 @@ public class KeyguardBouncer { 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 50e88d39f1f9..78497abe16ba 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.LayoutInflater; 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - 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 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 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 336b208cb50b..29ad5d1cec3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -27,6 +27,7 @@ import android.os.Process; 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 @@ public final class QSTileHost implements QSTile.Host, Tunable { 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 @@ public final class QSTileHost implements QSTile.Host, Tunable { TunerService.get(mContext).addTunable(this, TILES_SETTING); } + public void setHeaderView(View view) { + mHeader = view; + } + public PhoneStatusBar getPhoneStatusBar() { return mStatusBar; } @@ -173,6 +179,11 @@ public final class QSTileHost implements QSTile.Host, Tunable { // 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 3692aeeb20c5..5b44f0a4189f 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 class QuickStatusBarHeader extends BaseStatusBarHeader implements } } - 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 f14f0d4c28a4..7a05b8f3b9c4 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 android.view.WindowManagerGlobal; 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mDeviceInteractive = false; private boolean mScreenTurnedOn; - private KeyguardBouncer mBouncer; - private boolean mShowing; - private boolean mOccluded; - private boolean mRemoteInputActive; - - private boolean mFirstUpdate = true; - private boolean mLastShowing; - private boolean mLastOccluded; + protected KeyguardBouncer mBouncer; + protected boolean mShowing; + protected boolean mOccluded; + protected boolean mRemoteInputActive; + + 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } }; - private void updateStates() { + protected void updateStates() { int vis = mContainer.getSystemUiVisibility(); boolean showing = mShowing; boolean occluded = mOccluded; @@ -451,9 +452,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } - 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb 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 9d2f0de4c1e0..382de198d014 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 @@ public class StatusBarWindowManager implements RemoteInputController.Callback { 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 101a5f39d8d9..4f33d8278f1c 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.content.res.TypedArray; 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 @@ public class UserAvatarView extends View { 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 @@ public class UserAvatarView extends View { 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 b65bf43314f4..ad8e3bde58b5 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 @@ public class MobileSignalController extends SignalController< + " 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 65053f3c933f..61a985106705 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.PendingIntent; 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 class RemoteInputView extends LinearLayout implements View.OnClickListene } 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 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } @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 f3a355445ccf..05d9626aaf38 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.Dialog; 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.Bitmap; 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 android.widget.BaseAdapter; 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.lang.ref.WeakReference; 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 @@ public class UserSwitcherController { = 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 @@ public class UserSwitcherController { 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 @@ public class UserSwitcherController { } } - 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 @@ public class UserSwitcherController { } 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 @@ public class UserSwitcherController { 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 @@ public class UserSwitcherController { 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 @@ public class UserSwitcherController { } 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 @@ public class UserSwitcherController { } } + 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 class UserSwitcherController { 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 @@ public class UserSwitcherController { 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 3c63aae9c416..0901015f12f2 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.annotation.SuppressLint; 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 @@ public class VolumeDialog { : 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 @@ public class VolumeDialog { 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 6ebf488937f9..f86c6a46f1d6 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 class TileLifecycleManagerTests extends AndroidTestCase { 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 13fc47dab3d1..b64fbea0fa90 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 @@ public class NetworkControllerBaseTest extends SysuiTestCase { 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 @@ public class NetworkControllerBaseTest extends SysuiTestCase { mPhoneStateListener.onSignalStrengthsChanged(mSignalStrength); } - private void updateServiceState() { + protected void updateServiceState() { Log.d(TAG, "Sending Service State: " + mServiceState); mPhoneStateListener.onServiceStateChanged(mServiceState); } @@ -246,6 +246,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } 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 587e2b5b988f..e4f858bed8d6 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 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { 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/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 39d59525b522..28aeef7c6a2f 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // 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 2264c69b429c..aa1537377a42 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 @@ public class BackupManagerService { // 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 @@ public class BackupManagerService { 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"); + } + 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 } - // Wait until the next app in the queue falls due for a full data backup - latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun; } - } + } while (headBusy); if (!runBackup) { if (DEBUG_SCHEDULING) { @@ -4539,7 +4581,7 @@ public class BackupManagerService { 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 37a6c0243996..2de5324fe4e3 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1568,12 +1568,11 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + /** + * 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? diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index bd9589234b38..58a0356e23b6 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -50,11 +50,13 @@ import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.IDeviceIdleController; +import android.os.IMaintenanceActivityListener; import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -206,9 +208,13 @@ public class DeviceIdleController extends SystemService private boolean mSyncActive; private boolean mJobsActive; private boolean mAlarmsActive; + private boolean mReportedMaintenanceActivity; public final AtomicFile mConfigFile; + private final RemoteCallbackList<IMaintenanceActivityListener> mMaintenanceActivityListeners = + new RemoteCallbackList<IMaintenanceActivityListener>(); + /** * Package names the system has white-listed to opt out of power save restrictions, * except for device idle mode. @@ -813,6 +819,7 @@ public class DeviceIdleController extends SystemService static final int MSG_REPORT_IDLE_OFF = 4; static final int MSG_REPORT_ACTIVE = 5; static final int MSG_TEMP_APP_WHITELIST_TIMEOUT = 6; + static final int MSG_REPORT_MAINTENANCE_ACTIVITY = 7; final class MyHandler extends Handler { MyHandler(Looper looper) { @@ -902,6 +909,21 @@ public class DeviceIdleController extends SystemService int uid = msg.arg1; checkTempAppWhitelistTimeout(uid); } break; + case MSG_REPORT_MAINTENANCE_ACTIVITY: { + boolean active = (msg.arg1 == 1); + final int size = mMaintenanceActivityListeners.beginBroadcast(); + try { + for (int i = 0; i < size; i++) { + try { + mMaintenanceActivityListeners.getBroadcastItem(i) + .onMaintenanceActivityChanged(active); + } catch (RemoteException ignored) { + } + } + } finally { + mMaintenanceActivityListeners.finishBroadcast(); + } + } break; } } } @@ -996,6 +1018,16 @@ public class DeviceIdleController extends SystemService DeviceIdleController.this.downloadServiceInactive(); } + @Override public boolean registerMaintenanceActivityListener( + IMaintenanceActivityListener listener) { + return DeviceIdleController.this.registerMaintenanceActivityListener(listener); + } + + @Override public void unregisterMaintenanceActivityListener( + IMaintenanceActivityListener listener) { + DeviceIdleController.this.unregisterMaintenanceActivityListener(listener); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { DeviceIdleController.this.dump(fd, pw, args); } @@ -1704,6 +1736,7 @@ public class DeviceIdleController extends SystemService void downloadServiceActive(IBinder token) { synchronized (this) { mDownloadServiceActive = token; + reportMaintenanceActivityIfNeededLocked(); try { token.linkToDeath(new IBinder.DeathRecipient() { @Override public void binderDied() { @@ -1719,6 +1752,7 @@ public class DeviceIdleController extends SystemService void downloadServiceInactive() { synchronized (this) { mDownloadServiceActive = null; + reportMaintenanceActivityIfNeededLocked(); exitMaintenanceEarlyIfNeededLocked(); } } @@ -1726,6 +1760,7 @@ public class DeviceIdleController extends SystemService void setSyncActive(boolean active) { synchronized (this) { mSyncActive = active; + reportMaintenanceActivityIfNeededLocked(); if (!active) { exitMaintenanceEarlyIfNeededLocked(); } @@ -1735,6 +1770,7 @@ public class DeviceIdleController extends SystemService void setJobsActive(boolean active) { synchronized (this) { mJobsActive = active; + reportMaintenanceActivityIfNeededLocked(); if (!active) { exitMaintenanceEarlyIfNeededLocked(); } @@ -1750,6 +1786,30 @@ public class DeviceIdleController extends SystemService } } + boolean registerMaintenanceActivityListener(IMaintenanceActivityListener listener) { + synchronized (this) { + mMaintenanceActivityListeners.register(listener); + return mReportedMaintenanceActivity; + } + } + + void unregisterMaintenanceActivityListener(IMaintenanceActivityListener listener) { + synchronized (this) { + mMaintenanceActivityListeners.unregister(listener); + } + } + + void reportMaintenanceActivityIfNeededLocked() { + boolean active = mJobsActive | mSyncActive | (mDownloadServiceActive != null); + if (active == mReportedMaintenanceActivity) { + return; + } + mReportedMaintenanceActivity = active; + Message msg = mHandler.obtainMessage(MSG_REPORT_MAINTENANCE_ACTIVITY, + mReportedMaintenanceActivity ? 1 : 0, 0); + mHandler.sendMessage(msg); + } + void exitMaintenanceEarlyIfNeededLocked() { if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE) { if (mActiveIdleOpCount <= 0 && mDownloadServiceActive == null diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 1a21f5c75f0a..377d52fb1ac5 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -695,7 +695,7 @@ public class LockSettingsService extends ILockSettings.Stub { 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 137fa27ecd45..816c791d47fa 100644 --- a/services/core/java/com/android/server/LockSettingsStorage.java +++ b/services/core/java/com/android/server/LockSettingsStorage.java @@ -71,6 +71,7 @@ class LockSettingsStorage { private final Object mFileWriteLock = new Object(); private int mStoredCredentialType; + private LockPatternUtils mLockPatternUtils; class CredentialHash { static final int TYPE_NONE = -1; @@ -100,6 +101,7 @@ class LockSettingsStorage { 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 @@ class LockSettingsStorage { 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 11f9e2d58c31..f345d7e91ace 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 @@ public class AccountManagerService } 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 @@ public class AccountManagerService } 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 @@ public class AccountManagerService 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 d12eadb37164..7ba633878b29 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.ComponentName; 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { // 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 4cb64a172134..ca38b7132f11 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 @@ package com.android.server.am; 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; @@ -240,6 +239,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import dalvik.system.VMRuntime; + import libcore.io.IoUtils; import libcore.util.EmptyArray; @@ -1309,7 +1309,7 @@ public final class ActivityManagerService extends ActivityManagerNative 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; @@ -2242,7 +2242,7 @@ public final class ActivityManagerService extends ActivityManagerNative } int num = 0; - long[] tmp = new long[1]; + long[] tmp = new long[2]; do { ProcessRecord proc; int procState; @@ -2274,7 +2274,7 @@ public final class ActivityManagerService extends ActivityManagerNative 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()); } } @@ -7256,6 +7256,17 @@ public final class ActivityManagerService extends ActivityManagerNative } @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 { @@ -12270,6 +12281,8 @@ public final class ActivityManagerService extends ActivityManagerNative 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="); @@ -15177,6 +15190,7 @@ public final class ActivityManagerService extends ActivityManagerNative 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); @@ -15330,32 +15344,35 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ -15373,18 +15390,24 @@ public final class ActivityManagerService extends ActivityManagerNative 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); } } } @@ -15513,6 +15536,8 @@ public final class ActivityManagerService extends ActivityManagerNative boolean isCompact = false; boolean localOnly = false; boolean packages = false; + boolean isCheckinRequest = false; + boolean dumpSwapPss = false; int opti = 0; while (opti < args.length) { @@ -15525,6 +15550,7 @@ public final class ActivityManagerService extends ActivityManagerNative dumpDetails = true; dumpFullDetails = true; dumpDalvik = true; + dumpSwapPss = true; } else if ("-d".equals(opt)) { dumpDalvik = true; } else if ("-c".equals(opt)) { @@ -15532,22 +15558,29 @@ public final class ActivityManagerService extends ActivityManagerNative } 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; @@ -15556,7 +15589,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - final boolean isCheckinRequest = scanArgs(args, "--checkin"); long uptime = SystemClock.uptimeMillis(); long realtime = SystemClock.elapsedRealtime(); final long[] tmpLong = new long[1]; @@ -15628,18 +15660,28 @@ public final class ActivityManagerService extends ActivityManagerNative 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--) { @@ -15663,6 +15705,7 @@ public final class ActivityManagerService extends ActivityManagerNative } 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]; @@ -15690,6 +15733,7 @@ public final class ActivityManagerService extends ActivityManagerNative final long myTotalPss = mi.getTotalPss(); final long myTotalUss = mi.getTotalUss(); + final long myTotalSwapPss = mi.getTotalSwappedOutPss(); synchronized (this) { if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { @@ -15700,32 +15744,43 @@ public final class ActivityManagerService extends ActivityManagerNative 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>(); } @@ -15760,26 +15815,35 @@ public final class ActivityManagerService extends ActivityManagerNative } 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>(); } @@ -15790,21 +15854,23 @@ public final class ActivityManagerService extends ActivityManagerNative 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>(); @@ -15812,30 +15878,31 @@ public final class ActivityManagerService extends ActivityManagerNative 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(); @@ -19158,8 +19225,10 @@ public final class ActivityManagerService extends ActivityManagerNative /** * 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, @@ -19169,8 +19238,10 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ -19606,7 +19677,7 @@ public final class ActivityManagerService extends ActivityManagerNative // 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 4101dde37198..ffb2fc4a6d02 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.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.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 @@ class ActivityMetricsLogger { 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 4fb87c3a2660..eb0945bfe5f1 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 @@ final class ActivityRecord { 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 @@ final class ActivityRecord { 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 @@ final class ActivityRecord { 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 @@ final class ActivityRecord { 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 c44b4cfa7b26..98319fd2025d 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { if (r.finishing) { r.clearOptionsLocked(); } else { - if (r.configDestroy) { + if (r.deferRelaunchUntilPaused) { destroyActivityLocked(r, true, "stop-config"); mStackSupervisor.resumeFocusedStackTopActivityLocked(); } else { @@ -1114,14 +1122,11 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { && 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { // 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { } mService.resetFocusedActivityIfNeededLocked(r); - r.configDestroy = false; + r.deferRelaunchUntilPaused = false; r.frozenBeforeDestroy = false; if (setState) { @@ -3715,9 +3740,9 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { private void relaunchActivityLocked( ActivityRecord r, int changes, boolean andResume, boolean preserveWindow) { if (mService.mSuppressResizeConfigChanges && preserveWindow) { + r.configChangeFlags = 0; return; } @@ -4341,6 +4362,8 @@ final class ActivityStack { mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); r.state = ActivityState.PAUSED; } + + r.configChangeFlags = 0; } boolean willActivityBeVisibleLocked(IBinder token) { @@ -4757,8 +4780,7 @@ final class ActivityStack { } 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 @@ final class ActivityStack { } 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 4672023a220e..e837d9a0a610 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { // can have the right fullscreen state. bounds = null; } + bounds = TaskRecord.validateBounds(bounds); mTmpBounds.clear(); mTmpConfigs.clear(); @@ -1857,8 +1864,8 @@ public final class ActivityStackSupervisor implements DisplayListener { fitWithinBounds(tempRect2, bounds); task.updateOverrideConfiguration(tempRect2); } else { - task.updateOverrideConfiguration(tempTaskBounds != null - ? tempTaskBounds : bounds); + task.updateOverrideConfiguration( + tempTaskBounds != null ? tempTaskBounds : bounds); } } @@ -1973,6 +1980,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // 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 @@ public final class ActivityStackSupervisor implements DisplayListener { 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 @@ public final class ActivityStackSupervisor implements DisplayListener { // 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 cfa44335f6eb..f144e0c99cf3 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.ActivityManagerService.ANIMATE; 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 @@ class ActivityStarter { // 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 @@ class ActivityStarter { 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 03975536816d..f2e8d090d85d 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 @@ option java_package com.android.server.am 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 4bfe30075cff..b4aa4cfe84ec 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 @@ final class ProcessRecord { 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 @@ final class ProcessRecord { 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 ae987e6f4402..f7e30c035625 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 @@ final class TaskRecord { 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 @@ final class TaskRecord { /** 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 717285930f5b..56757ca28c5b 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 @@ final class UserController { private volatile UserManagerService mUserManager; + private final LockPatternUtils mLockPatternUtils; + UserController(ActivityManagerService service) { mService = service; mHandler = mService.mHandler; @@ -150,6 +152,7 @@ final class UserController { 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 @@ final class UserController { * 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 b8cbecb48928..9331dd8cd92e 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.AudioFormat; 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 @@ public class AudioService extends IAudioService.Stub { LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal()); mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); + + mRecordMonitor.initMonitor(); } public void systemReady() { @@ -6165,7 +6169,7 @@ public class AudioService extends IAudioService.Stub { } //====================== - // 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 @@ public class AudioService extends IAudioService.Stub { } } } + } + + //====================== + // 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 000000000000..5806f3fb5b70 --- /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/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 5bd4f987073e..e957fc685188 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.SystemClock; 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 @@ public class Vpn { } /** + * 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 @@ public class Vpn { /** * 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 @@ public class Vpn { (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 @@ public class Vpn { 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 78618ce07b69..2eb90952f6c4 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -2682,32 +2682,17 @@ public class SyncManager { } continue; } - if (!isOperationValidLocked(op)) { - operationIterator.remove(); - mSyncStorageEngine.deleteFromPending(op.pendingOperation); - continue; - } - // If the next run time is in the future, even given the flexible scheduling, - // return the time. - if (op.effectiveRunTime - op.flexTime > now) { - if (nextReadyToRunTime > op.effectiveRunTime) { - nextReadyToRunTime = op.effectiveRunTime; - } - if (isLoggable) { - Log.v(TAG, " Not running sync operation: Sync too far in future." - + "effective: " + op.effectiveRunTime + " flex: " + op.flexTime - + " now: " + now); - } - 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); + | 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 @@ -2722,6 +2707,24 @@ public class SyncManager { } else { op.appIdle = false; } + if (!isOperationValidLocked(op)) { + operationIterator.remove(); + mSyncStorageEngine.deleteFromPending(op.pendingOperation); + continue; + } + // If the next run time is in the future, even given the flexible scheduling, + // return the time. + if (op.effectiveRunTime - op.flexTime > now) { + if (nextReadyToRunTime > op.effectiveRunTime) { + nextReadyToRunTime = op.effectiveRunTime; + } + if (isLoggable) { + Log.v(TAG, " Not running sync operation: Sync too far in future." + + "effective: " + op.effectiveRunTime + " flex: " + op.flexTime + + " now: " + now); + } + continue; + } // 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 835ba1734da3..a16fcd2fa112 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 @@ final class ColorFade { Slog.e(TAG, "Unable to create surface.", ex); return false; } - } - mSurfaceControl.setLayerStack(mDisplayLayerStack); - mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight); - mSurface = new Surface(); - mSurface.copyFrom(mSurfaceControl); + mSurfaceControl.setLayerStack(mDisplayLayerStack); + mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight); + mSurface = new Surface(); + mSurface.copyFrom(mSurfaceControl); - mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal, - mDisplayId, mSurfaceControl); - mSurfaceLayout.onDisplayTransaction(); + 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 433d8876dee7..b74b0f2e3caa 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call 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 3530d80577c7..a6db6137e54c 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.Slog; 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.JobStatus; 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 @@ public class JobSchedulerService extends com.android.server.SystemService */ 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService } 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 @@ public class JobSchedulerService extends com.android.server.SystemService @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 @@ public class JobSchedulerService extends com.android.server.SystemService // Remove from pending queue. mPendingJobs.remove(cancelled); // Cancel if running. - stopJobOnServiceContextLocked(cancelled); + stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED); reportActive(); } } @@ -357,7 +344,7 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService @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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService */ 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService * 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService * - 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 @@ public class JobSchedulerService extends com.android.server.SystemService * - 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,43 +847,118 @@ public class JobSchedulerService extends com.android.server.SystemService // 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; - } + 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()); } - 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(); + // 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]); } - reportActive(); + } + 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; } /** @@ -936,8 +977,7 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 5376043e1e90..5935319e6f91 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 @@ import java.util.concurrent.atomic.AtomicBoolean; * 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 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne /** 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 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne * 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 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne @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 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne return false; } + mPreferredUid = NO_PREFERRED_UID; + mRunningJob = job; final boolean isDeadlineExpired = job.hasDeadlineConstraint() && @@ -206,17 +212,22 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne } /** 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 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne } 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 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne /** 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 b8aa9dd7d156..a8cb19f58aee 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 @@ public class JobStore { 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 @@ public class JobStore { } } - /** 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 @@ public class JobStore { 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 @@ public class JobStore { 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/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index 92df851bb687..fe5e8c94bc3e 100644 --- a/services/core/java/com/android/server/job/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -34,14 +34,13 @@ import com.android.server.job.StateChangedListener; public class IdleController extends StateController { private static final String TAG = "IdleController"; - // Policy: we decide that we're "idle" if the device has been unused / - // screen off or dreaming for at least this long - private static final long INACTIVITY_IDLE_THRESHOLD = 71 * 60 * 1000; // millis; 71 min - private static final long IDLE_WINDOW_SLOP = 5 * 60 * 1000; // 5 minute window, to be nice - private static final String ACTION_TRIGGER_IDLE = "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE"; + // Policy: we decide that we're "idle" if the device has been unused / + // screen off or dreaming for at least this long + private long mInactivityIdleThreshold; + private long mIdleWindowSlop; final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>(); IdlenessTracker mIdleTracker; @@ -100,6 +99,10 @@ public class IdleController extends StateController { * significant state changes occur */ private void initIdleStateTracking() { + mInactivityIdleThreshold = mContext.getResources().getInteger( + com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold); + mIdleWindowSlop = mContext.getResources().getInteger( + com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop); mIdleTracker = new IdlenessTracker(); mIdleTracker.startTracking(); } @@ -168,14 +171,14 @@ public class IdleController extends StateController { // alarm that will tell us when we have decided the device is // truly idle. final long nowElapsed = SystemClock.elapsedRealtime(); - final long when = nowElapsed + INACTIVITY_IDLE_THRESHOLD; + final long when = nowElapsed + mInactivityIdleThreshold; if (DEBUG) { Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when=" + when); } mScreenOn = false; mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, - when, IDLE_WINDOW_SLOP, mIdleTriggerIntent); + when, mIdleWindowSlop, mIdleTriggerIntent); } else if (action.equals(ACTION_TRIGGER_IDLE)) { // idle time starts now. Do not set mIdle if screen is on. if (!mIdle && !mScreenOn) { 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 060a93ef8ed9..56d92d535531 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 class JobStatus { 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 000000000000..50dd607e5a5a --- /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 018bf2d2cb27..b1fe68c82c89 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 @@ public class NotificationManagerService extends SystemService { } @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 f7043a601c9d..1d91fb7d858a 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.app.NotificationManager.Policy; 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.Process; 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 @@ public class ZenModeHelper { 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 @@ public class ZenModeHelper { mSettingsObserver.observe(); mFiltering = new ZenModeFiltering(mContext); mConditions = new ZenModeConditions(this, conditionProviders); + mServiceConfig = conditionProviders.getConfig(); } public Looper getLooper() { @@ -197,7 +202,7 @@ public class ZenModeHelper { config.user = user; } synchronized (mConfig) { - setConfig(config, "onUserSwitched"); + setConfigLocked(config, "onUserSwitched"); } cleanUpZenRules(); } @@ -257,22 +262,34 @@ public class ZenModeHelper { } 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 @@ public class ZenModeHelper { } populateZenRule(automaticZenRule, rule, false); newConfig.automaticRules.put(ruleId, rule); - return setConfig(newConfig, reason, true); + return setConfigLocked(newConfig, reason, true); } } @@ -320,7 +337,7 @@ public class ZenModeHelper { 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 @@ public class ZenModeHelper { 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 @@ public class ZenModeHelper { } } + 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 @@ public class ZenModeHelper { newRule.conditionId = conditionId; newConfig.manualRule = newRule; } - setConfig(newConfig, reason, setRingerMode); + setConfigLocked(newConfig, reason, setRingerMode); } } @@ -478,7 +530,7 @@ public class ZenModeHelper { } if (DEBUG) Log.d(TAG, "readXml"); synchronized (mConfig) { - setConfig(config, "readXml"); + setConfigLocked(config, "readXml"); } } } @@ -507,7 +559,7 @@ public class ZenModeHelper { synchronized (mConfig) { final ZenModeConfig newConfig = mConfig.copy(); newConfig.applyNotificationPolicy(policy); - setConfig(newConfig, "setNotificationPolicy"); + setConfigLocked(newConfig, "setNotificationPolicy"); } } @@ -530,7 +582,7 @@ public class ZenModeHelper { } } } - setConfig(newConfig, "cleanUpZenRules"); + setConfigLocked(newConfig, "cleanUpZenRules"); } } @@ -543,30 +595,30 @@ public class ZenModeHelper { } } - 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 @@ public class ZenModeHelper { 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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f777faf3c6c0..9f571e34b4c4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -76,7 +76,6 @@ import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; - import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT; import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; @@ -227,6 +226,7 @@ import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; +import com.android.internal.util.XmlUtils; import com.android.server.EventLogTags; import com.android.server.FgThread; import com.android.server.IntentResolver; @@ -978,6 +978,30 @@ public class PackageManagerService extends IPackageManager.Stub { private static final String TAG_DEFAULT_APPS = "da"; private static final String TAG_INTENT_FILTER_VERIFICATION = "iv"; + private static final String TAG_PERMISSION_BACKUP = "perm-grant-backup"; + private static final String TAG_ALL_GRANTS = "rt-grants"; + private static final String TAG_GRANT = "grant"; + private static final String ATTR_PACKAGE_NAME = "pkg"; + + private static final String TAG_PERMISSION = "perm"; + private static final String ATTR_PERMISSION_NAME = "name"; + private static final String ATTR_IS_GRANTED = "g"; + private static final String ATTR_USER_SET = "set"; + private static final String ATTR_USER_FIXED = "fixed"; + private static final String ATTR_REVOKE_ON_UPGRADE = "rou"; + + // System/policy permission grants are not backed up + private static final int SYSTEM_RUNTIME_GRANT_MASK = + FLAG_PERMISSION_POLICY_FIXED + | FLAG_PERMISSION_SYSTEM_FIXED + | FLAG_PERMISSION_GRANTED_BY_DEFAULT; + + // And we back up these user-adjusted states + private static final int USER_RUNTIME_GRANT_MASK = + FLAG_PERMISSION_USER_SET + | FLAG_PERMISSION_USER_FIXED + | FLAG_PERMISSION_REVOKE_ON_UPGRADE; + final @Nullable String mRequiredVerifierPackage; final @Nullable String mRequiredInstallerPackage; @@ -1490,16 +1514,25 @@ public class PackageManagerService extends IPackageManager.Stub { deleteOld = true; } - // If this app is a browser and it's newly-installed for some - // users, clear any default-browser state in those users + + // Work that needs to happen on first install within each user if (firstUsers.length > 0) { - // the app's nature doesn't depend on the user, so we can just - // check its browser nature in any user and generalize. - if (packageIsBrowser(packageName, firstUsers[0])) { + for (int userId : firstUsers) { synchronized (mPackages) { - for (int userId : firstUsers) { - mSettings.setDefaultBrowserPackageNameLPw(null, userId); + // If this app is a browser and it's newly-installed for + // some users, clear any default-browser state in those + // users. The app's nature doesn't depend on the user, + // so we can just check its browser nature in any user + // and generalize. + if (packageIsBrowser(packageName, firstUsers[0])) { + mSettings.setDefaultBrowserPackageNameLPw( + null, userId); } + + // We may also need to apply pending (restored) runtime + // permission grants within these users. + mSettings.applyPendingPermissionGrantsLPw( + packageName, userId); } } } @@ -10216,7 +10249,7 @@ public class PackageManagerService extends IPackageManager.Stub { long callingId = Binder.clearCallingIdentity(); try { - boolean sendAdded = false; + boolean installed = false; // writer synchronized (mPackages) { @@ -10228,11 +10261,21 @@ public class PackageManagerService extends IPackageManager.Stub { 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 { @@ -14813,7 +14856,7 @@ public class PackageManagerService extends IPackageManager.Stub { } return; } - +Slog.v(TAG, ":: restoreFromXml() : got to tag " + parser.getName()); // this is supposed to be TAG_PREFERRED_BACKUP if (!expectedStartTag.equals(parser.getName())) { if (DEBUG_BACKUP) { @@ -14824,6 +14867,7 @@ public class PackageManagerService extends IPackageManager.Stub { // skip interfering stuff, then we're aligned with the backing implementation while ((type = parser.next()) == XmlPullParser.TEXT) { } +Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); functor.apply(parser, userId); } @@ -15011,6 +15055,192 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + public byte[] getPermissionGrantBackup(int userId) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system may call getPermissionGrantBackup()"); + } + + ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); + try { + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); + serializer.startDocument(null, true); + serializer.startTag(null, TAG_PERMISSION_BACKUP); + + synchronized (mPackages) { + serializeRuntimePermissionGrantsLPr(serializer, userId); + } + + serializer.endTag(null, TAG_PERMISSION_BACKUP); + serializer.endDocument(); + serializer.flush(); + } catch (Exception e) { + if (DEBUG_BACKUP) { + Slog.e(TAG, "Unable to write default apps for backup", e); + } + return null; + } + + return dataStream.toByteArray(); + } + + @Override + public void restorePermissionGrants(byte[] backup, int userId) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system may call restorePermissionGrants()"); + } + + try { + final XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); + restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP, + new BlobXmlRestorer() { + @Override + public void apply(XmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + synchronized (mPackages) { + processRestoredPermissionGrantsLPr(parser, userId); + } + } + } ); + } catch (Exception e) { + if (DEBUG_BACKUP) { + Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage()); + } + } + } + + private void serializeRuntimePermissionGrantsLPr(XmlSerializer serializer, final int userId) + throws IOException { + serializer.startTag(null, TAG_ALL_GRANTS); + + final int N = mSettings.mPackages.size(); + for (int i = 0; i < N; i++) { + final PackageSetting ps = mSettings.mPackages.valueAt(i); + boolean pkgGrantsKnown = false; + + PermissionsState packagePerms = ps.getPermissionsState(); + + for (PermissionState state : packagePerms.getRuntimePermissionStates(userId)) { + final int grantFlags = state.getFlags(); + // only look at grants that are not system/policy fixed + if ((grantFlags & SYSTEM_RUNTIME_GRANT_MASK) == 0) { + final boolean isGranted = state.isGranted(); + // And only back up the user-twiddled state bits + if (isGranted || (grantFlags & USER_RUNTIME_GRANT_MASK) != 0) { + final String packageName = mSettings.mPackages.keyAt(i); + if (!pkgGrantsKnown) { + serializer.startTag(null, TAG_GRANT); + serializer.attribute(null, ATTR_PACKAGE_NAME, packageName); + pkgGrantsKnown = true; + } + + final boolean userSet = + (grantFlags & FLAG_PERMISSION_USER_SET) != 0; + final boolean userFixed = + (grantFlags & FLAG_PERMISSION_USER_FIXED) != 0; + final boolean revoke = + (grantFlags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0; + + serializer.startTag(null, TAG_PERMISSION); + serializer.attribute(null, ATTR_PERMISSION_NAME, state.getName()); + if (isGranted) { + serializer.attribute(null, ATTR_IS_GRANTED, "true"); + } + if (userSet) { + serializer.attribute(null, ATTR_USER_SET, "true"); + } + if (userFixed) { + serializer.attribute(null, ATTR_USER_FIXED, "true"); + } + if (revoke) { + serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true"); + } + serializer.endTag(null, TAG_PERMISSION); + } + } + } + + if (pkgGrantsKnown) { + serializer.endTag(null, TAG_GRANT); + } + } + + serializer.endTag(null, TAG_ALL_GRANTS); + } + + private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + String pkgName = null; + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + final String tagName = parser.getName(); + if (tagName.equals(TAG_GRANT)) { + pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); + if (DEBUG_BACKUP) { + Slog.v(TAG, "+++ Restoring grants for package " + pkgName); + } + } else if (tagName.equals(TAG_PERMISSION)) { + + final boolean isGranted = "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED)); + final String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME); + + int newFlagSet = 0; + if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) { + newFlagSet |= FLAG_PERMISSION_USER_SET; + } + if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) { + newFlagSet |= FLAG_PERMISSION_USER_FIXED; + } + if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) { + newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE; + } + if (DEBUG_BACKUP) { + Slog.v(TAG, " + Restoring grant: pkg=" + pkgName + " perm=" + permName + + " granted=" + isGranted + " bits=0x" + Integer.toHexString(newFlagSet)); + } + final PackageSetting ps = mSettings.mPackages.get(pkgName); + if (ps != null) { + // Already installed so we apply the grant immediately + if (DEBUG_BACKUP) { + Slog.v(TAG, " + already installed; applying"); + } + PermissionsState perms = ps.getPermissionsState(); + BasePermission bp = mSettings.mPermissions.get(permName); + if (bp != null) { + if (isGranted) { + perms.grantRuntimePermission(bp, userId); + } + if (newFlagSet != 0) { + perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet); + } + } + } else { + // Need to wait for post-restore install to apply the grant + if (DEBUG_BACKUP) { + Slog.v(TAG, " - not yet installed; saving for later"); + } + mSettings.processRestoredPermissionGrantLPr(pkgName, permName, + isGranted, newFlagSet, userId); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <" + TAG_PERMISSION_BACKUP + ">: " + tagName); + XmlUtils.skipCurrentTag(parser); + } + } + + scheduleWriteSettingsLocked(); + mSettings.writeRuntimePermissionsForUserLPr(userId, false); + } + + @Override public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage, int sourceUserId, int targetUserId, int flags) { mContext.enforceCallingOrSelfPermission( @@ -16022,6 +16252,10 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin); } + if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS) && packageName == null) { + mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState); + } + if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) { // XXX should handle packageName != null by dumping only install data that // the given package is involved with. diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 9fef51565c04..ad286855f18e 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -22,6 +22,9 @@ 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.FLAG_PERMISSION_USER_SET; +import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; @@ -32,7 +35,6 @@ import static android.content.pm.PackageManager.MATCH_ENCRYPTION_UNAWARE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; - import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION; import android.annotation.NonNull; @@ -217,6 +219,22 @@ final class Settings { private static final String ATTR_SDK_VERSION = "sdkVersion"; private static final String ATTR_DATABASE_VERSION = "databaseVersion"; + // Bookkeeping for restored permission grants + private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms"; + // package name: ATTR_PACKAGE_NAME + private static final String TAG_PERMISSION_ENTRY = "perm"; + // permission name: ATTR_NAME + // permission granted (boolean): ATTR_GRANTED + private static final String ATTR_USER_SET = "set"; + private static final String ATTR_USER_FIXED = "fixed"; + private static final String ATTR_REVOKE_ON_UPGRADE = "rou"; + + // Flag mask of restored permission grants that are applied at install time + private static final int USER_RUNTIME_GRANT_MASK = + FLAG_PERMISSION_USER_SET + | FLAG_PERMISSION_USER_FIXED + | FLAG_PERMISSION_REVOKE_ON_UPGRADE; + private final Object mLock; private final RuntimePermissionPersistence mRuntimePermissionsPersistence; @@ -238,6 +256,26 @@ final class Settings { private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications = new ArrayMap<String, IntentFilterVerificationInfo>(); + // Bookkeeping for restored user permission grants + final class RestoredPermissionGrant { + String permissionName; + boolean granted; + int grantBits; + + RestoredPermissionGrant(String name, boolean isGranted, int theGrantBits) { + permissionName = name; + granted = isGranted; + grantBits = theGrantBits; + } + } + + // This would be more compact as a flat array of restored grants or something, but we + // may have quite a few, especially during early device lifetime, and avoiding all those + // linear lookups will be important. + private final SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>> + mRestoredUserGrants = + new SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>(); + private static int mFirstAvailableUid = 0; /** Map from volume UUID to {@link VersionInfo} */ @@ -388,7 +426,7 @@ final class Settings { return mPackages.get(name); } - void setInstallStatus(String pkgName, int status) { + void setInstallStatus(String pkgName, final int status) { PackageSetting p = mPackages.get(pkgName); if(p != null) { if(p.getInstallStatus() != status) { @@ -397,6 +435,43 @@ final class Settings { } } + void applyPendingPermissionGrantsLPw(String packageName, int userId) { + ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage = + mRestoredUserGrants.get(userId); + if (grantsByPackage == null || grantsByPackage.size() == 0) { + return; + } + + ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(packageName); + if (grants == null || grants.size() == 0) { + return; + } + + final PackageSetting ps = mPackages.get(packageName); + if (ps == null) { + Slog.e(TAG, "Can't find supposedly installed package " + packageName); + return; + } + final PermissionsState perms = ps.getPermissionsState(); + + for (RestoredPermissionGrant grant : grants) { + BasePermission bp = mPermissions.get(grant.permissionName); + if (bp != null) { + if (grant.granted) { + perms.grantRuntimePermission(bp, userId); + } + perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, grant.grantBits); + } + } + + // And remove it from the pending-grant bookkeeping + grantsByPackage.remove(packageName); + if (grantsByPackage.size() < 1) { + mRestoredUserGrants.remove(userId); + } + writeRuntimePermissionsForUserLPr(userId, false); + } + void setInstallerPackageName(String pkgName, String installerPkgName) { PackageSetting p = mPackages.get(pkgName); if (p != null) { @@ -1725,6 +1800,14 @@ final class Settings { } } + // Specifically for backup/restore + public void processRestoredPermissionGrantLPr(String pkgName, String permission, + boolean isGranted, int restoredFlagSet, int userId) + throws IOException, XmlPullParserException { + mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr( + pkgName, permission, isGranted, restoredFlagSet, userId); + } + void writeDefaultAppsLPr(XmlSerializer serializer, int userId) throws IllegalArgumentException, IllegalStateException, IOException { serializer.startTag(null, TAG_DEFAULT_APPS); @@ -4410,6 +4493,48 @@ final class Settings { pw.print(mReadMessages.toString()); } + void dumpRestoredPermissionGrantsLPr(PrintWriter pw, DumpState dumpState) { + if (mRestoredUserGrants.size() > 0) { + pw.println(); + pw.println("Restored (pending) permission grants:"); + for (int userIndex = 0; userIndex < mRestoredUserGrants.size(); userIndex++) { + ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage = + mRestoredUserGrants.valueAt(userIndex); + if (grantsByPackage != null && grantsByPackage.size() > 0) { + final int userId = mRestoredUserGrants.keyAt(userIndex); + pw.print(" User "); pw.println(userId); + + for (int pkgIndex = 0; pkgIndex < grantsByPackage.size(); pkgIndex++) { + ArraySet<RestoredPermissionGrant> grants = grantsByPackage.valueAt(pkgIndex); + if (grants != null && grants.size() > 0) { + final String pkgName = grantsByPackage.keyAt(pkgIndex); + pw.print(" "); pw.print(pkgName); pw.println(" :"); + + for (RestoredPermissionGrant g : grants) { + pw.print(" "); + pw.print(g.permissionName); + if (g.granted) { + pw.print(" GRANTED"); + } + if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) { + pw.print(" user_set"); + } + if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) { + pw.print(" user_fixed"); + } + if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { + pw.print(" revoke_on_upgrade"); + } + pw.println(); + } + } + } + } + } + pw.println(); + } + } + private static void dumpSplitNames(PrintWriter pw, PackageParser.Package pkg) { if (pkg == null) { pw.print("unknown"); @@ -4507,7 +4632,6 @@ final class Settings { private final class RuntimePermissionPersistence { private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200; - private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000; private final Handler mHandler = new MyHandler(); @@ -4624,6 +4748,7 @@ final class Settings { serializer.setFeature( "http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); + serializer.startTag(null, TAG_RUNTIME_PERMISSIONS); String fingerprint = mFingerprints.get(userId); @@ -4652,6 +4777,51 @@ final class Settings { } serializer.endTag(null, TAG_RUNTIME_PERMISSIONS); + + // Now any restored permission grants that are waiting for the apps + // in question to be installed. These are stored as per-package + // TAG_RESTORED_RUNTIME_PERMISSIONS blocks, each containing some + // number of individual permission grant entities. + if (mRestoredUserGrants.get(userId) != null) { + ArrayMap<String, ArraySet<RestoredPermissionGrant>> restoredGrants = + mRestoredUserGrants.get(userId); + if (restoredGrants != null) { + final int pkgCount = restoredGrants.size(); + for (int i = 0; i < pkgCount; i++) { + final ArraySet<RestoredPermissionGrant> pkgGrants = + restoredGrants.valueAt(i); + if (pkgGrants != null && pkgGrants.size() > 0) { + final String pkgName = restoredGrants.keyAt(i); + serializer.startTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS); + serializer.attribute(null, ATTR_PACKAGE_NAME, pkgName); + + final int N = pkgGrants.size(); + for (int z = 0; z < N; z++) { + RestoredPermissionGrant g = pkgGrants.valueAt(z); + serializer.startTag(null, TAG_PERMISSION_ENTRY); + serializer.attribute(null, ATTR_NAME, g.permissionName); + + if (g.granted) { + serializer.attribute(null, ATTR_GRANTED, "true"); + } + + if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) { + serializer.attribute(null, ATTR_USER_SET, "true"); + } + if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) { + serializer.attribute(null, ATTR_USER_FIXED, "true"); + } + if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { + serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true"); + } + serializer.endTag(null, TAG_PERMISSION_ENTRY); + } + serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS); + } + } + } + } + serializer.endDocument(); destination.finishWrite(out); @@ -4725,6 +4895,31 @@ final class Settings { } } + // Backup/restore support + + public void rememberRestoredUserGrantLPr(String pkgName, String permission, + boolean isGranted, int restoredFlagSet, int userId) { + // This change will be remembered at write-settings time + ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage = + mRestoredUserGrants.get(userId); + if (grantsByPackage == null) { + grantsByPackage = new ArrayMap<String, ArraySet<RestoredPermissionGrant>>(); + mRestoredUserGrants.put(userId, grantsByPackage); + } + + ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(pkgName); + if (grants == null) { + grants = new ArraySet<RestoredPermissionGrant>(); + grantsByPackage.put(pkgName, grants); + } + + RestoredPermissionGrant grant = new RestoredPermissionGrant(permission, + isGranted, restoredFlagSet); + grants.add(grant); + } + + // Private internals + private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); @@ -4764,6 +4959,46 @@ final class Settings { } parsePermissionsLPr(parser, sus.getPermissionsState(), userId); } break; + + case TAG_RESTORED_RUNTIME_PERMISSIONS: { + final String pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); + parseRestoredRuntimePermissionsLPr(parser, pkgName, userId); + } break; + } + } + } + + private void parseRestoredRuntimePermissionsLPr(XmlPullParser parser, + final String pkgName, final int userId) throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + switch (parser.getName()) { + case TAG_PERMISSION_ENTRY: { + final String permName = parser.getAttributeValue(null, ATTR_NAME); + final boolean isGranted = "true".equals( + parser.getAttributeValue(null, ATTR_GRANTED)); + + int permBits = 0; + if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) { + permBits |= FLAG_PERMISSION_USER_SET; + } + if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) { + permBits |= FLAG_PERMISSION_USER_FIXED; + } + if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) { + permBits |= FLAG_PERMISSION_REVOKE_ON_UPGRADE; + } + + if (isGranted || permBits != 0) { + rememberRestoredUserGrantLPr(pkgName, permName, isGranted, permBits, userId); + } + } break; } } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 3d614a35c13b..ce6b3693c2d7 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 @@ public class UserManagerService extends IUserManager.Stub { private final ArrayList<UserRestrictionsListener> mUserRestrictionsListeners = new ArrayList<>(); + private final LockPatternUtils mLockPatternUtils; + private static UserManagerService sInstance; public static UserManagerService getInstance() { @@ -320,6 +322,7 @@ public class UserManagerService extends IUserManager.Stub { } mLocalService = new LocalService(); LocalServices.addService(UserManagerInternal.class, mLocalService); + mLockPatternUtils = new LockPatternUtils(mContext); } void systemReady() { @@ -456,7 +459,7 @@ public class UserManagerService extends IUserManager.Stub { @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 de1c1ea091df..806c4ca6dd5b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -183,6 +183,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int LONG_PRESS_HOME_NOTHING = 0; static final int LONG_PRESS_HOME_RECENT_SYSTEM_UI = 1; static final int LONG_PRESS_HOME_ASSIST = 2; + static final int LONG_PRESS_HOME_PICTURE_IN_PICTURE = 3; + static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_PICTURE_IN_PICTURE; static final int DOUBLE_TAP_HOME_NOTHING = 0; static final int DOUBLE_TAP_HOME_RECENT_SYSTEM_UI = 1; @@ -1313,16 +1315,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - private void handleLongPressOnHome(int deviceId) { - if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) { - mHomeConsumed = true; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); + private void handleLongPressOnHome(int deviceId, KeyEvent event) { + if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) { + return; + } + mHomeConsumed = true; + performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); - if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) { + switch (mLongPressOnHomeBehavior) { + case LONG_PRESS_HOME_RECENT_SYSTEM_UI: toggleRecentApps(); - } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_ASSIST) { + break; + case LONG_PRESS_HOME_ASSIST: launchAssistAction(null, deviceId); - } + break; + case LONG_PRESS_HOME_PICTURE_IN_PICTURE: + handlePipKey(event); + break; + default: + Log.w(TAG, "Not defined home long press behavior: " + mLongPressOnHomeBehavior); + break; } } @@ -1333,6 +1345,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void handlePipKey(KeyEvent event) { + if (DEBUG_INPUT) Log.d(TAG, "handlePipKey event=" + event); + Intent intent = new Intent(Intent.ACTION_PICTURE_IN_PICTURE_BUTTON); + intent.putExtra(Intent.EXTRA_KEY_EVENT, event); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } + private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() { @Override public void run() { @@ -1625,7 +1644,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mLongPressOnHomeBehavior = mContext.getResources().getInteger( com.android.internal.R.integer.config_longPressOnHomeBehavior); if (mLongPressOnHomeBehavior < LONG_PRESS_HOME_NOTHING || - mLongPressOnHomeBehavior > LONG_PRESS_HOME_ASSIST) { + mLongPressOnHomeBehavior > LAST_LONG_PRESS_HOME_BEHAVIOR) { mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING; } @@ -2851,7 +2870,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { if (!keyguardOn) { - handleLongPressOnHome(event.getDeviceId()); + handleLongPressOnHome(event.getDeviceId(), event); } } return -1; @@ -3003,6 +3022,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 +4081,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 +5117,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 +5176,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 +6429,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 990862431799..928444275854 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.text.TextUtils; 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 @@ class ShortcutManager { 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 @@ class ShortcutManager { 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 @@ class ShortcutManager { 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 @@ class ShortcutManager { } final int shortcutChar = shortcutName.charAt(0); + final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true")); final Intent intent; final String title; @@ -158,7 +167,11 @@ class ShortcutManager { } 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 d888c5686d61..1d498e1894eb 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 class TrustManagerService extends SystemService { } 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 class TrustManagerService extends SystemService { 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 @@ public class TrustManagerService extends SystemService { @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 a035826c770d..ebbb8b315d00 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 @@ final class TvInputHal implements Handler.Callback { 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 d814ebf0351f..3193ff8a4977 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 @@ public final class TvInputManagerService extends SystemService { 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 a9025bda2fbc..0c429e5a1cd2 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 @@ class DisplayContent { 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 6bb3e20727e8..71d616d900d1 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 @@ class Task implements DimLayer.DimLayerUser { 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 @@ class Task implements DimLayer.DimLayerUser { 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 @@ class Task implements DimLayer.DimLayerUser { // 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 @@ class Task implements DimLayer.DimLayerUser { 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 @@ class Task implements DimLayer.DimLayerUser { } } - 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 @@ class Task implements DimLayer.DimLayerUser { 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 e75780f6a095..632c03376d60 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 @@ package com.android.server.wm; 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 java.util.ArrayList; 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 @@ public class TaskStack implements DimLayer.DimLayerUser { /** 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 @@ public class TaskStack implements DimLayer.DimLayerUser { // 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 @@ public class TaskStack implements DimLayer.DimLayerUser { } 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 @@ public class TaskStack implements DimLayer.DimLayerUser { } else if (mFullscreen) { setBounds(null); } else { + mUpdateBoundsAfterRotation = true; mTmpRect2.set(mBounds); final int newRotation = mDisplayContent.getDisplayInfo().rotation; if (mRotation == newRotation) { @@ -265,6 +268,10 @@ public class TaskStack implements DimLayer.DimLayerUser { * 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 @@ public class TaskStack implements DimLayer.DimLayerUser { "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 @@ public class TaskStack implements DimLayer.DimLayerUser { 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 685df25910d6..7d142eca0bb4 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 android.widget.Toast; 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 @@ public class WindowManagerService extends IWindowManager.Stub 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 058fa67a5e4a..afbaf00652a5 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 @@ final class WindowState implements WindowManagerPolicy.WindowState { 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 @@ final class WindowState implements WindowManagerPolicy.WindowState { 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 83ab19094fb0..a3a59c009b50 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 @@ class WindowStateAnimator { } } w.mToken.hasVisible = true; + + final Task task = w.getTask(); + if (task != null) { + task.scheduleShowNonResizeableDockToastIfNeeded(); + } } } @@ -1513,10 +1518,6 @@ class WindowStateAnimator { 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 380763abf6e3..f14b03207d95 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final IPackageManager mIPackageManager; final UserManager mUserManager; final UserManagerInternal mUserManagerInternal; + private final LockPatternUtils mLockPatternUtils; final LocalService mLocalService; @@ -1329,6 +1330,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager()); mLocalService = new LocalService(); + mLockPatternUtils = new LockPatternUtils(mContext); mHasFeature = mContext.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN); @@ -2573,10 +2575,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - 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 @@ -2618,7 +2626,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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(); @@ -2638,7 +2646,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // 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++) { @@ -2654,6 +2662,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + 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) { @@ -3224,8 +3241,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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)) { @@ -3899,6 +3916,42 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + @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( @@ -4028,7 +4081,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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"); } @@ -4055,7 +4108,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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); @@ -4647,10 +4700,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ -4669,9 +4721,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // 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); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 287b39c235c3..ee5f23fe028c 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.job.JobSchedulerService; 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 @@ public final class SystemServer { "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 @@ public final class SystemServer { } 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 @@ public final class SystemServer { mSystemServiceManager.startService(TvInputManagerService.class); } + mSystemServiceManager.startService(MediaResourceMonitorService.class); + if (!disableNonCoreServices) { traceBeginAndSlog("StartMediaRouterService"); try { diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java index 0af1525ff716..5ee4066f69ec 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 @@ final class RemotePrintService implements DeathRecipient { } 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/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index eed326e4a4c4..248cf664734c 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/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index 312b1b088bf1..ae0a25ed05d6 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 @@ public class JobStoreTest extends AndroidTestCase { 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 000000000000..3ea86f2e9ac0 --- /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 000000000000..63bccfa01413 --- /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 e27441e12a72..4253cd4d7916 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements 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 @@ public class UsageStatsService extends SystemService implements * 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 72621a422ce6..224faf459fc0 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 @@ class UserUsageStatsService { 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 @@ class UserUsageStatsService { interface StatsUpdatedListener { void onStatsUpdated(); + long getAppIdleRollingWindowDurationMillis(); } UserUsageStatsService(Context context, int userId, File usageStatsDir, @@ -133,6 +135,8 @@ class UserUsageStatsService { stat.updateConfigurationStats(null, stat.lastTimeSaved); } + refreshAppIdleRollingWindow(currentTimeMillis, deviceUsageTime); + if (mDatabase.isNewUpdate()) { initializeDefaultsForApps(currentTimeMillis, deviceUsageTime, mDatabase.isFirstUpdate()); @@ -158,18 +162,24 @@ class UserUsageStatsService { 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 @@ class UserUsageStatsService { 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 @@ class UserUsageStatsService { } } + if (event.mEventType != Event.CONFIGURATION_CHANGE) { + mAppIdleRollingWindow.update(event.mPackage, event.mTimeStamp, event.mEventType); + mAppIdleRollingWindow.updateBeginIdleTime(event.mPackage, deviceUsageTime); + } + notifyStatsChanged(); } @@ -222,6 +237,7 @@ class UserUsageStatsService { for (IntervalStats stats : mCurrentStats) { stats.updateBeginIdleTime(packageName, beginIdleTime); } + mAppIdleRollingWindow.updateBeginIdleTime(packageName, beginIdleTime); notifyStatsChanged(); } @@ -229,6 +245,7 @@ class UserUsageStatsService { for (IntervalStats stats : mCurrentStats) { stats.updateSystemLastUsedTime(packageName, lastUsedTime); } + mAppIdleRollingWindow.updateSystemLastUsedTime(packageName, lastUsedTime); notifyStatsChanged(); } @@ -387,9 +404,8 @@ class UserUsageStatsService { } 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 @@ class UserUsageStatsService { } 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 @@ class UserUsageStatsService { } } - 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 @@ class UserUsageStatsService { } 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 @@ class UserUsageStatsService { } } } + mStatsChanged = false; updateRolloverDeadline(); } @@ -516,6 +534,71 @@ class UserUsageStatsService { 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 @@ class UserUsageStatsService { 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/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 80c5b1e204f4..10815b396265 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -495,6 +495,13 @@ public class CarrierConfigManager { */ 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"; @@ -638,6 +645,7 @@ public class CarrierConfigManager { 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); diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 99989377c374..39f321365c8d 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -24,6 +24,7 @@ import android.content.Intent; 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 @@ public class SubscriptionManager { * 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 @@ public class SubscriptionManager { } 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 59a8a67f85a2..b9d7297587a1 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -53,6 +53,9 @@ public class CallerInfo { 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 class CallerInfo { 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 @@ public class CallerInfo { // TODO: Move all the basic initialization here? mIsEmergency = false; mIsVoiceMail = false; + userType = USER_TYPE_CURRENT; } /** @@ -173,6 +179,7 @@ public class CallerInfo { 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 @@ public class CallerInfo { 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 @@ public class CallerInfo { /** * @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 @@ public class CallerInfo { .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 000000000000..5dffa280fef2 --- /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 a183de5c7396..ecd89eda4577 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -35,17 +35,17 @@ public class PhoneConstants { 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 class PhoneConstants { 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 d2e4de30421b..c70f8cf2fdfc 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -75,6 +75,7 @@ public class TelephonyIntents { */ 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 class TelephonyIntents { */ 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 989e80250405..e82b3b9d45fc 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 000000000000..f01b920be9ec --- /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 564d301bae71..854a8bb7c4a1 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 000000000000..6cca7c3a2dc1 --- /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 adfb575a0da6..aa222dd6aa66 100644 --- a/tests/FeatureSplit/feature1/Android.mk +++ b/tests/FeatureSplit/feature1/Android.mk @@ -22,10 +22,12 @@ LOCAL_PACKAGE_NAME := FeatureSplit1 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 2aadc6d9c2f3..42619b623e10 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 70eb56a76fb3..10dbd9733889 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 000000000000..def133919f90 --- /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 e69cbe89234f..1a0322bf686e 100644 --- a/tests/FeatureSplit/feature2/Android.mk +++ b/tests/FeatureSplit/feature2/Android.mk @@ -24,6 +24,8 @@ LOCAL_MODULE_TAGS := tests 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 @@ $(localRStamp): $(featureOfApk) $(featureAfterApk) 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 d139900eae46..b50044ac37b7 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 64353deb204d..13f8b3b54f68 100644 --- a/tools/aapt2/ConfigDescription.cpp +++ b/tools/aapt2/ConfigDescription.cpp @@ -30,6 +30,11 @@ using android::ResTable_config; 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 4af089dc282a..5749816f5124 100644 --- a/tools/aapt2/ConfigDescription.h +++ b/tools/aapt2/ConfigDescription.h @@ -29,6 +29,11 @@ namespace aapt { * 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 5fce2c16f630..b4e75f9be3a9 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -210,4 +210,19 @@ void Debug::printStyleGraph(ResourceTable* table, const ResourceName& targetStyl 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 5b0d7d69e703..ba05be91e91d 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 9435396991df..666e8a8efff1 100644 --- a/tools/aapt2/Flags.cpp +++ b/tools/aapt2/Flags.cpp @@ -81,6 +81,8 @@ Flags& Flags::optionalSwitch(const StringPiece& name, const StringPiece& descrip } 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 @@ void Flags::usage(const StringPiece& command, std::ostream* out) { // 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 5e7d3ec4c1e0..b37d366a7887 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -70,6 +70,7 @@ static uint32_t parseFormatAttribute(const StringPiece16& str) { */ struct ParsedResource { ResourceName name; + ConfigDescription config; Source source; ResourceId id; Maybe<SymbolState> symbolState; @@ -108,8 +109,7 @@ bool ResourceParser::shouldStripResource(const ResourceNameRef& name, } // 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 @@ static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& c 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 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) { } ParsedResource parsedResource; + parsedResource.config = mConfig; parsedResource.source = mSource.withLine(parser->getLineNumber()); parsedResource.comment = std::move(comment); @@ -310,7 +311,7 @@ bool ResourceParser::parseResources(xml::XmlPullParser* parser) { // 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 ResourceParser::parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* o 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 @@ Maybe<Attribute::Symbol> ResourceParser::parseEnumOrFlagItem(xml::XmlPullParser* } 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 @@ bool ResourceParser::parsePlural(xml::XmlPullParser* parser, ParsedResource* out 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 8d10ba14924b..cf0fcd11b903 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -47,13 +47,26 @@ struct ResourceParserTest : public ::testing::Test { 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 @@ TEST_F(ResourceParserTest, ParseAttr) { 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 1dc123e45949..07f62afe05b9 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -176,7 +176,7 @@ bool isAttributeReference(const StringPiece16& str) { /* * 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 @@ Maybe<Reference> parseStyleParentReference(const StringPiece16& str, std::string *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 @@ std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr, 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 @@ std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str) { 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 88efa6779021..c9f93e1dd7c2 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 @@ TEST(ResourceUtilsTest, ParseStyleParentReference) { 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 @@ TEST(ResourceUtilsTest, ParseStyleParentReference) { 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 8552f470b123..aadb00b6be2a 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 @@ bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) { // 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 c722fbeca690..e93c2fba7f3c 100644 --- a/tools/aapt2/StringPool_test.cpp +++ b/tools/aapt2/StringPool_test.cpp @@ -180,6 +180,22 @@ TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) { 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 c78670fbc20d..689ace6e6aa1 100644 --- a/tools/aapt2/compile/Compile.cpp +++ b/tools/aapt2/compile/Compile.cpp @@ -117,7 +117,11 @@ static std::string buildIntermediateFilename(const ResourcePathData& data) { 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 @@ static bool compileFile(IAaptContext* context, const CompileOptions& options, 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 9081c55fc6e1..467e60464a68 100644 --- a/tools/aapt2/io/Data.h +++ b/tools/aapt2/io/Data.h @@ -79,6 +79,21 @@ private: 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 76f87aec6556..e758d8a421e1 100644 --- a/tools/aapt2/io/FileSystem.cpp +++ b/tools/aapt2/io/FileSystem.cpp @@ -32,7 +32,10 @@ RegularFile::RegularFile(const Source& source) : mSource(source) { 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 8a87d9691b5d..51b9cecb7b7e 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -51,16 +51,19 @@ struct LinkOptions { 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 @@ public: 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 @@ public: 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 @@ public: return false; } - if (writer->startEntry(path, ArchiveEntry::kCompress)) { if (writer->writeEntry(buffer)) { if (writer->finishEntry()) { @@ -508,7 +523,10 @@ public: } 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic; if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(), - mContext.getCompilationPackage(), options)) { + outputPackage, options)) { return 1; } @@ -838,9 +863,8 @@ public: 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 @@ int link(const std::vector<StringPiece>& args) { 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 @@ int link(const std::vector<StringPiece>& args) { "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 @@ int link(const std::vector<StringPiece>& args) { } 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 2034c5701492..9baf1d86795c 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 @@ static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element 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 @@ bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) { 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 a77e6d5f709c..b8d9c833ff05 100644 --- a/tools/aapt2/link/ManifestFixer.h +++ b/tools/aapt2/link/ManifestFixer.h @@ -28,6 +28,10 @@ namespace aapt { 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 f6bf895a7f57..f40fbfb2e81a 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -82,8 +82,6 @@ TEST_F(ManifestFixerTest, EnsureManifestHasPackage) { 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 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { 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 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { </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 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { </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 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { 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 @@ TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) { 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 a81dc7b47926..04e8199d85b4 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -163,6 +163,11 @@ Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError) } 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 9ecc974d3a1c..7b0c71d93bb5 100644 --- a/tools/aapt2/util/Util.cpp +++ b/tools/aapt2/util/Util.cpp @@ -175,10 +175,11 @@ Maybe<std::u16string> getFullyQualifiedClassName(const StringPiece16& package, 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 9208e07e635b..1e0c7fa9152d 100644 --- a/tools/aapt2/util/Util_test.cpp +++ b/tools/aapt2/util/Util_test.cpp @@ -144,8 +144,7 @@ TEST(UtilTest, IsJavaPackageName) { 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 01ee18b1649c..9f0153a09c70 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -19,6 +19,7 @@ package android.view; 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 class IWindowManagerImpl implements IWindowManager { 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 d691c8ea71a5..411417c883fb 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 class WindowCallback implements Window.Callback { 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 63411b070d58..e6fb6207a206 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 @@ public final class BridgeContext extends Context { 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 3662573849a9..48012db01aae 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 @@ public final class BridgeWindow implements IWindow { } @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 7e5ae8d8c756..d417eb76b45f 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 class WindowManagerImpl implements WindowManager { 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 9aab340909fd..8c60bae4f11e 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 @@ public class AdapterHelper { 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 @@ public class AdapterHelper { 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 dea86bfb5a38..b1b375908dad 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 @@ public class Main { } /** - * 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 @@ public class Main { } /** - * 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 @@ public class Main { } /** - * 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 @@ public class Main { } /** - * 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 dc329e2a4818..0c06ae8300cd 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -24,15 +24,12 @@ import android.net.ProxyInfo; 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 @@ public class WifiConfiguration implements Parcelable { } /** @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 @@ public class WifiConfiguration implements Parcelable { /** * 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 @@ public class WifiConfiguration implements Parcelable { /** * @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 @@ public class WifiConfiguration implements Parcelable { /** * @hide - * Uid used by autoJoin - */ - public String autoJoinBSSID; - - /** - * @hide * Status of user approval for connection */ public int userApproved = USER_UNSPECIFIED; @@ -444,77 +417,6 @@ public class WifiConfiguration implements Parcelable { /** @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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { /** * @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 @@ public class WifiConfiguration implements Parcelable { /** * @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 @@ public class WifiConfiguration implements Parcelable { /** * @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 class WifiConfiguration implements Parcelable { */ 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { wepKeys[i] = null; } enterpriseConfig = new WifiEnterpriseConfig(); - autoJoinStatus = AUTO_JOIN_ENABLED; selfAdded = false; didSelfAdd = false; ephemeral = false; @@ -929,6 +1111,7 @@ public class WifiConfiguration implements Parcelable { mIpConfiguration = new IpConfiguration(); lastUpdateUid = -1; creatorUid = -1; + shared = true; } /** @@ -946,10 +1129,12 @@ public class WifiConfiguration implements Parcelable { * @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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { } 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"); - } - 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"); + 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.numAssociation > 0) { sbuf.append(" numAssociation ").append(this.numAssociation).append("\n"); } @@ -1094,11 +1269,15 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { } else { key = SSID + KeyMgmt.strings[KeyMgmt.NONE]; } + if (!shared) { + key += "-" + Integer.toString(UserHandle.getUserId(creatorUid)); + } mCachedConfigKey = key; } return key; @@ -1346,27 +1526,6 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { providerFriendlyName = source.providerFriendlyName; preSharedKey = source.preSharedKey; + mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus()); apBand = source.apBand; apChannel = source.apChannel; @@ -1458,7 +1621,6 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 7a5a74ff6584..c1269f9518cc 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1007,7 +1007,7 @@ public class WifiManager { /** * @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 000000000000..38dddc239247 --- /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 000000000000..23e37547b9f0 --- /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 000000000000..13efc361fbb3 --- /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 000000000000..ff3d29f41545 --- /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 000000000000..773f83bc70dc --- /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 000000000000..15e4ddfd1c0b --- /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 000000000000..80119eb0f4fd --- /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 000000000000..ff692936bf06 --- /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 000000000000..bbc53408f2b5 --- /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 000000000000..662fdb83f74c --- /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 000000000000..cd6e91882834 --- /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 000000000000..44849bc04a57 --- /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 000000000000..5c4f8fb385fa --- /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 000000000000..ea8785a858ee --- /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 000000000000..eae0a55f7af1 --- /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 000000000000..877f9937bbb9 --- /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 000000000000..81b38f4c12a3 --- /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 000000000000..c6b384e3ae00 --- /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 000000000000..c9d08c7610f3 --- /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 000000000000..7dfdd32a2f74 --- /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); + } +} |