diff options
320 files changed, 7079 insertions, 3561 deletions
diff --git a/api/current.txt b/api/current.txt index d1a904a..b9c67c4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -858,6 +858,7 @@ package android { field public static final int resource = 16842789; // 0x1010025 field public static final int restoreAnyVersion = 16843450; // 0x10102ba field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d + field public static final int restrictedAccountType = 16843733; // 0x10103d5 field public static final int right = 16843183; // 0x10101af field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093 field public static final int ringtoneType = 16843257; // 0x10101f9 @@ -2209,6 +2210,7 @@ package android.accounts { field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; field public static final java.lang.String AUTHENTICATOR_META_DATA_NAME = "android.accounts.AccountAuthenticator"; field public static final int ERROR_CODE_BAD_ARGUMENTS = 7; // 0x7 + field public static final int ERROR_CODE_BAD_AUTHENTICATION = 9; // 0x9 field public static final int ERROR_CODE_BAD_REQUEST = 8; // 0x8 field public static final int ERROR_CODE_CANCELED = 4; // 0x4 field public static final int ERROR_CODE_INVALID_RESPONSE = 5; // 0x5 @@ -4612,8 +4614,13 @@ package android.bluetooth { method public boolean isEnabled(); method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException; method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException; + method public boolean registerCallback(android.bluetooth.BluetoothAdapterCallback); method public boolean setName(java.lang.String); method public boolean startDiscovery(); + method public boolean startLeScan(); + method public boolean startLeScan(java.util.UUID[]); + method public void stopLeScan(); + method public boolean unRegisterCallback(android.bluetooth.BluetoothAdapterCallback); field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; field public static final java.lang.String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; field public static final java.lang.String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED"; @@ -4644,6 +4651,14 @@ package android.bluetooth { field public static final int STATE_TURNING_ON = 11; // 0xb } + public abstract class BluetoothAdapterCallback { + ctor public BluetoothAdapterCallback(); + method public void onCallbackRegistration(int); + method public void onLeScan(android.bluetooth.BluetoothDevice, int, byte[]); + field public static final int CALLBACK_REGISTERED = 0; // 0x0 + field public static final int CALLBACK_REGISTRATION_FAILURE = 1; // 0x1 + } + public class BluetoothAssignedNumbers { field public static final int ACCEL_SEMICONDUCTOR = 74; // 0x4a field public static final int ALCATEL = 36; // 0x24 @@ -4836,6 +4851,7 @@ package android.bluetooth { } public final class BluetoothDevice implements android.os.Parcelable { + method public android.bluetooth.BluetoothGatt connectGattServer(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback); method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException; method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException; method public int describeContents(); @@ -4868,6 +4884,159 @@ package android.bluetooth { field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; } + public final class BluetoothGatt implements android.bluetooth.BluetoothProfile { + method public void abortReliableWrite(android.bluetooth.BluetoothDevice); + method public boolean beginReliableWrite(); + method public void disconnect(); + method public boolean discoverServices(); + method public boolean executeReliableWrite(); + method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method public int getConnectionState(android.bluetooth.BluetoothDevice); + method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]); + method public android.bluetooth.BluetoothGattService getService(java.util.UUID); + method public java.util.List<android.bluetooth.BluetoothGattService> getServices(); + method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic); + method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor); + method public boolean readRemoteRssi(); + method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean); + method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic); + method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor); + field public static final int GATT_FAILURE = 0; // 0x0 + field public static final int GATT_INSUFFICIENT_AUTHENTICATION = 5; // 0x5 + field public static final int GATT_INSUFFICIENT_ENCRYPTION = 15; // 0xf + field public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 13; // 0xd + field public static final int GATT_INVALID_OFFSET = 7; // 0x7 + field public static final int GATT_READ_NOT_PERMITTED = 2; // 0x2 + field public static final int GATT_REQUEST_NOT_SUPPORTED = 6; // 0x6 + field public static final int GATT_SUCCESS = 0; // 0x0 + field public static final int GATT_WRITE_NOT_PERMITTED = 3; // 0x3 + } + + public abstract class BluetoothGattCallback { + ctor public BluetoothGattCallback(); + method public void onCharacteristicChanged(android.bluetooth.BluetoothGattCharacteristic); + method public void onCharacteristicRead(android.bluetooth.BluetoothGattCharacteristic, int); + method public void onCharacteristicWrite(android.bluetooth.BluetoothGattCharacteristic, int); + method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int); + method public void onDescriptorRead(android.bluetooth.BluetoothGattDescriptor, int); + method public void onDescriptorWrite(android.bluetooth.BluetoothGattDescriptor, int); + method public void onReadRemoteRssi(android.bluetooth.BluetoothDevice, int, int); + method public void onReliableWriteCompleted(android.bluetooth.BluetoothDevice, int); + method public void onServicesDiscovered(android.bluetooth.BluetoothDevice, int); + } + + public class BluetoothGattCharacteristic { + ctor public BluetoothGattCharacteristic(java.util.UUID, int, int); + method public boolean addDescriptor(android.bluetooth.BluetoothGattDescriptor); + method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID); + method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors(); + method public java.lang.Float getFloatValue(int, int); + method public int getInstanceId(); + method public java.lang.Integer getIntValue(int, int); + method public int getPermissions(); + method public int getProperties(); + method public android.bluetooth.BluetoothGattService getService(); + method public java.lang.String getStringValue(int); + method public java.util.UUID getUuid(); + method public byte[] getValue(); + method public int getWriteType(); + method public boolean setValue(byte[]); + method public boolean setValue(int, int, int); + method public boolean setValue(int, int, int, int); + method public boolean setValue(java.lang.String); + method public void setWriteType(int); + field public static final int FORMAT_FLOAT = 52; // 0x34 + field public static final int FORMAT_SFLOAT = 50; // 0x32 + field public static final int FORMAT_SINT16 = 34; // 0x22 + field public static final int FORMAT_SINT32 = 36; // 0x24 + field public static final int FORMAT_SINT8 = 33; // 0x21 + field public static final int FORMAT_UINT16 = 18; // 0x12 + field public static final int FORMAT_UINT32 = 20; // 0x14 + field public static final int FORMAT_UINT8 = 17; // 0x11 + field public static final int PERMISSION_READ = 1; // 0x1 + field public static final int PERMISSION_READ_ENCRYPTED = 2; // 0x2 + field public static final int PERMISSION_READ_ENCRYPTED_MITM = 4; // 0x4 + field public static final int PERMISSION_WRITE = 16; // 0x10 + field public static final int PERMISSION_WRITE_ENCRYPTED = 32; // 0x20 + field public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 64; // 0x40 + field public static final int PERMISSION_WRITE_SIGNED = 128; // 0x80 + field public static final int PERMISSION_WRITE_SIGNED_MITM = 256; // 0x100 + field public static final int PROPERTY_BROADCAST = 1; // 0x1 + field public static final int PROPERTY_EXTENDED_PROPS = 128; // 0x80 + field public static final int PROPERTY_INDICATE = 32; // 0x20 + field public static final int PROPERTY_NOTIFY = 16; // 0x10 + field public static final int PROPERTY_READ = 2; // 0x2 + field public static final int PROPERTY_SIGNED_WRITE = 64; // 0x40 + field public static final int PROPERTY_WRITE = 8; // 0x8 + field public static final int PROPERTY_WRITE_NO_RESPONSE = 4; // 0x4 + field public static final int WRITE_TYPE_DEFAULT = 2; // 0x2 + field public static final int WRITE_TYPE_NO_RESPONSE = 1; // 0x1 + field public static final int WRITE_TYPE_SIGNED = 4; // 0x4 + field protected java.util.List mDescriptors; + } + + public class BluetoothGattDescriptor { + ctor public BluetoothGattDescriptor(java.util.UUID, int); + method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic(); + method public int getPermissions(); + method public java.util.UUID getUuid(); + method public byte[] getValue(); + method public boolean setValue(byte[]); + field public static final byte[] DISABLE_NOTIFICATION_VALUE; + field public static final byte[] ENABLE_INDICATION_VALUE; + field public static final byte[] ENABLE_NOTIFICATION_VALUE; + field public static final int PERMISSION_READ = 1; // 0x1 + field public static final int PERMISSION_READ_ENCRYPTED = 2; // 0x2 + field public static final int PERMISSION_READ_ENCRYPTED_MITM = 4; // 0x4 + field public static final int PERMISSION_WRITE = 16; // 0x10 + field public static final int PERMISSION_WRITE_ENCRYPTED = 32; // 0x20 + field public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 64; // 0x40 + field public static final int PERMISSION_WRITE_SIGNED = 128; // 0x80 + field public static final int PERMISSION_WRITE_SIGNED_MITM = 256; // 0x100 + } + + public final class BluetoothGattServer implements android.bluetooth.BluetoothProfile { + method public boolean addService(android.bluetooth.BluetoothGattService); + method public void cancelConnection(android.bluetooth.BluetoothDevice); + method public void clearServices(); + method public boolean connect(android.bluetooth.BluetoothDevice, boolean); + method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method public int getConnectionState(android.bluetooth.BluetoothDevice); + method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]); + method public android.bluetooth.BluetoothGattService getService(java.util.UUID); + method public java.util.List<android.bluetooth.BluetoothGattService> getServices(); + method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean); + method public boolean removeService(android.bluetooth.BluetoothGattService); + method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]); + } + + public abstract class BluetoothGattServerCallback { + ctor public BluetoothGattServerCallback(); + method public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattCharacteristic); + method public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattCharacteristic, boolean, boolean, int, byte[]); + method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int); + method public void onDescriptorReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattDescriptor); + method public void onDescriptorWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattDescriptor, boolean, boolean, int, byte[]); + method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean); + method public void onServiceAdded(int, android.bluetooth.BluetoothGattService); + } + + public class BluetoothGattService { + ctor public BluetoothGattService(java.util.UUID, int); + method public boolean addCharacteristic(android.bluetooth.BluetoothGattCharacteristic); + method public boolean addService(android.bluetooth.BluetoothGattService); + method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic(java.util.UUID); + method public java.util.List<android.bluetooth.BluetoothGattCharacteristic> getCharacteristics(); + method public java.util.List<android.bluetooth.BluetoothGattService> getIncludedServices(); + method public int getInstanceId(); + method public int getType(); + method public java.util.UUID getUuid(); + field public static final int SERVICE_TYPE_PRIMARY = 0; // 0x0 + field public static final int SERVICE_TYPE_SECONDARY = 1; // 0x1 + field protected java.util.List mCharacteristics; + field protected java.util.List mIncludedServices; + } + public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile { method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); method public int getConnectionState(android.bluetooth.BluetoothDevice); @@ -4930,6 +5099,14 @@ package android.bluetooth { method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int); } + public final class BluetoothManager { + method public android.bluetooth.BluetoothAdapter getAdapter(); + method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int); + method public int getConnectionState(android.bluetooth.BluetoothDevice, int); + method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int, int[]); + method public android.bluetooth.BluetoothGattServer openGattServer(android.content.Context, android.bluetooth.BluetoothGattServerCallback); + } + public abstract interface BluetoothProfile { method public abstract java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); method public abstract int getConnectionState(android.bluetooth.BluetoothDevice); @@ -4937,6 +5114,8 @@ package android.bluetooth { field public static final int A2DP = 2; // 0x2 field public static final java.lang.String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE"; field public static final java.lang.String EXTRA_STATE = "android.bluetooth.profile.extra.STATE"; + field public static final int GATT = 7; // 0x7 + field public static final int GATT_SERVER = 8; // 0x8 field public static final int HEADSET = 1; // 0x1 field public static final int HEALTH = 3; // 0x3 field public static final int STATE_CONNECTED = 2; // 0x2 @@ -5494,6 +5673,7 @@ package android.content { field public static final int BIND_IMPORTANT = 64; // 0x40 field public static final int BIND_NOT_FOREGROUND = 4; // 0x4 field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20 + field public static final java.lang.String BLUETOOTH_SERVICE = "bluetooth"; field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard"; field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity"; field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2 @@ -5998,6 +6178,7 @@ package android.content { field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token"; field public static final java.lang.String EXTRA_REPLACING = "android.intent.extra.REPLACING"; field public static final java.lang.String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions"; + field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent"; field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT"; field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON"; field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE"; @@ -6246,12 +6427,25 @@ package android.content { ctor public RestrictionEntry(java.lang.String, java.lang.String[]); ctor public RestrictionEntry(android.os.Parcel); method public int describeContents(); - method public boolean getBooleanValue(); - method public java.lang.String[] getMultipleValues(); - method public java.lang.String getStringValue(); - method public void setMultipleValues(java.lang.String[]); - method public void setValue(java.lang.String); - method public void setValue(boolean); + method public java.lang.String[] getAllSelectedStrings(); + method public java.lang.String[] getChoiceEntries(); + method public java.lang.String[] getChoiceValues(); + method public java.lang.String getDescription(); + method public java.lang.String getKey(); + method public boolean getSelectedState(); + method public java.lang.String getSelectedString(); + method public java.lang.String getTitle(); + method public int getType(); + method public void setAllSelectedStrings(java.lang.String[]); + method public void setChoiceEntries(java.lang.String[]); + method public void setChoiceEntries(android.content.Context, int); + method public void setChoiceValues(java.lang.String[]); + method public void setChoiceValues(android.content.Context, int); + method public void setDescription(java.lang.String); + method public void setSelectedState(boolean); + method public void setSelectedString(java.lang.String); + method public void setTitle(java.lang.String); + method public void setType(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int TYPE_BOOLEAN = 1; // 0x1 @@ -6259,12 +6453,6 @@ package android.content { field public static final int TYPE_CHOICE_LEVEL = 3; // 0x3 field public static final int TYPE_MULTI_SELECT = 4; // 0x4 field public static final int TYPE_NULL = 0; // 0x0 - field public java.lang.String[] choices; - field public java.lang.String description; - field public java.lang.String key; - field public java.lang.String title; - field public int type; - field public java.lang.String[] values; } public class SearchRecentSuggestionsProvider extends android.content.ContentProvider { @@ -6657,6 +6845,7 @@ package android.content.pm { method public abstract boolean addPermission(android.content.pm.PermissionInfo); method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo); method public abstract deprecated void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName); + method public android.content.Intent buildPermissionRequestIntent(java.lang.String...); method public abstract java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]); method public abstract int checkPermission(java.lang.String, java.lang.String); method public abstract int checkSignatures(java.lang.String, java.lang.String); @@ -10305,10 +10494,12 @@ package android.hardware.usb { } public class UsbDeviceConnection { - method public int bulkTransfer(android.hardware.usb.UsbEndpoint, byte[], int, int); + method public deprecated int bulkTransfer(android.hardware.usb.UsbEndpoint, byte[], int, int); + method public int bulkTransfer(android.hardware.usb.UsbEndpoint, byte[], int, int, int); method public boolean claimInterface(android.hardware.usb.UsbInterface, boolean); method public void close(); - method public int controlTransfer(int, int, int, int, byte[], int, int); + method public deprecated int controlTransfer(int, int, int, int, byte[], int, int); + method public int controlTransfer(int, int, int, int, byte[], int, int, int); method public int getFileDescriptor(); method public byte[] getRawDescriptors(); method public java.lang.String getSerial(); @@ -16318,7 +16509,7 @@ package android.os { method public abstract android.os.IBinder asBinder(); } - public class Looper { + public final class Looper { method public void dump(android.util.Printer, java.lang.String); method public static android.os.Looper getMainLooper(); method public java.lang.Thread getThread(); @@ -16373,9 +16564,9 @@ package android.os { field public int what; } - public class MessageQueue { - method public final void addIdleHandler(android.os.MessageQueue.IdleHandler); - method public final void removeIdleHandler(android.os.MessageQueue.IdleHandler); + public final class MessageQueue { + method public void addIdleHandler(android.os.MessageQueue.IdleHandler); + method public void removeIdleHandler(android.os.MessageQueue.IdleHandler); } public static abstract interface MessageQueue.IdleHandler { @@ -16749,6 +16940,7 @@ package android.os { method public android.os.StrictMode.VmPolicy build(); method public android.os.StrictMode.VmPolicy.Builder detectActivityLeaks(); method public android.os.StrictMode.VmPolicy.Builder detectAll(); + method public android.os.StrictMode.VmPolicy.Builder detectFileUriExposure(); method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects(); method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects(); method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects(); @@ -16793,6 +16985,7 @@ package android.os { } public class UserManager { + method public static synchronized android.os.UserManager get(android.content.Context); method public long getSerialNumberForUser(android.os.UserHandle); method public int getUserCount(); method public android.os.UserHandle getUserForSerialNumber(long); @@ -16806,11 +16999,14 @@ package android.os { method public void setUserRestriction(java.lang.String, boolean); method public void setUserRestrictions(android.os.Bundle); method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle); + field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth"; field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi"; field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps"; + field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources"; field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts"; field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; + field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer"; } public abstract class Vibrator { @@ -20561,6 +20757,8 @@ package android.security { method public static android.content.Intent createInstallIntent(); method public static java.security.cert.X509Certificate[] getCertificateChain(android.content.Context, java.lang.String) throws java.lang.InterruptedException, android.security.KeyChainException; method public static java.security.PrivateKey getPrivateKey(android.content.Context, java.lang.String) throws java.lang.InterruptedException, android.security.KeyChainException; + method public static boolean isBoundKeyAlgorithm(java.lang.String); + method public static boolean isKeyAlgorithmSupported(java.lang.String); field public static final java.lang.String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED"; field public static final java.lang.String EXTRA_CERTIFICATE = "CERT"; field public static final java.lang.String EXTRA_NAME = "name"; @@ -21251,6 +21449,7 @@ package android.telephony { method public int getDataState(); method public java.lang.String getDeviceId(); method public java.lang.String getDeviceSoftwareVersion(); + method public java.lang.String getGroupIdLevel1(); method public java.lang.String getLine1Number(); method public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); method public java.lang.String getNetworkCountryIso(); @@ -22608,6 +22807,7 @@ package android.text.format { method public static java.lang.CharSequence format(java.lang.CharSequence, long); method public static java.lang.CharSequence format(java.lang.CharSequence, java.util.Date); method public static java.lang.CharSequence format(java.lang.CharSequence, java.util.Calendar); + method public static java.lang.String getBestDateTimePattern(java.util.Locale, java.lang.String); method public static java.text.DateFormat getDateFormat(android.content.Context); method public static char[] getDateFormatOrder(android.content.Context); method public static java.text.DateFormat getLongDateFormat(android.content.Context); diff --git a/cmds/interrupter/Android.mk b/cmds/interrupter/Android.mk new file mode 100644 index 0000000..e324627 --- /dev/null +++ b/cmds/interrupter/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + interrupter.c +LOCAL_MODULE := interrupter +LOCAL_MODULE_TAGS := eng tests +LOCAL_LDFLAGS := -ldl + +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + interrupter.c +LOCAL_MODULE := interrupter +LOCAL_MODULE_TAGS := eng tests +LOCAL_LDFLAGS := -ldl + +include $(BUILD_HOST_SHARED_LIBRARY)
\ No newline at end of file diff --git a/cmds/interrupter/interrupter.c b/cmds/interrupter/interrupter.c new file mode 100644 index 0000000..ae55515 --- /dev/null +++ b/cmds/interrupter/interrupter.c @@ -0,0 +1,53 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * The probability of a syscall failing from 0.0 to 1.0 + */ +#define PROBABILITY 0.9 + + + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +/* for various intercepted calls */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <fcntl.h> + +/* For builds on glibc */ +#define __USE_GNU +#include <dlfcn.h> + +#include "interrupter.h" + +static int probability = PROBABILITY * RAND_MAX; + +static int maybe_interrupt() { + if (rand() < probability) { + return 1; + } + return 0; +} + +DEFINE_INTERCEPT(read, ssize_t, int, void*, size_t); +DEFINE_INTERCEPT(write, ssize_t, int, const void*, size_t); +DEFINE_INTERCEPT(accept, int, int, struct sockaddr*, socklen_t*); +DEFINE_INTERCEPT(creat, int, const char*, mode_t); diff --git a/cmds/interrupter/interrupter.h b/cmds/interrupter/interrupter.h new file mode 100644 index 0000000..9ad0277 --- /dev/null +++ b/cmds/interrupter/interrupter.h @@ -0,0 +1,79 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) +#define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) +#define CONCATENATE2(arg1, arg2) arg1##arg2 + +#define INTERRUPTER(sym) \ + if (real_##sym == NULL) \ + __init_##sym(); \ + if (maybe_interrupt()) { \ + errno = EINTR; \ + return -1; \ + } + +#define CALL_FUNCTION_1(sym, ret, type1) \ +ret (*real_##sym)(type1) = NULL; \ +ret sym(type1 arg1) { \ + INTERRUPTER(sym) \ + return real_##sym(arg1); \ +} + +#define CALL_FUNCTION_2(sym, ret, type1, type2) \ +ret (*real_##sym)(type1, type2) = NULL; \ +ret sym(type1 arg1, type2 arg2) { \ + INTERRUPTER(sym) \ + return real_##sym(arg1, arg2); \ +} + +#define CALL_FUNCTION_3(sym, ret, type1, type2, type3) \ +ret (*real_##sym)(type1, type2, type3) = NULL; \ +ret sym(type1 arg1, type2 arg2, type3 arg3) { \ + INTERRUPTER(sym) \ + return real_##sym(arg1, arg2, arg3); \ +} + +#define CALL_FUNCTION_4(sym, ret, type1, type2, type3, type4) \ +ret (*real_##sym)(type1, type2, type3, type4) = NULL; \ +ret sym(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + INTERRUPTER(sym) \ + return real_##sym(arg1, arg2, arg3, arg4); \ +} + +#define CALL_FUNCTION_5(sym, ret, type1, type2, type3, type4, type5) \ +ret (*real_##sym)(type1, type2, type3, type4, type5) = NULL; \ +ret sym(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \ + INTERRUPTER(sym) \ + return real_##sym(arg1, arg2, arg3, arg4, arg5); \ +} + +#define DEFINE_INTERCEPT_N(N, sym, ret, ...) \ +static void __init_##sym(void); \ +CONCATENATE(CALL_FUNCTION_, N)(sym, ret, __VA_ARGS__) \ +static void __init_##sym(void) { \ + real_##sym = dlsym(RTLD_NEXT, #sym); \ + if (real_##sym == NULL) { \ + fprintf(stderr, "Error hooking " #sym ": %s\n", dlerror()); \ + } \ +} + +#define INTERCEPT_NARG(...) INTERCEPT_NARG_N(__VA_ARGS__, INTERCEPT_RSEQ_N()) +#define INTERCEPT_NARG_N(...) INTERCEPT_ARG_N(__VA_ARGS__) +#define INTERCEPT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N +#define INTERCEPT_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0 + +#define DEFINE_INTERCEPT(sym, ret, ...) DEFINE_INTERCEPT_N(INTERCEPT_NARG(__VA_ARGS__), sym, ret, __VA_ARGS__) diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 98c82b5..224945a 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -1218,7 +1218,8 @@ public final class Pm { ComponentName cn = ComponentName.unflattenFromString(pkg); if (cn == null) { try { - mPm.setApplicationEnabledSetting(pkg, state, 0, userId); + mPm.setApplicationEnabledSetting(pkg, state, 0, userId, + "shell:" + android.os.Process.myUid()); System.err.println("Package " + pkg + " new state: " + enabledSettingToString( mPm.getApplicationEnabledSetting(pkg, userId))); diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 313260f..241a64a 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -151,6 +151,7 @@ public class AccountManager { public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6; public static final int ERROR_CODE_BAD_ARGUMENTS = 7; public static final int ERROR_CODE_BAD_REQUEST = 8; + public static final int ERROR_CODE_BAD_AUTHENTICATION = 9; /** @hide */ public static final int ERROR_CODE_USER_RESTRICTED = 100; @@ -387,6 +388,23 @@ public class AccountManager { } /** + * @hide + * For use by internal activities. Returns the list of accounts that the calling package + * is authorized to use, particularly for shared accounts. + * @param packageName package name of the calling app. + * @param uid the uid of the calling app. + * @return the accounts that are available to this package and user. + */ + public Account[] getAccountsForPackage(String packageName, int uid) { + try { + return mService.getAccountsForPackage(packageName, uid); + } catch (RemoteException re) { + // possible security exception + throw new RuntimeException(re); + } + } + + /** * Lists all accounts of a particular type. The account type is a * string token corresponding to the authenticator and useful domain * of the account. For example, there are types corresponding to Google @@ -574,7 +592,7 @@ public class AccountManager { public boolean addAccountExplicitly(Account account, String password, Bundle userdata) { if (account == null) throw new IllegalArgumentException("account is null"); try { - return mService.addAccount(account, password, userdata); + return mService.addAccountExplicitly(account, password, userdata); } catch (RemoteException e) { // won't ever happen throw new RuntimeException(e); @@ -964,10 +982,10 @@ public class AccountManager { */ @Deprecated public AccountManagerFuture<Bundle> getAuthToken( - final Account account, final String authTokenType, + final Account account, final String authTokenType, final boolean notifyAuthFailure, AccountManagerCallback<Bundle> callback, Handler handler) { - return getAuthToken(account, authTokenType, null, notifyAuthFailure, callback, + return getAuthToken(account, authTokenType, null, notifyAuthFailure, callback, handler); } @@ -1122,7 +1140,7 @@ public class AccountManager { return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { - mService.addAcount(mResponse, accountType, authTokenType, + mService.addAccount(mResponse, accountType, authTokenType, requiredFeatures, activity != null, optionsIn); } }.start(); @@ -1913,7 +1931,7 @@ public class AccountManager { * <p> * The most common case is to call this with one account type, e.g.: * <p> - * <pre> newChooseAccountsIntent(null, null, new String[]{"com.google"}, false, null, + * <pre> newChooseAccountIntent(null, null, new String[]{"com.google"}, false, null, * null, null, null);</pre> * @param selectedAccount if specified, indicates that the {@link Account} is the currently * selected one, according to the caller's definition of selected. diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java index 5358bc7..2aba163 100644 --- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java +++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java @@ -18,9 +18,14 @@ package android.accounts; import com.google.android.collect.Sets; import android.app.Activity; +import android.app.ActivityManagerNative; import android.content.Intent; import android.os.Bundle; +import android.os.IBinder; import android.os.Parcelable; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -29,6 +34,7 @@ import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; +import android.widget.Toast; import com.android.internal.R; @@ -119,6 +125,9 @@ public class ChooseTypeAndAccountActivity extends Activity private Parcelable[] mExistingAccounts = null; private int mSelectedItemIndex; private Button mOkButton; + private int mCallingUid; + private String mCallingPackage; + private boolean mDisallowAddAccounts; @Override public void onCreate(Bundle savedInstanceState) { @@ -128,6 +137,24 @@ public class ChooseTypeAndAccountActivity extends Activity + savedInstanceState + ")"); } + String message = null; + + try { + IBinder activityToken = getActivityToken(); + mCallingUid = ActivityManagerNative.getDefault().getLaunchedFromUid(activityToken); + mCallingPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage( + activityToken); + if (mCallingUid != 0 && mCallingPackage != null) { + Bundle restrictions = UserManager.get(this) + .getUserRestrictions(new UserHandle(UserHandle.getUserId(mCallingUid))); + mDisallowAddAccounts = + restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false); + } + } catch (RemoteException re) { + // Couldn't figure out caller details + Log.w(getClass().getSimpleName(), "Unable to get caller identity \n" + re); + } + // save some items we use frequently final Intent intent = getIntent(); @@ -179,6 +206,11 @@ public class ChooseTypeAndAccountActivity extends Activity // If there are no relevant accounts and only one relevant account type go directly to // add account. Otherwise let the user choose. if (mAccounts.isEmpty()) { + if (mDisallowAddAccounts) { + setContentView(R.layout.app_not_authorized); + setTitle(R.string.error_message_title); + return; + } if (mSetOfRelevantAccountTypes.size() == 1) { runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next()); } else { @@ -296,7 +328,8 @@ public class ChooseTypeAndAccountActivity extends Activity } if (accountName == null || accountType == null) { - Account[] currentAccounts = AccountManager.get(this).getAccounts(); + Account[] currentAccounts = AccountManager.get(this).getAccountsForPackage( + mCallingPackage, mCallingUid); Set<Account> preExistingAccounts = new HashSet<Account>(); for (Parcelable accountParcel : mExistingAccounts) { preExistingAccounts.add((Account) accountParcel); @@ -347,7 +380,8 @@ public class ChooseTypeAndAccountActivity extends Activity AccountManager.KEY_INTENT); if (intent != null) { mPendingRequest = REQUEST_ADD_ACCOUNT; - mExistingAccounts = AccountManager.get(this).getAccounts(); + mExistingAccounts = AccountManager.get(this).getAccountsForPackage(mCallingPackage, + mCallingUid); intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); startActivityForResult(intent, REQUEST_ADD_ACCOUNT); return; @@ -424,12 +458,14 @@ public class ChooseTypeAndAccountActivity extends Activity private String[] getListOfDisplayableOptions(ArrayList<Account> accounts) { // List of options includes all accounts found together with "Add new account" as the // last item in the list. - String[] listItems = new String[accounts.size() + 1]; + String[] listItems = new String[accounts.size() + (mDisallowAddAccounts ? 0 : 1)]; for (int i = 0; i < accounts.size(); i++) { listItems[i] = accounts.get(i).name; } - listItems[accounts.size()] = getResources().getString( - R.string.add_account_button_label); + if (!mDisallowAddAccounts) { + listItems[accounts.size()] = getResources().getString( + R.string.add_account_button_label); + } return listItems; } @@ -439,7 +475,8 @@ public class ChooseTypeAndAccountActivity extends Activity * allowable accounts, if provided. */ private ArrayList<Account> getAcceptableAccountChoices(AccountManager accountManager) { - final Account[] accounts = accountManager.getAccounts(); + final Account[] accounts = accountManager.getAccountsForPackage(mCallingPackage, + mCallingUid); ArrayList<Account> accountsToPopulate = new ArrayList<Account>(accounts.length); for (Account account : accounts) { if (mSetOfAllowableAccounts != null diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl index 47b257d..8141813 100644 --- a/core/java/android/accounts/IAccountManager.aidl +++ b/core/java/android/accounts/IAccountManager.aidl @@ -31,10 +31,11 @@ interface IAccountManager { String getUserData(in Account account, String key); AuthenticatorDescription[] getAuthenticatorTypes(); Account[] getAccounts(String accountType); + Account[] getAccountsForPackage(String packageName, int uid); Account[] getAccountsAsUser(String accountType, int userId); void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features); void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features); - boolean addAccount(in Account account, String password, in Bundle extras); + boolean addAccountExplicitly(in Account account, String password, in Bundle extras); void removeAccount(in IAccountManagerResponse response, in Account account); void invalidateAuthToken(String accountType, String authToken); String peekAuthToken(in Account account, String authTokenType); @@ -47,7 +48,7 @@ interface IAccountManager { void getAuthToken(in IAccountManagerResponse response, in Account account, String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch, in Bundle options); - void addAcount(in IAccountManagerResponse response, String accountType, + void addAccount(in IAccountManagerResponse response, String accountType, String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch, in Bundle options); void updateCredentials(in IAccountManagerResponse response, in Account account, diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 87c2d8c..31074e2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3513,7 +3513,8 @@ public class Activity extends ContextThemeWrapper try { String resolvedType = null; if (fillInIntent != null) { - fillInIntent.setAllowFds(false); + fillInIntent.migrateExtraStreamToClipData(); + fillInIntent.prepareToLeaveProcess(); resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -3738,7 +3739,8 @@ public class Activity extends ContextThemeWrapper if (mParent == null) { int result = ActivityManager.START_RETURN_INTENT_TO_CALLER; try { - intent.setAllowFds(false); + intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); result = ActivityManagerNative.getDefault() .startActivity(mMainThread.getApplicationThread(), getBasePackageName(), intent, intent.resolveTypeIfNeeded(getContentResolver()), @@ -3808,7 +3810,8 @@ public class Activity extends ContextThemeWrapper public boolean startNextMatchingActivity(Intent intent, Bundle options) { if (mParent == null) { try { - intent.setAllowFds(false); + intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); return ActivityManagerNative.getDefault() .startNextMatchingActivity(mToken, intent, options); } catch (RemoteException e) { @@ -4162,7 +4165,7 @@ public class Activity extends ContextThemeWrapper if (false) Log.v(TAG, "Finishing self: token=" + mToken); try { if (resultData != null) { - resultData.setAllowFds(false); + resultData.prepareToLeaveProcess(); } if (ActivityManagerNative.getDefault() .finishActivity(mToken, resultCode, resultData)) { @@ -4314,7 +4317,7 @@ public class Activity extends ContextThemeWrapper int flags) { String packageName = getPackageName(); try { - data.setAllowFds(false); + data.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, @@ -4993,9 +4996,10 @@ public class Activity extends ContextThemeWrapper resultData = mResultData; } if (resultData != null) { - resultData.setAllowFds(false); + resultData.prepareToLeaveProcess(); } try { + upIntent.prepareToLeaveProcess(); return ActivityManagerNative.getDefault().navigateUpTo(mToken, upIntent, resultCode, resultData); } catch (RemoteException e) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 944a533..bb9e19f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -896,7 +896,7 @@ public class ActivityManager { * @param taskId The identifier of the task to be moved, as found in * {@link RunningTaskInfo} or {@link RecentTaskInfo}. * @param flags Additional operational flags, 0 or more of - * {@link #MOVE_TASK_WITH_HOME}. + * {@link #MOVE_TASK_WITH_HOME}, {@link #MOVE_TASK_NO_USER_ACTION}. */ public void moveTaskToFront(int taskId, int flags) { moveTaskToFront(taskId, flags, null); @@ -911,7 +911,7 @@ public class ActivityManager { * @param taskId The identifier of the task to be moved, as found in * {@link RunningTaskInfo} or {@link RecentTaskInfo}. * @param flags Additional operational flags, 0 or more of - * {@link #MOVE_TASK_WITH_HOME}. + * {@link #MOVE_TASK_WITH_HOME}, {@link #MOVE_TASK_NO_USER_ACTION}. * @param options Additional options for the operation, either null or * as per {@link Context#startActivity(Intent, android.os.Bundle) * Context.startActivity(Intent, Bundle)}. diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 6d55dd5..271494f 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1279,7 +1279,8 @@ final class ApplicationPackageManager extends PackageManager { public void setApplicationEnabledSetting(String packageName, int newState, int flags) { try { - mPM.setApplicationEnabledSetting(packageName, newState, flags, mContext.getUserId()); + mPM.setApplicationEnabledSetting(packageName, newState, flags, + mContext.getUserId(), mContext.getBasePackageName()); } catch (RemoteException e) { // Should never happen! } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 734d435..9bf8830 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -19,7 +19,7 @@ package android.app; import com.android.internal.policy.PolicyManager; import com.android.internal.util.Preconditions; -import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -319,7 +319,7 @@ class ContextImpl extends Context { registerService(BLUETOOTH_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { - return BluetoothAdapter.getDefaultAdapter(); + return new BluetoothManager(ctx); }}); registerService(CLIPBOARD_SERVICE, new ServiceFetcher() { @@ -1020,7 +1020,8 @@ class ContextImpl extends Context { try { String resolvedType = null; if (fillInIntent != null) { - fillInIntent.setAllowFds(false); + fillInIntent.migrateExtraStreamToClipData(); + fillInIntent.prepareToLeaveProcess(); resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -1040,7 +1041,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, @@ -1054,7 +1055,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, @@ -1068,7 +1069,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false, @@ -1083,7 +1084,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, true, false, @@ -1126,7 +1127,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermission, appOp, @@ -1139,7 +1140,7 @@ class ContextImpl extends Context { public void sendBroadcastAsUser(Intent intent, UserHandle user) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, user.getIdentifier()); @@ -1152,7 +1153,7 @@ class ContextImpl extends Context { String receiverPermission) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, false, false, @@ -1184,7 +1185,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermission, @@ -1198,7 +1199,7 @@ class ContextImpl extends Context { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, @@ -1232,7 +1233,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, @@ -1249,7 +1250,7 @@ class ContextImpl extends Context { intent.setDataAndType(intent.getData(), resolvedType); } try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().unbroadcastIntent( mMainThread.getApplicationThread(), intent, getUserId()); } catch (RemoteException e) { @@ -1260,7 +1261,7 @@ class ContextImpl extends Context { public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, user.getIdentifier()); @@ -1292,7 +1293,7 @@ class ContextImpl extends Context { } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, @@ -1309,7 +1310,7 @@ class ContextImpl extends Context { intent.setDataAndType(intent.getData(), resolvedType); } try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().unbroadcastIntent( mMainThread.getApplicationThread(), intent, user.getIdentifier()); } catch (RemoteException e) { @@ -1393,7 +1394,7 @@ class ContextImpl extends Context { @Override public ComponentName startServiceAsUser(Intent service, UserHandle user) { try { - service.setAllowFds(false); + service.prepareToLeaveProcess(); ComponentName cn = ActivityManagerNative.getDefault().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier()); @@ -1417,7 +1418,7 @@ class ContextImpl extends Context { @Override public boolean stopServiceAsUser(Intent service, UserHandle user) { try { - service.setAllowFds(false); + service.prepareToLeaveProcess(); int res = ActivityManagerNative.getDefault().stopService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier()); @@ -1459,7 +1460,7 @@ class ContextImpl extends Context { < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { flags |= BIND_WAIVE_PRIORITY; } - service.setAllowFds(false); + service.prepareToLeaveProcess(); int res = ActivityManagerNative.getDefault().bindService( mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index e7bf305..e0dfb25 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1410,8 +1410,8 @@ public class Instrumentation { } } try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), @@ -1467,7 +1467,8 @@ public class Instrumentation { try { String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { - intents[i].setAllowFds(false); + intents[i].migrateExtraStreamToClipData(); + intents[i].prepareToLeaveProcess(); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver()); } int result = ActivityManagerNative.getDefault() @@ -1526,8 +1527,8 @@ public class Instrumentation { } } try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), @@ -1586,8 +1587,8 @@ public class Instrumentation { } } try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); int result = ActivityManagerNative.getDefault() .startActivityAsUser(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 5e69128..dbafc78 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -21,6 +21,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.StrictMode; import android.os.UserHandle; import android.util.Log; @@ -126,6 +127,9 @@ public class NotificationManager String pkg = mContext.getPackageName(); if (notification.sound != null) { notification.sound = notification.sound.getCanonicalUri(); + if (StrictMode.vmFileUriExposureEnabled()) { + notification.sound.checkFileUriExposed("Notification.sound"); + } } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { @@ -148,6 +152,9 @@ public class NotificationManager String pkg = mContext.getPackageName(); if (notification.sound != null) { notification.sound = notification.sound.getCanonicalUri(); + if (StrictMode.vmFileUriExposureEnabled()) { + notification.sound.checkFileUriExposed("Notification.sound"); + } } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 20114cc..25c790f 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -260,8 +260,8 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY, packageName, @@ -285,8 +285,8 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.setAllowFds(false); intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_ACTIVITY, packageName, @@ -401,7 +401,8 @@ public final class PendingIntent implements Parcelable { String packageName = context.getPackageName(); String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { - intents[i].setAllowFds(false); + intents[i].migrateExtraStreamToClipData(); + intents[i].prepareToLeaveProcess(); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); } try { @@ -426,7 +427,8 @@ public final class PendingIntent implements Parcelable { String packageName = context.getPackageName(); String[] resolvedTypes = new String[intents.length]; for (int i=0; i<intents.length; i++) { - intents[i].setAllowFds(false); + intents[i].migrateExtraStreamToClipData(); + intents[i].prepareToLeaveProcess(); resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver()); } try { @@ -482,7 +484,7 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_BROADCAST, packageName, @@ -526,7 +528,7 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; try { - intent.setAllowFds(false); + intent.prepareToLeaveProcess(); IIntentSender target = ActivityManagerNative.getDefault().getIntentSender( ActivityManager.INTENT_SENDER_SERVICE, packageName, diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 02cf3aa..4fbca73 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -666,7 +666,9 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac /** * Print the Service's state into the given stream. This gets invoked if - * you run "adb shell dumpsys activity service <yourservicename>". + * you run "adb shell dumpsys activity service <yourservicename>" + * (note that for this command to work, the service must be running, and + * you must specify a fully-qualified service name). * This is distinct from "dumpsys <servicename>", which only works for * named system services and which invokes the {@link IBinder#dump} method * on the {@link IBinder} interface registered with ServiceManager. diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 6fdf3b4..6fdf3b4 100755..100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index b00bf09..2e9c9e3 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -22,7 +22,6 @@ import android.content.Context; import android.os.Binder; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.Message; import android.os.ParcelUuid; import android.os.RemoteException; @@ -359,6 +358,8 @@ public final class BluetoothAdapter { private IBluetooth mService; private Handler mServiceRecordHandler; + private BluetoothAdapterCallback mCallback; + private int mClientIf; /** * Get a handle to the default local Bluetooth adapter. @@ -1137,7 +1138,8 @@ public final class BluetoothAdapter { * Get the profile proxy object associated with the profile. * * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET}, - * or {@link BluetoothProfile#A2DP}. Clients must implement + * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or + * {@link BluetoothProfile#GATT_SERVER}. Clients must implement * {@link BluetoothProfile.ServiceListener} to get notified of * the connection status and to get the proxy object. * @@ -1166,12 +1168,6 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.HEALTH) { BluetoothHealth health = new BluetoothHealth(context, listener); return true; - } else if (profile == BluetoothProfile.GATT) { - BluetoothGatt gatt = new BluetoothGatt(context, listener); - return true; - } else if (profile == BluetoothProfile.GATT_SERVER) { - BluetoothGattServer gattServer = new BluetoothGattServer(context, listener); - return true; } else { return false; } @@ -1411,4 +1407,230 @@ public final class BluetoothAdapter { mProxyServiceStateCallbacks.remove(cb); } } + + /** + * Register an callback to receive async results, such as LE scan result. + * + * <p>This is an asynchronous call. The callback + * {@link BluetoothAdapterCallback#onCallbackRegistration} + * is used to notify success or failure if the function returns true. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callback BluetootAdapter callback handler that will receive asynchronous callbacks. + * @return If true, the callback will be called to notify success or failure, + * false on immediate error + */ + public boolean registerCallback(BluetoothAdapterCallback callback) { + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + mCallback = callback; + UUID uuid = UUID.randomUUID(); + if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); + + iGatt.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); + return true; + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + } + + /** + * Unregister the registered callback. + */ + public boolean unRegisterCallback(BluetoothAdapterCallback callback) { + if (callback != mCallback) return false; + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + + iGatt.unregisterClient(mClientIf); + return true; + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + } + + /** + * Starts a scan for Bluetooth LE devices. + * + * <p>Results of the scan are reported using the + * {@link BluetoothAdapterCallback#onLeScan} callback. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @return true, if the scan was started successfully + */ + public boolean startLeScan() { + if (DBG) Log.d(TAG, "startLeScan()"); + if (mClientIf == 0) return false; + + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + iGatt.startScan(mClientIf, false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Starts a scan for Bluetooth LE devices, looking for devices that + * advertise given services. + * + * <p>Devices which advertise all specified services are reported using the + * {@link BluetoothAdapterCallback#onLeScan} callback. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param serviceUuids Array of services to look for + * @return true, if the scan was started successfully + */ + public boolean startLeScan(UUID[] serviceUuids) { + if (DBG) Log.d(TAG, "startLeScan() - with UUIDs"); + if (mClientIf == 0) return false; + + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(serviceUuids[i]); + } + iGatt.startScanWithUuids(mClientIf, false, uuids); + } catch (RemoteException e) { + Log.e(TAG,"",e); + return false; + } + + return true; + } + + /** + * Stops an ongoing Bluetooth LE device scan. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + */ + public void stopLeScan() { + if (DBG) Log.d(TAG, "stopScan()"); + if (mClientIf == 0) return; + + try { + IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); + iGatt.stopScan(mClientIf, false); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + + /** + * Bluetooth GATT interface callbacks + */ + private final IBluetoothGattCallback mBluetoothGattCallback = + new IBluetoothGattCallback.Stub() { + /** + * Application interface registered - app is ready to go + */ + public void onClientRegistered(int status, int clientIf) { + if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + mClientIf = clientIf; + mCallback.onCallbackRegistration(status == BluetoothGatt.GATT_SUCCESS ? + BluetoothAdapterCallback.CALLBACK_REGISTERED : + BluetoothAdapterCallback.CALLBACK_REGISTRATION_FAILURE); + } + + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } + + /** + * Callback reporting an LE scan result. + * @hide + */ + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); + + try { + mCallback.onLeScan(getRemoteDevice(address), rssi, advData); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); + } + } + + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } + + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descUuid) { + // no op + } + + public void onSearchComplete(String address, int status) { + // no op + } + + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + public void onNotify(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + byte[] value) { + // no op + } + + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid, byte[] value) { + // no op + } + + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid) { + // no op + } + + public void onExecuteWrite(String address, int status) { + // no op + } + + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + }; + } diff --git a/core/java/android/bluetooth/BluetoothAdapterCallback.java b/core/java/android/bluetooth/BluetoothAdapterCallback.java new file mode 100644 index 0000000..a726bc9 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothAdapterCallback.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.bluetooth.BluetoothDevice; + +/** + * This abstract class is used to implement {@link BluetoothAdapter} callbacks. + */ +public abstract class BluetoothAdapterCallback { + + /** + * Indicates the callback has been registered successfully + */ + public static final int CALLBACK_REGISTERED = 0; + + /** + * Indicates the callback registration has failed + */ + public static final int CALLBACK_REGISTRATION_FAILURE = 1; + + /** + * Callback to inform change in registration state of the application. + * + * @param status Returns {@link #CALLBACK_REGISTERED} if the application + * was successfully registered. + */ + public void onCallbackRegistration(int status) { + } + + /** + * Callback reporting an LE device found during a device scan initiated + * by the {@link BluetoothAdapter#startLeScan} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device as reported by the + * Bluetooth hardware. 0 if no RSSI value is available. + * @param scanRecord The content of the advertisement record offered by + * the remote device. + */ + public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { + } +} diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 4cc22b4..83e95ca 100755..100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -1126,4 +1127,30 @@ public final class BluetoothDevice implements Parcelable { return pinBytes; } + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @throws IllegalArgumentException if callback is null + */ + public BluetoothGatt connectGattServer(Context context, boolean autoConnect, + BluetoothGattCallback callback) { + // TODO(Bluetooth) check whether platform support BLE + // Do the check here or in GattServer? + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + IBluetoothManager managerService = adapter.getBluetoothManager(); + try { + IBluetoothGatt iGatt = managerService.getBluetoothGatt(); + BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this); + gatt.connect(autoConnect, callback); + return gatt; + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } } diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index 1e12025..f9ce6ea 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -16,8 +16,6 @@ package android.bluetooth; - -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; @@ -39,42 +37,48 @@ import java.util.List; import java.util.UUID; /** - * Public API for the Bluetooth Gatt Profile. + * Public API for the Bluetooth GATT Profile. * - * <p>This class provides Bluetooth Gatt functionality to enable communication + * <p>This class provides Bluetooth GATT functionality to enable communication * with Bluetooth Smart or Smart Ready devices. * - * <p>BluetoothGatt is a proxy object for controlling the Bluetooth Service - * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the - * BluetoothGatt proxy object. - * * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback} - * and call {@link #registerApp} to register your application. Gatt capable - * devices can be discovered using the {@link #startScan} function or the - * regular Bluetooth device discovery process. - * @hide + * and call {@link BluetoothDevice#connectGattServer} to get a instance of this class. + * GATT capable devices can be discovered using the Bluetooth device discovery or BLE + * scan process. */ public final class BluetoothGatt implements BluetoothProfile { private static final String TAG = "BluetoothGatt"; private static final boolean DBG = true; + private static final boolean VDBG = true; - private Context mContext; - private ServiceListener mServiceListener; - private BluetoothAdapter mAdapter; + private final Context mContext; private IBluetoothGatt mService; private BluetoothGattCallback mCallback; private int mClientIf; private boolean mAuthRetry = false; + private BluetoothDevice mDevice; + private boolean mAutoConnect; + private int mConnState; + private final Object mStateLock = new Object(); + + private static final int CONN_STATE_IDLE = 0; + private static final int CONN_STATE_CONNECTING = 1; + private static final int CONN_STATE_CONNECTED = 2; + private static final int CONN_STATE_DISCONNECTING = 3; private List<BluetoothGattService> mServices; - /** A Gatt operation completed successfully */ + /** A GATT operation failed */ + public static final int GATT_FAILURE = 0; + + /** A GATT operation completed successfully */ public static final int GATT_SUCCESS = 0; - /** Gatt read operation is not permitted */ + /** GATT read operation is not permitted */ public static final int GATT_READ_NOT_PERMITTED = 0x2; - /** Gatt write operation is not permitted */ + /** GATT write operation is not permitted */ public static final int GATT_WRITE_NOT_PERMITTED = 0x3; /** Insufficient authentication for a given operation */ @@ -111,55 +115,6 @@ public final class BluetoothGatt implements BluetoothProfile { /*package*/ static final int AUTHENTICATION_MITM = 2; /** - * Bluetooth state change handlers - */ - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (DBG) Log.d(TAG,"Unbinding service..."); - synchronized (mConnection) { - mService = null; - mContext.unbindService(mConnection); - } - } else { - synchronized (mConnection) { - if (mService == null) { - if (DBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService(new Intent(IBluetoothGatt.class.getName()), - mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth GATT Service"); - } - } - } - } - } - }; - - /** - * Service binder handling - */ - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothGatt.Stub.asInterface(service); - ServiceListener serviceListener = mServiceListener; - if (serviceListener != null) { - serviceListener.onServiceConnected(BluetoothProfile.GATT, BluetoothGatt.this); - } - } - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - ServiceListener serviceListener = mServiceListener; - if (serviceListener != null) { - serviceListener.onServiceDisconnected(BluetoothProfile.GATT); - } - } - }; - - /** * Bluetooth GATT interface callbacks */ private final IBluetoothGattCallback mBluetoothGattCallback = @@ -171,11 +126,27 @@ public final class BluetoothGatt implements BluetoothProfile { public void onClientRegistered(int status, int clientIf) { if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf); + if (VDBG) { + synchronized(mStateLock) { + if (mConnState != CONN_STATE_CONNECTING) { + Log.e(TAG, "Bad connection state: " + mConnState); + } + } + } mClientIf = clientIf; + if (status != GATT_SUCCESS) { + mCallback.onConnectionStateChange(mDevice, GATT_FAILURE, + BluetoothProfile.STATE_DISCONNECTED); + synchronized(mStateLock) { + mConnState = CONN_STATE_IDLE; + } + return; + } try { - mCallback.onAppRegistered(status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + mService.clientConnect(mClientIf, mDevice.getAddress(), + !mAutoConnect); // autoConnect is inverse of "isDirect" + } catch (RemoteException e) { + Log.e(TAG,"",e); } } @@ -187,13 +158,24 @@ public final class BluetoothGatt implements BluetoothProfile { boolean connected, String address) { if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status + " clientIf=" + clientIf + " device=" + address); + if (!address.equals(mDevice.getAddress())) { + return; + } + int profileState = connected ? BluetoothProfile.STATE_CONNECTED : + BluetoothProfile.STATE_DISCONNECTED; try { - mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status, - connected ? BluetoothProfile.STATE_CONNECTED - : BluetoothProfile.STATE_DISCONNECTED); + mCallback.onConnectionStateChange(mDevice, status, profileState); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } + + synchronized(mStateLock) { + if (connected) { + mConnState = CONN_STATE_CONNECTED; + } else { + mConnState = CONN_STATE_IDLE; + } + } } /** @@ -201,13 +183,7 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - - try { - mCallback.onScanResult(mAdapter.getRemoteDevice(address), rssi, advData); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } + // no op } /** @@ -219,8 +195,10 @@ public final class BluetoothGatt implements BluetoothProfile { public void onGetService(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid) { if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - mServices.add(new BluetoothGattService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType)); } @@ -236,10 +214,12 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); - BluetoothGattService includedService = getService(device, + BluetoothGattService includedService = getService(mDevice, inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType); if (service != null && includedService != null) { @@ -260,8 +240,10 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" + charUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service != null) { service.addCharacteristic(new BluetoothGattCharacteristic( @@ -281,8 +263,10 @@ public final class BluetoothGatt implements BluetoothProfile { ParcelUuid descUuid) { if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -303,9 +287,11 @@ public final class BluetoothGatt implements BluetoothProfile { */ public void onSearchComplete(String address, int status) { if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); - BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (!address.equals(mDevice.getAddress())) { + return; + } try { - mCallback.onServicesDiscovered(device, status); + mCallback.onServicesDiscovered(mDevice, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -322,6 +308,9 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address + " UUID=" + charUuid + " Status=" + status); + if (!address.equals(mDevice.getAddress())) { + return; + } if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) && mAuthRetry == false) { @@ -338,8 +327,7 @@ public final class BluetoothGatt implements BluetoothProfile { mAuthRetry = false; - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -367,8 +355,10 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address + " UUID=" + charUuid + " Status=" + status); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -411,8 +401,10 @@ public final class BluetoothGatt implements BluetoothProfile { byte[] value) { if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -439,8 +431,10 @@ public final class BluetoothGatt implements BluetoothProfile { ParcelUuid descrUuid, byte[] value) { if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -486,8 +480,10 @@ public final class BluetoothGatt implements BluetoothProfile { ParcelUuid descrUuid) { if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid); - BluetoothDevice device = mAdapter.getRemoteDevice(address); - BluetoothGattService service = getService(device, srvcUuid.getUuid(), + if (!address.equals(mDevice.getAddress())) { + return; + } + BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(), srvcInstId, srvcType); if (service == null) return; @@ -529,9 +525,11 @@ public final class BluetoothGatt implements BluetoothProfile { public void onExecuteWrite(String address, int status) { if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address + " status=" + status); - BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (!address.equals(mDevice.getAddress())) { + return; + } try { - mCallback.onReliableWriteCompleted(device, status); + mCallback.onReliableWriteCompleted(mDevice, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } @@ -544,43 +542,24 @@ public final class BluetoothGatt implements BluetoothProfile { public void onReadRemoteRssi(String address, int rssi, int status) { if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address + " rssi=" + rssi + " status=" + status); - BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (!address.equals(mDevice.getAddress())) { + return; + } try { - mCallback.onReadRemoteRssi(device, rssi, status); + mCallback.onReadRemoteRssi(mDevice, rssi, status); } catch (Exception ex) { Log.w(TAG, "Unhandled exception: " + ex); } } }; - /** - * Create a BluetoothGatt proxy object. - */ - /*package*/ BluetoothGatt(Context context, ServiceListener l) { + /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { mContext = context; - mServiceListener = l; - mAdapter = BluetoothAdapter.getDefaultAdapter(); + mService = iGatt; + mDevice = device; mServices = new ArrayList<BluetoothGattService>(); - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re); - } - } else { - Log.e(TAG, "Unable to get BluetoothManager interface."); - throw new RuntimeException("BluetoothManager inactive"); - } - - //Bind to the service only if the Bluetooth is ON - if(mAdapter.isEnabled()){ - if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Gatt Service"); - } - } + mConnState = CONN_STATE_IDLE; } /** @@ -590,24 +569,6 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "close()"); unregisterApp(); - mServiceListener = null; - - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re); - } - } - - synchronized (mConnection) { - if (mService != null) { - mService = null; - mContext.unbindService(mConnection); - } - } } /** @@ -629,18 +590,18 @@ public final class BluetoothGatt implements BluetoothProfile { /** - * Register an application callback to start using Gatt. + * Register an application callback to start using GATT. * - * <p>This is an asynchronous call. The callback is used to notify - * success or failure if the function returns true. + * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered} + * is used to notify success or failure if the function returns true. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param callback Gatt callback handler that will receive asynchronous - * callbacks. - * @return true, if application was successfully registered. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @return If true, the callback will be called to notify success or failure, + * false on immediate error */ - public boolean registerApp(BluetoothGattCallback callback) { + private boolean registerApp(BluetoothGattCallback callback) { if (DBG) Log.d(TAG, "registerApp()"); if (mService == null) return false; @@ -661,7 +622,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * Unregister the current application and callbacks. */ - public void unregisterApp() { + private void unregisterApp() { if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); if (mService == null || mClientIf == 0) return; @@ -675,77 +636,7 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Starts a scan for Bluetooth LE devices. - * - * <p>Results of the scan are reported using the - * {@link BluetoothGattCallback#onScanResult} callback. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return true, if the scan was started successfully - */ - public boolean startScan() { - if (DBG) Log.d(TAG, "startScan()"); - if (mService == null || mClientIf == 0) return false; - - try { - mService.startScan(mClientIf, false); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; - } - - /** - * Starts a scan for Bluetooth LE devices, looking for devices that - * advertise given services. - * - * <p>Devices which advertise all specified services are reported using the - * {@link BluetoothGattCallback#onScanResult} callback. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param serviceUuids Array of services to look for - * @return true, if the scan was started successfully - */ - public boolean startScan(UUID[] serviceUuids) { - if (DBG) Log.d(TAG, "startScan() - with UUIDs"); - if (mService == null || mClientIf == 0) return false; - - try { - ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(serviceUuids[i]); - } - mService.startScanWithUuids(mClientIf, false, uuids); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; - } - - /** - * Stops an ongoing Bluetooth LE device scan. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - */ - public void stopScan() { - if (DBG) Log.d(TAG, "stopScan()"); - if (mService == null || mClientIf == 0) return; - - try { - mService.stopScan(mClientIf, false); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - } - - /** - * Initiate a connection to a Bluetooth Gatt capable device. + * Initiate a connection to a Bluetooth GATT capable device. * * <p>The connection may not be established right away, but will be * completed when the remote device is available. A @@ -757,7 +648,7 @@ public final class BluetoothGatt implements BluetoothProfile { * when the remote device is in range/available. Generally, the first ever * connection to a device should be direct (autoConnect set to false) and * subsequent connections to known devices should be invoked with the - * autoConnect parameter set to false. + * autoConnect parameter set to true. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * @@ -767,18 +658,24 @@ public final class BluetoothGatt implements BluetoothProfile { * device becomes available (true). * @return true, if the connection attempt was initiated successfully */ - public boolean connect(BluetoothDevice device, boolean autoConnect) { - if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect); - if (mService == null || mClientIf == 0) return false; - - try { - mService.clientConnect(mClientIf, device.getAddress(), - autoConnect ? false : true); // autoConnect is inverse of "isDirect" - } catch (RemoteException e) { - Log.e(TAG,"",e); + /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) { + if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect); + synchronized(mStateLock) { + if (mConnState != CONN_STATE_IDLE) { + throw new IllegalStateException("Not idle"); + } + mConnState = CONN_STATE_CONNECTING; + } + if (!registerApp(callback)) { + synchronized(mStateLock) { + mConnState = CONN_STATE_IDLE; + } + Log.e(TAG, "Failed to register callback"); return false; } + // the connection will continue after successful callback registration + mAutoConnect = autoConnect; return true; } @@ -787,18 +684,17 @@ public final class BluetoothGatt implements BluetoothProfile { * currently in progress. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device Remote device */ - public void cancelConnection(BluetoothDevice device) { - if (DBG) Log.d(TAG, "cancelOpen() - device: " + device.getAddress()); + public void disconnect() { + if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return; try { - mService.clientDisconnect(mClientIf, device.getAddress()); + mService.clientDisconnect(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); } + // TBD deregister after conneciton is torn down } /** @@ -812,17 +708,16 @@ public final class BluetoothGatt implements BluetoothProfile { * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device to explore * @return true, if the remote service discovery has been started */ - public boolean discoverServices(BluetoothDevice device) { - if (DBG) Log.d(TAG, "discoverServices() - device: " + device.getAddress()); + public boolean discoverServices() { + if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; mServices.clear(); try { - mService.discoverServices(mClientIf, device.getAddress()); + mService.discoverServices(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -839,16 +734,15 @@ public final class BluetoothGatt implements BluetoothProfile { * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @return List of services on the remote device. Returns an empty list * if service discovery has not yet been performed. */ - public List<BluetoothGattService> getServices(BluetoothDevice device) { + public List<BluetoothGattService> getServices() { List<BluetoothGattService> result = new ArrayList<BluetoothGattService>(); for (BluetoothGattService service : mServices) { - if (service.getDevice().equals(device)) { + if (service.getDevice().equals(mDevice)) { result.add(service); } } @@ -868,14 +762,13 @@ public final class BluetoothGatt implements BluetoothProfile { * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @param uuid UUID of the requested service * @return BluetoothGattService if supported, or null if the requested * service is not offered by the remote device. */ - public BluetoothGattService getService(BluetoothDevice device, UUID uuid) { + public BluetoothGattService getService(UUID uuid) { for (BluetoothGattService service : mServices) { - if (service.getDevice().equals(device) && + if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) { return service; } @@ -923,8 +816,7 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Writes a given characteristic and it's values to the associated remote - * device. + * Writes a given characteristic and its values to the associated remote device. * * <p>Once the write operation has been completed, the * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked, @@ -1061,15 +953,14 @@ public final class BluetoothGatt implements BluetoothProfile { * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @return true, if the reliable write transaction has been initiated */ - public boolean beginReliableWrite(BluetoothDevice device) { - if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + device.getAddress()); + public boolean beginReliableWrite() { + if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; try { - mService.beginReliableWrite(mClientIf, device.getAddress()); + mService.beginReliableWrite(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -1089,15 +980,14 @@ public final class BluetoothGatt implements BluetoothProfile { * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @return true, if the request to execute the transaction has been sent */ - public boolean executeReliableWrite(BluetoothDevice device) { - if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + device.getAddress()); + public boolean executeReliableWrite() { + if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; try { - mService.endReliableWrite(mClientIf, device.getAddress(), true); + mService.endReliableWrite(mClientIf, mDevice.getAddress(), true); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -1113,15 +1003,13 @@ public final class BluetoothGatt implements BluetoothProfile { * operations for a given remote device. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device Remote device */ - public void abortReliableWrite(BluetoothDevice device) { - if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + device.getAddress()); + public void abortReliableWrite(BluetoothDevice mDevice) { + if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return; try { - mService.endReliableWrite(mClientIf, device.getAddress(), false); + mService.endReliableWrite(mClientIf, mDevice.getAddress(), false); } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -1172,12 +1060,12 @@ public final class BluetoothGatt implements BluetoothProfile { * remote device. * @hide */ - public boolean refresh(BluetoothDevice device) { - if (DBG) Log.d(TAG, "refresh() - device: " + device.getAddress()); + public boolean refresh() { + if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; try { - mService.refreshDevice(mClientIf, device.getAddress()); + mService.refreshDevice(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -1194,15 +1082,14 @@ public final class BluetoothGatt implements BluetoothProfile { * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device * @return true, if the RSSI value has been requested successfully */ - public boolean readRemoteRssi(BluetoothDevice device) { - if (DBG) Log.d(TAG, "readRssi() - device: " + device.getAddress()); + public boolean readRemoteRssi() { + if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress()); if (mService == null || mClientIf == 0) return false; try { - mService.readRemoteRssi(mClientIf, device.getAddress()); + mService.readRemoteRssi(mClientIf, mDevice.getAddress()); } catch (RemoteException e) { Log.e(TAG,"",e); return false; @@ -1212,98 +1099,38 @@ public final class BluetoothGatt implements BluetoothProfile { } /** - * Get the current connection state of the profile. - * - * <p>This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * with {@link BluetoothProfile#GATT} as argument * - * @param device Remote bluetooth device. - * @return State of the profile connection. One of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + * @throws UnsupportedOperationException */ @Override public int getConnectionState(BluetoothDevice device) { - if (DBG) Log.d(TAG,"getConnectionState()"); - if (mService == null) return STATE_DISCONNECTED; - - List<BluetoothDevice> connectedDevices = getConnectedDevices(); - for(BluetoothDevice connectedDevice : connectedDevices) { - if (device.equals(connectedDevice)) { - return STATE_CONNECTED; - } - } - - return STATE_DISCONNECTED; + throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); } /** - * Get connected devices for the Gatt profile. - * - * <p> Return the set of devices which are in state {@link #STATE_CONNECTED} - * - * <p>This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * with {@link BluetoothProfile#GATT} as argument * - * @return List of devices. The list will be empty on error. + * @throws UnsupportedOperationException */ @Override public List<BluetoothDevice> getConnectedDevices() { - if (DBG) Log.d(TAG,"getConnectedDevices"); - - List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>(); - if (mService == null) return connectedDevices; - - try { - connectedDevices = mService.getDevicesMatchingConnectionStates( - new int[] { BluetoothProfile.STATE_CONNECTED }); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - - return connectedDevices; + throw new UnsupportedOperationException + ("Use BluetoothManager#getConnectedDevices instead."); } /** - * Get a list of devices that match any of the given connection - * states. - * - * <p> If none of the devices match any of the given states, - * an empty list will be returned. - * - * <p>This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. + * Not supported - please use + * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} + * with {@link BluetoothProfile#GATT} as first argument * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param states Array of states. States can be one of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, - * @return List of devices. The list will be empty on error. + * @throws UnsupportedOperationException */ @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); - - List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); - if (mService == null) return devices; - - try { - devices = mService.getDevicesMatchingConnectionStates(states); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - - return devices; + throw new UnsupportedOperationException + ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); } } diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java index afa4539..c9e5fea 100644 --- a/core/java/android/bluetooth/BluetoothGattCallback.java +++ b/core/java/android/bluetooth/BluetoothGattCallback.java @@ -18,34 +18,10 @@ package android.bluetooth; import android.bluetooth.BluetoothDevice; -import android.util.Log; - /** * This abstract class is used to implement {@link BluetoothGatt} callbacks. - * @hide */ public abstract class BluetoothGattCallback { - /** - * Callback to inform change in registration state of the application. - * - * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application - * was successfully registered. - */ - public void onAppRegistered(int status) { - } - - /** - * Callback reporting an LE device found during a device scan initiated - * by the {@link BluetoothGatt#startScan} function. - * - * @param device Identifies the remote device - * @param rssi The RSSI value for the remote device as reported by the - * Bluetooth hardware. 0 if no RSSI value is available. - * @param scanRecord The content of the advertisement record offered by - * the remote device. - */ - public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) { - } /** * Callback indicating when a remote device has been connected or disconnected. @@ -61,8 +37,8 @@ public abstract class BluetoothGattCallback { } /** - * Callback invoked when the list of remote services, characteristics and - * descriptors for the remote device have been updated. + * Callback invoked when the list of remote services, characteristics and descriptors + * for the remote device have been updated, ie new services have been discovered. * * @param device Remote device * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device @@ -97,7 +73,7 @@ public abstract class BluetoothGattCallback { * @param status The result of the write operation */ public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic, - int status) { + int status) { } /** @@ -113,23 +89,21 @@ public abstract class BluetoothGattCallback { * Callback reporting the result of a descriptor read operation. * * @param descriptor Descriptor that was read from the associated - * remote device. + * remote device. * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation * was completed successfully */ - public void onDescriptorRead(BluetoothGattDescriptor descriptor, - int status) { + public void onDescriptorRead(BluetoothGattDescriptor descriptor, int status) { } /** * Callback indicating the result of a descriptor write operation. * * @param descriptor Descriptor that was writte to the associated - * remote device. + * remote device. * @param status The result of the write operation */ - public void onDescriptorWrite(BluetoothGattDescriptor descriptor, - int status) { + public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) { } /** @@ -150,7 +124,7 @@ public abstract class BluetoothGattCallback { * * @param device Identifies the remote device * @param rssi The RSSI value for the remote device - * @param status 0 if the RSSI was read successfully + * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully */ public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) { } diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java index f44dc5c..d63d97e 100644 --- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -21,8 +21,7 @@ import java.util.List; import java.util.UUID; /** - * Represents a Bluetooth Gatt Characteristic - * @hide + * Represents a Bluetooth GATT Characteristic */ public class BluetoothGattCharacteristic { @@ -119,7 +118,7 @@ public class BluetoothGattCharacteristic { public static final int WRITE_TYPE_NO_RESPONSE = 0x01; /** - * Write characteristic including and authenticated signature + * Write characteristic including authentication signature */ public static final int WRITE_TYPE_SIGNED = 0x04; @@ -219,12 +218,30 @@ public class BluetoothGattCharacteristic { protected List<BluetoothGattDescriptor> mDescriptors; /** + * Create a new BluetoothGattCharacteristic. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this characteristic + * @param properties Properties of this characteristic + * @param permissions Permissions for this characteristic + */ + public BluetoothGattCharacteristic(UUID uuid, int properties, int permissions) { + initCharacteristic(null, uuid, 0, properties, permissions); + } + + /** * Create a new BluetoothGattCharacteristic * @hide */ /*package*/ BluetoothGattCharacteristic(BluetoothGattService service, UUID uuid, int instanceId, int properties, int permissions) { + initCharacteristic(service, uuid, instanceId, properties, permissions); + } + + private void initCharacteristic(BluetoothGattService service, + UUID uuid, int instanceId, + int properties, int permissions) { mUuid = uuid; mInstance = instanceId; mProperties = properties; @@ -249,11 +266,16 @@ public class BluetoothGattCharacteristic { } /** - * Add a descriptor to this characteristic - * @hide + * Adds a descriptor to this characteristic. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param descriptor Descriptor to be added to this characteristic. + * @return true, if the descriptor was added to the characteristic */ - /*package*/ void addDescriptor(BluetoothGattDescriptor descriptor) { + public boolean addDescriptor(BluetoothGattDescriptor descriptor) { mDescriptors.add(descriptor); + descriptor.setCharacteristic(this); + return true; } /** @@ -265,8 +287,15 @@ public class BluetoothGattCharacteristic { } /** + * Sets the service associated with this device. + * @hide + */ + /*package*/ void setService(BluetoothGattService service) { + mService = service; + } + + /** * Returns the UUID of this characteristic - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return UUID of this characteristic */ @@ -280,8 +309,6 @@ public class BluetoothGattCharacteristic { * <p>If a remote device offers multiple characteristics with the same UUID, * the instance ID is used to distuinguish between characteristics. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Instance ID of this characteristic */ public int getInstanceId() { @@ -294,8 +321,6 @@ public class BluetoothGattCharacteristic { * <p>The properties contain a bit mask of property flags indicating * the features of this characteristic. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Properties of this characteristic */ public int getProperties() { @@ -304,7 +329,6 @@ public class BluetoothGattCharacteristic { /** * Returns the permissions for this characteristic. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Permissions of this characteristic */ @@ -314,7 +338,6 @@ public class BluetoothGattCharacteristic { /** * Gets the write type for this characteristic. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Write type for this characteristic */ @@ -329,11 +352,6 @@ public class BluetoothGattCharacteristic { * {@link BluetoothGatt#writeCharacteristic} function write this * characteristic. * - * <p>The default write type for a characteristic is - * {@link #WRITE_TYPE_DEFAULT}. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param writeType The write type to for this characteristic. Can be one * of: * {@link #WRITE_TYPE_DEFAULT}, @@ -345,8 +363,15 @@ public class BluetoothGattCharacteristic { } /** + * Set the desired key size. + * @hide + */ + public void setKeySize(int keySize) { + mKeySize = keySize; + } + + /** * Returns a list of descriptors for this characteristic. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Descriptors for this characteristic */ @@ -358,9 +383,7 @@ public class BluetoothGattCharacteristic { * Returns a descriptor with a given UUID out of the list of * descriptors for this characteristic. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return Gatt descriptor object or null if no descriptor with the + * @return GATT descriptor object or null if no descriptor with the * given UUID was found. */ public BluetoothGattDescriptor getDescriptor(UUID uuid) { @@ -376,12 +399,10 @@ public class BluetoothGattCharacteristic { * Get the stored value for this characteristic. * * <p>This function returns the stored value for this characteristic as - * retrieved by calling {@link BluetoothGatt#readCharacteristic}. To cached + * retrieved by calling {@link BluetoothGatt#readCharacteristic}. The cached * value of the characteristic is updated as a result of a read characteristic * operation or if a characteristic update notification has been received. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Cached value of the characteristic */ public byte[] getValue() { @@ -397,8 +418,6 @@ public class BluetoothGattCharacteristic { * characteristic value at the given offset are interpreted to generate the * return value. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param formatType The format type used to interpret the characteristic * value. * @param offset Offset at which the integer value can be found. @@ -436,7 +455,6 @@ public class BluetoothGattCharacteristic { /** * Return the stored value of this characteristic. * <p>See {@link #getValue} for details. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param formatType The format type used to interpret the characteristic * value. @@ -462,7 +480,7 @@ public class BluetoothGattCharacteristic { /** * Return the stored value of this characteristic. * <p>See {@link #getValue} for details. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * * @param offset Offset at which the string value can be found. * @return Cached value of the characteristic */ @@ -481,8 +499,6 @@ public class BluetoothGattCharacteristic { * {@link BluetoothGatt#writeCharacteristic} to send the value to the * remote device. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param value New value for this characteristic * @return true if the locally stored value has been set, false if the * requested value could not be stored locally. @@ -495,7 +511,6 @@ public class BluetoothGattCharacteristic { /** * Set the locally stored value of this characteristic. * <p>See {@link #setValue(byte[])} for details. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param value New value for this characteristic * @param formatType Integer format type used to transform the value parameter @@ -542,7 +557,7 @@ public class BluetoothGattCharacteristic { /** * Set the locally stored value of this characteristic. * <p>See {@link #setValue(byte[])} for details. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * * @param mantissa Mantissa for this characteristic * @param exponent exponent value for this characteristic * @param formatType Float format type used to transform the value parameter @@ -582,7 +597,7 @@ public class BluetoothGattCharacteristic { /** * Set the locally stored value of this characteristic. * <p>See {@link #setValue(byte[])} for details. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * * @param value New value for this characteristic * @return true if the locally stored value has been set */ @@ -593,7 +608,6 @@ public class BluetoothGattCharacteristic { /** * Returns the size of a give value type. - * @hide */ private int getTypeLen(int formatType) { return formatType & 0xF; @@ -601,7 +615,6 @@ public class BluetoothGattCharacteristic { /** * Convert a signed byte to an unsigned int. - * @hide */ private int unsignedByteToInt(byte b) { return b & 0xFF; @@ -609,7 +622,6 @@ public class BluetoothGattCharacteristic { /** * Convert signed bytes to a 16-bit unsigned int. - * @hide */ private int unsignedBytesToInt(byte b0, byte b1) { return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)); @@ -617,7 +629,6 @@ public class BluetoothGattCharacteristic { /** * Convert signed bytes to a 32-bit unsigned int. - * @hide */ private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) { return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8)) @@ -626,7 +637,6 @@ public class BluetoothGattCharacteristic { /** * Convert signed bytes to a 16-bit short float value. - * @hide */ private float bytesToFloat(byte b0, byte b1) { int mantissa = unsignedToSigned(unsignedByteToInt(b0) @@ -637,7 +647,6 @@ public class BluetoothGattCharacteristic { /** * Convert signed bytes to a 32-bit short float value. - * @hide */ private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) { int mantissa = unsignedToSigned(unsignedByteToInt(b0) @@ -649,7 +658,6 @@ public class BluetoothGattCharacteristic { /** * Convert an unsigned integer value to a two's-complement encoded * signed value. - * @hide */ private int unsignedToSigned(int unsigned, int size) { if ((unsigned & (1 << size-1)) != 0) { @@ -660,7 +668,6 @@ public class BluetoothGattCharacteristic { /** * Convert an integer into the signed bits of a given length. - * @hide */ private int intToSignedBits(int i, int size) { if (i < 0) { diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java index ba1f28a..6ba2db7 100644 --- a/core/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java @@ -19,8 +19,7 @@ package android.bluetooth; import java.util.UUID; /** - * Represents a Bluetooth Gatt Descriptor - * @hide + * Represents a Bluetooth GATT Descriptor */ public class BluetoothGattDescriptor { @@ -109,12 +108,28 @@ public class BluetoothGattDescriptor { * Create a new BluetoothGattDescriptor. * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * + * @param uuid The UUID for this descriptor + * @param permissions Permissions for this descriptor + */ + public BluetoothGattDescriptor(UUID uuid, int permissions) { + initDescriptor(null, uuid, permissions); + } + + /** + * Create a new BluetoothGattDescriptor. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * * @param characteristic The characteristic this descriptor belongs to * @param uuid The UUID for this descriptor * @param permissions Permissions for this descriptor */ /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, int permissions) { + initDescriptor(characteristic, uuid, permissions); + } + + private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid, + int permissions) { mCharacteristic = characteristic; mUuid = uuid; mPermissions = permissions; @@ -129,8 +144,15 @@ public class BluetoothGattDescriptor { } /** + * Set the back-reference to the associated characteristic + * @hide + */ + /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { + mCharacteristic = characteristic; + } + + /** * Returns the UUID of this descriptor. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return UUID of this descriptor */ @@ -140,7 +162,6 @@ public class BluetoothGattDescriptor { /** * Returns the permissions for this descriptor. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Permissions of this descriptor */ @@ -152,12 +173,10 @@ public class BluetoothGattDescriptor { * Returns the stored value for this descriptor * * <p>This function returns the stored value for this descriptor as - * retrieved by calling {@link BluetoothGatt#readDescriptor}. To cached + * retrieved by calling {@link BluetoothGatt#readDescriptor}. The cached * value of the descriptor is updated as a result of a descriptor read * operation. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Cached value of the descriptor */ public byte[] getValue() { @@ -172,8 +191,6 @@ public class BluetoothGattDescriptor { * {@link BluetoothGatt#writeDescriptor} to send the value to the * remote device. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @param value New value for this descriptor * @return true if the locally stored value has been set, false if the * requested value could not be stored locally. diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index 6b69377..644c619 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -38,88 +38,30 @@ import java.util.List; import java.util.UUID; /** - * Public API for the Bluetooth Gatt Profile server role. + * Public API for the Bluetooth GATT Profile server role. * - * <p>This class provides Bluetooth Gatt server role functionality, + * <p>This class provides Bluetooth GATT server role functionality, * allowing applications to create and advertise Bluetooth Smart services * and characteristics. * * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service * via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the * BluetoothGatt proxy object. - * @hide */ public final class BluetoothGattServer implements BluetoothProfile { private static final String TAG = "BluetoothGattServer"; private static final boolean DBG = true; - private Context mContext; - private ServiceListener mServiceListener; + private final Context mContext; private BluetoothAdapter mAdapter; private IBluetoothGatt mService; private BluetoothGattServerCallback mCallback; - private int mServerIf; + private Object mServerIfLock = new Object(); + private int mServerIf; private List<BluetoothGattService> mServices; - /** - * Bluetooth state change handlers - */ - private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = - new IBluetoothStateChangeCallback.Stub() { - public void onBluetoothStateChange(boolean up) { - if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); - if (!up) { - if (DBG) 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 (DBG) Log.d(TAG,"Binding service..."); - if (!mContext.bindService(new - Intent(IBluetoothGatt.class.getName()), - mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth GATT Service"); - } - } - } catch (Exception re) { - Log.e(TAG,"",re); - } - } - } - } - }; - - /** - * Service binder handling - */ - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "Proxy object connected"); - mService = IBluetoothGatt.Stub.asInterface(service); - ServiceListener serviceListner = mServiceListener; - if (serviceListner != null) { - serviceListner.onServiceConnected(BluetoothProfile.GATT_SERVER, - BluetoothGattServer.this); - } - } - public void onServiceDisconnected(ComponentName className) { - if (DBG) Log.d(TAG, "Proxy object disconnected"); - mService = null; - ServiceListener serviceListner = mServiceListener; - if (serviceListner != null) { - serviceListner.onServiceDisconnected(BluetoothProfile.GATT_SERVER); - } - } - }; + private static final int CALLBACK_REG_TIMEOUT = 10000; /** * Bluetooth GATT interface callbacks @@ -133,11 +75,14 @@ public final class BluetoothGattServer implements BluetoothProfile { public void onServerRegistered(int status, int serverIf) { if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status + " serverIf=" + serverIf); - mServerIf = serverIf; - try { - mCallback.onAppRegistered(status); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + synchronized(mServerIfLock) { + if (mCallback != null) { + mServerIf = serverIf; + mServerIfLock.notify(); + } else { + // registration timeout + Log.e(TAG, "onServerRegistered: mCallback is null"); + } } } @@ -147,13 +92,7 @@ public final class BluetoothGattServer implements BluetoothProfile { */ public void onScanResult(String address, int rssi, byte[] advData) { if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - - try { - mCallback.onScanResult(mAdapter.getRemoteDevice(address), - rssi, advData); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); - } + // no op } /** @@ -209,8 +148,7 @@ public final class BluetoothGattServer implements BluetoothProfile { BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType); if (service == null) return; - BluetoothGattCharacteristic characteristic = service.getCharacteristic( - charUuid); + BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid); if (characteristic == null) return; try { @@ -340,31 +278,13 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Create a BluetoothGattServer proxy object. */ - /*package*/ BluetoothGattServer(Context context, ServiceListener l) { + /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) { mContext = context; - mServiceListener = l; + mService = iGatt; mAdapter = BluetoothAdapter.getDefaultAdapter(); + mCallback = null; + mServerIf = 0; mServices = new ArrayList<BluetoothGattService>(); - - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); - try { - mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re); - } - } else { - Log.e(TAG, "Unable to get BluetoothManager interface."); - throw new RuntimeException("BluetoothManager inactive"); - } - - //Bind to the service only if the Bluetooth is ON - if(mAdapter.isEnabled()){ - if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) { - Log.e(TAG, "Could not bind to Bluetooth Gatt Service"); - } - } } /** @@ -372,82 +292,67 @@ public final class BluetoothGattServer implements BluetoothProfile { */ /*package*/ void close() { if (DBG) Log.d(TAG, "close()"); - - unregisterApp(); - mServiceListener = null; - - IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE); - if (b != null) { - IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b); - try { - mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); - } catch (RemoteException re) { - Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re); - } - } - - synchronized (mConnection) { - if (mService != null) { - try { - mService = null; - mContext.unbindService(mConnection); - } catch (Exception re) { - Log.e(TAG,"",re); - } - } - } - } - - /** - * Returns a service by UUID, instance and type. - * @hide - */ - /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { - for(BluetoothGattService svc : mServices) { - if (svc.getType() == type && - svc.getInstanceId() == instanceId && - svc.getUuid().equals(uuid)) { - return svc; - } - } - return null; + unregisterCallback(); } /** - * Register an application callback to start using Gatt. + * Register an application callback to start using GattServer. * * <p>This is an asynchronous call. The callback is used to notify * success or failure if the function returns true. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param callback Gatt callback handler that will receive asynchronous - * callbacks. - * @return true, if application was successfully registered. + * @param callback GATT callback handler that will receive asynchronous + * callbacks. + * @return true, the callback will be called to notify success or failure, + * false on immediate error */ - public boolean registerApp(BluetoothGattServerCallback callback) { - if (DBG) Log.d(TAG, "registerApp()"); - if (mService == null) return false; - - mCallback = callback; - UUID uuid = UUID.randomUUID(); - if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); - - try { - mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); - } catch (RemoteException e) { - Log.e(TAG,"",e); + /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) { + if (DBG) Log.d(TAG, "registerCallback()"); + if (mService == null) { + Log.e(TAG, "GATT service not available"); return false; } + UUID uuid = UUID.randomUUID(); + if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); - return true; + synchronized(mServerIfLock) { + if (mCallback != null) { + Log.e(TAG, "App can register callback only once"); + return false; + } + + mCallback = callback; + try { + mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback); + } catch (RemoteException e) { + Log.e(TAG,"",e); + mCallback = null; + return false; + } + + try { + mServerIfLock.wait(CALLBACK_REG_TIMEOUT); + } catch (InterruptedException e) { + Log.e(TAG, "" + e); + mCallback = null; + } + + if (mServerIf == 0) { + mCallback = null; + return false; + } else { + return true; + } + } } /** * Unregister the current application and callbacks. */ - public void unregisterApp() { - if (DBG) Log.d(TAG, "unregisterApp() - mServerIf=" + mServerIf); + private void unregisterCallback() { + if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf); if (mService == null || mServerIf == 0) return; try { @@ -460,77 +365,22 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Starts a scan for Bluetooth LE devices. - * - * <p>Results of the scan are reported using the - * {@link BluetoothGattServerCallback#onScanResult} callback. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return true, if the scan was started successfully - */ - public boolean startScan() { - if (DBG) Log.d(TAG, "startScan()"); - if (mService == null || mServerIf == 0) return false; - - try { - mService.startScan(mServerIf, true); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; - } - - /** - * Starts a scan for Bluetooth LE devices, looking for devices that - * advertise given services. - * - * <p>Devices which advertise all specified services are reported using the - * {@link BluetoothGattServerCallback#onScanResult} callback. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param serviceUuids Array of services to look for - * @return true, if the scan was started successfully + * Returns a service by UUID, instance and type. + * @hide */ - public boolean startScan(UUID[] serviceUuids) { - if (DBG) Log.d(TAG, "startScan() - with UUIDs"); - if (mService == null || mServerIf == 0) return false; - - try { - ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(serviceUuids[i]); + /*package*/ BluetoothGattService getService(UUID uuid, int instanceId, int type) { + for(BluetoothGattService svc : mServices) { + if (svc.getType() == type && + svc.getInstanceId() == instanceId && + svc.getUuid().equals(uuid)) { + return svc; } - mService.startScanWithUuids(mServerIf, true, uuids); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; - } - - /** - * Stops an ongoing Bluetooth LE device scan. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - */ - public void stopScan() { - if (DBG) Log.d(TAG, "stopScan()"); - if (mService == null || mServerIf == 0) return; - - try { - mService.stopScan(mServerIf, true); - } catch (RemoteException e) { - Log.e(TAG,"",e); } + return null; } /** - * Initiate a connection to a Bluetooth Gatt capable device. + * Initiate a connection to a Bluetooth GATT capable device. * * <p>The connection may not be established right away, but will be * completed when the remote device is available. A @@ -542,11 +392,10 @@ public final class BluetoothGattServer implements BluetoothProfile { * when the remote device is in range/available. Generally, the first ever * connection to a device should be direct (autoConnect set to false) and * subsequent connections to known devices should be invoked with the - * autoConnect parameter set to false. + * autoConnect parameter set to true. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param device Remote device to connect to * @param autoConnect Whether to directly connect to the remote device (false) * or to automatically connect as soon as the remote * device becomes available (true). @@ -590,7 +439,7 @@ public final class BluetoothGattServer implements BluetoothProfile { * Send a response to a read or write request to a remote device. * * <p>This function must be invoked in when a remote read/write request - * is received by one of these callback methots: + * is received by one of these callback methods: * * <ul> * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest} @@ -662,17 +511,17 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Add a service to the list of services to be advertised. + * Add a service to the list of services to be hosted. * * <p>Once a service has been addded to the the list, the service and it's - * included characteristics will be advertised by the local device. + * included characteristics will be provided by the local device. * - * <p>If the local device is already advertising services when this function + * <p>If the local device has already exposed services when this function * is called, a service update notification will be sent to all clients. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param service Service to be added to the list of services advertised + * @param service Service to be added to the list of services provided * by this device. * @return true, if the service has been added successfully */ @@ -705,9 +554,10 @@ public final class BluetoothGattServer implements BluetoothProfile { List<BluetoothGattDescriptor> descriptors = characteristic.getDescriptors(); for (BluetoothGattDescriptor descriptor: descriptors) { + permission = ((characteristic.getKeySize() - 7) << 12) + + descriptor.getPermissions(); mService.addDescriptor(mServerIf, - new ParcelUuid(descriptor.getUuid()), - descriptor.getPermissions()); + new ParcelUuid(descriptor.getUuid()), permission); } } @@ -721,11 +571,11 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Removes a service from the list of services to be advertised. + * Removes a service from the list of services to be provided. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * - * @param service Service to beremoved. + * @param service Service to be removed. * @return true, if the service has been removed */ public boolean removeService(BluetoothGattService service) { @@ -749,7 +599,7 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Remove all services from the list of advertised services. + * Remove all services from the list of provided services. * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. */ public void clearServices() { @@ -765,7 +615,7 @@ public final class BluetoothGattServer implements BluetoothProfile { } /** - * Returns a list of GATT services offered bu this device. + * Returns a list of GATT services offered by this device. * * <p>An application must call {@link #addService} to add a serice to the * list of services offered by this device. @@ -802,99 +652,40 @@ public final class BluetoothGattServer implements BluetoothProfile { return null; } + /** - * Get the current connection state of the profile. + * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * with {@link BluetoothProfile#GATT} as argument * - * <p>This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device Remote bluetooth device. - * @return State of the profile connection. One of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} + * @throws UnsupportedOperationException */ @Override public int getConnectionState(BluetoothDevice device) { - if (DBG) Log.d(TAG,"getConnectionState()"); - if (mService == null) return STATE_DISCONNECTED; - - List<BluetoothDevice> connectedDevices = getConnectedDevices(); - for(BluetoothDevice connectedDevice : connectedDevices) { - if (device.equals(connectedDevice)) { - return STATE_CONNECTED; - } - } - - return STATE_DISCONNECTED; + throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); } /** - * Get connected devices for the Gatt profile. - * - * <p> Return the set of devices which are in state {@link #STATE_CONNECTED} + * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} + * with {@link BluetoothProfile#GATT} as argument * - * <p>This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return List of devices. The list will be empty on error. + * @throws UnsupportedOperationException */ @Override public List<BluetoothDevice> getConnectedDevices() { - if (DBG) Log.d(TAG,"getConnectedDevices"); - - List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>(); - if (mService == null) return connectedDevices; - - try { - connectedDevices = mService.getDevicesMatchingConnectionStates( - new int[] { BluetoothProfile.STATE_CONNECTED }); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - - return connectedDevices; + throw new UnsupportedOperationException + ("Use BluetoothManager#getConnectedDevices instead."); } /** - * Get a list of devices that match any of the given connection - * states. - * - * <p> If none of the devices match any of the given states, - * an empty list will be returned. - * - * <p>This is not specific to any application configuration but represents - * the connection state of the local Bluetooth adapter for this profile. - * This can be used by applications like status bar which would just like - * to know the state of the local adapter. + * Not supported - please use + * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} + * with {@link BluetoothProfile#GATT} as first argument * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param states Array of states. States can be one of - * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, - * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, - * @return List of devices. The list will be empty on error. + * @throws UnsupportedOperationException */ @Override public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { - if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); - - List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); - if (mService == null) return devices; - - try { - devices = mService.getDevicesMatchingConnectionStates(states); - } catch (RemoteException e) { - Log.e(TAG,"",e); - } - - return devices; + throw new UnsupportedOperationException + ("Use BluetoothManager#getDevicesMatchingConnectionStates instead."); } } diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java index 4f608ff..f9f1d97 100644 --- a/core/java/android/bluetooth/BluetoothGattServerCallback.java +++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java @@ -22,30 +22,8 @@ import android.util.Log; /** * This abstract class is used to implement {@link BluetoothGattServer} callbacks. - * @hide */ public abstract class BluetoothGattServerCallback { - /** - * Callback to inform change in registration state of the application. - * - * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application - * was successfully registered. - */ - public void onAppRegistered(int status) { - } - - /** - * Callback reporting an LE device found during a device scan initiated - * by the {@link BluetoothGattServer#startScan} function. - * - * @param device Identifies the remote device - * @param rssi The RSSI value for the remote device as reported by the - * Bluetooth hardware. 0 if no RSSI value is available. - * @param scanRecord The content of the advertisement record offered by - * the remote device. - */ - public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) { - } /** * Callback indicating when a remote device has been connected or disconnected. @@ -101,9 +79,9 @@ public abstract class BluetoothGattServerCallback { * @param value The value the client wants to assign to the characteristic */ public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { + BluetoothGattCharacteristic characteristic, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { } /** @@ -118,7 +96,7 @@ public abstract class BluetoothGattServerCallback { * @param descriptor Descriptor to be read */ public void onDescriptorReadRequest(BluetoothDevice device, int requestId, - int offset, BluetoothGattDescriptor descriptor) { + int offset, BluetoothGattDescriptor descriptor) { } /** @@ -137,9 +115,9 @@ public abstract class BluetoothGattServerCallback { * @param value The value the client wants to assign to the descriptor */ public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, - BluetoothGattDescriptor descriptor, - boolean preparedWrite, boolean responseNeeded, - int offset, byte[] value) { + BluetoothGattDescriptor descriptor, + boolean preparedWrite, boolean responseNeeded, + int offset, byte[] value) { } /** diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java index 6a3ce66..c3b3cfe 100644 --- a/core/java/android/bluetooth/BluetoothGattService.java +++ b/core/java/android/bluetooth/BluetoothGattService.java @@ -22,8 +22,7 @@ import java.util.List; import java.util.UUID; /** - * Represents a Bluetooth Gatt Service - * @hide + * Represents a Bluetooth GATT Service */ public class BluetoothGattService { @@ -81,9 +80,14 @@ public class BluetoothGattService { /** * Create a new BluetoothGattService. - * @hide + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param uuid The UUID for this service + * @param serviceType The type of this service, + * {@link BluetoothGattService#SERVICE_TYPE_PRIMARY} or + * {@link BluetoothGattService#SERVICE_TYPE_SECONDARY} */ - /*package*/ BluetoothGattService(UUID uuid, int serviceType) { + public BluetoothGattService(UUID uuid, int serviceType) { mDevice = null; mUuid = uuid; mInstanceId = 0; @@ -115,11 +119,28 @@ public class BluetoothGattService { } /** + * Add an included service to this service. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param service The service to be added + * @return true, if the included service was added to the service + */ + public boolean addService(BluetoothGattService service) { + mIncludedServices.add(service); + return true; + } + + /** * Add a characteristic to this service. - * @hide + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param characteristic The characteristics to be added + * @return true, if the characteristic was added to the service */ - /*package*/ void addCharacteristic(BluetoothGattCharacteristic characteristic) { + public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) { mCharacteristics.add(characteristic); + characteristic.setService(this); + return true; } /** @@ -136,6 +157,15 @@ public class BluetoothGattService { } /** + * Force the instance ID. + * This is needed for conformance testing only. + * @hide + */ + public void setInstanceId(int instanceId) { + mInstanceId = instanceId; + } + + /** * Get the handle count override (conformance testing. * @hide */ @@ -144,6 +174,15 @@ public class BluetoothGattService { } /** + * Force the number of handles to reserve for this service. + * This is needed for conformance testing only. + * @hide + */ + public void setHandles(int handles) { + mHandles = handles; + } + + /** * Add an included service to the internal map. * @hide */ @@ -153,7 +192,6 @@ public class BluetoothGattService { /** * Returns the UUID of this service - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return UUID of this service */ @@ -168,8 +206,6 @@ public class BluetoothGattService { * (ex. multiple battery services for different batteries), the instance * ID is used to distuinguish services. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * * @return Instance ID of this service */ public int getInstanceId() { @@ -178,15 +214,13 @@ public class BluetoothGattService { /** * Get the type of this service (primary/secondary) - * @hide */ public int getType() { return mServiceType; } /** - * Get the list of included Gatt services for this service. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * Get the list of included GATT services for this service. * * @return List of included services or empty list if no included services * were discovered. @@ -197,7 +231,6 @@ public class BluetoothGattService { /** * Returns a list of characteristics included in this service. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @return Characteristics included in this service */ @@ -217,9 +250,7 @@ public class BluetoothGattService { * UUID, the first instance of a characteristic with the given UUID * is returned. * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @return Gatt characteristic object or null if no characteristic with the + * @return GATT characteristic object or null if no characteristic with the * given UUID was found. */ public BluetoothGattCharacteristic getCharacteristic(UUID uuid) { diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 793d798..793d798 100755..100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java index db7e424..db7e424 100755..100644 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothInputDevice.java diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java new file mode 100644 index 0000000..19083b5 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.content.Context; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * High level manager used to obtain an instance of an {@link BluetoothAdapter} + * and to conduct overall Bluetooth Management. + * <p> + * Use {@link android.content.Context#getSystemService(java.lang.String)} + * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager}, + * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}. + * <p> + * Alternately, you can just call the static helper + * {@link BluetoothAdapter#getDefaultAdapter()}. + * + * <div class="special reference"> + * <h3>Developer Guides</h3> + * <p>For more information about using BLUETOOTH, read the + * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p> + * </div> + * + * @see Context#getSystemService + * @see BluetoothAdapter#getDefaultAdapter() + */ +public final class BluetoothManager { + private static final String TAG = "BluetoothManager"; + private static final boolean DBG = true; + private static final boolean VDBG = true; + + private final BluetoothAdapter mAdapter; + + /** + * @hide + */ + public BluetoothManager(Context context) { + context = context.getApplicationContext(); + if (context == null) { + throw new IllegalArgumentException( + "context not associated with any application (using a mock context?)"); + } + // Legacy api - getDefaultAdapter does not take in the context + mAdapter = BluetoothAdapter.getDefaultAdapter(); + } + + /** + * Get the default BLUETOOTH Adapter for this device. + * + * @return the default BLUETOOTH Adapter + */ + public BluetoothAdapter getAdapter() { + return mAdapter; + } + + /** + * Get the current connection state of the profile to the remote device. + * + * <p>This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for certain profile. + * This can be used by applications like status bar which would just like + * to know the state of Bluetooth. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Remote bluetooth device. + * @param profile GATT or GATT_SERVER + * @return State of the profile connection. One of + * {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING}, + * {@link BluetoothProfile#STATE_DISCONNECTED}, + * {@link BluetoothProfile#STATE_DISCONNECTING} + */ + public int getConnectionState(BluetoothDevice device, int profile) { + if (DBG) Log.d(TAG,"getConnectionState()"); + + List<BluetoothDevice> connectedDevices = getConnectedDevices(profile); + for(BluetoothDevice connectedDevice : connectedDevices) { + if (device.equals(connectedDevice)) { + return BluetoothProfile.STATE_CONNECTED; + } + } + + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Get connected devices for the specified profile. + * + * <p> Return the set of devices which are in state {@link BluetoothProfile#STATE_CONNECTED} + * + * <p>This is not specific to any application configuration but represents + * the connection state of Bluetooth for this profile. + * This can be used by applications like status bar which would just like + * to know the state of Bluetooth. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param profile GATT or GATT_SERVER + * @return List of devices. The list will be empty on error. + */ + public List<BluetoothDevice> getConnectedDevices(int profile) { + if (DBG) Log.d(TAG,"getConnectedDevices"); + if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { + throw new IllegalArgumentException("Profile not supported: " + profile); + } + + List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>(); + + try { + IBluetoothManager managerService = mAdapter.getBluetoothManager(); + IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + if (iGatt == null) return connectedDevices; + + connectedDevices = iGatt.getDevicesMatchingConnectionStates( + new int[] { BluetoothProfile.STATE_CONNECTED }); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return connectedDevices; + } + + /** + * + * Get a list of devices that match any of the given connection + * states. + * + * <p> If none of the devices match any of the given states, + * an empty list will be returned. + * + * <p>This is not specific to any application configuration but represents + * the connection state of the local Bluetooth adapter for this profile. + * This can be used by applications like status bar which would just like + * to know the state of the local adapter. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param profile GATT or GATT_SERVER + * @param states Array of states. States can be one of + * {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING}, + * {@link BluetoothProfile#STATE_DISCONNECTED}, + * {@link BluetoothProfile#STATE_DISCONNECTING}, + * @return List of devices. The list will be empty on error. + */ + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) { + if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); + + if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { + throw new IllegalArgumentException("Profile not supported: " + profile); + } + + List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); + + try { + IBluetoothManager managerService = mAdapter.getBluetoothManager(); + IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + if (iGatt == null) return devices; + devices = iGatt.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + + return devices; + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @return BluetoothGattServer instance + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback) { + if (context == null || callback == null) { + throw new IllegalArgumentException("null parameter: " + context + " " + callback); + } + + // TODO(Bluetooth) check whether platform support BLE + // Do the check here or in GattServer? + + try { + IBluetoothManager managerService = mAdapter.getBluetoothManager(); + IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + if (iGatt == null) { + Log.e(TAG, "Fail to get GATT Server connection"); + return null; + } + BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt); + Boolean regStatus = mGattServer.registerCallback(callback); + return regStatus? mGattServer : null; + } catch (RemoteException e) { + Log.e(TAG,"",e); + return null; + } + } +} diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index b5280e5..b5280e5 100755..100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 9ee202a..43079f4 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -89,13 +89,11 @@ public interface BluetoothProfile { /** * GATT - * @hide */ static public final int GATT = 7; /** * GATT_SERVER - * @hide */ static public final int GATT_SERVER = 8; diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index 43c2392..81c0a6a 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -21,6 +21,7 @@ import android.os.ServiceManager; import android.os.INetworkManagementService; import android.content.Context; import android.net.ConnectivityManager; +import android.net.DhcpResults; import android.net.LinkCapabilities; import android.net.LinkProperties; import android.net.NetworkInfo; @@ -28,7 +29,10 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkStateTracker; import android.net.NetworkUtils; import android.os.Handler; +import android.os.Looper; import android.os.Message; +import android.os.Messenger; +import android.text.TextUtils; import android.util.Log; import java.net.InterfaceAddress; import android.net.LinkAddress; @@ -36,8 +40,11 @@ import android.net.RouteInfo; import java.net.Inet4Address; import android.os.SystemProperties; +import com.android.internal.util.AsyncChannel; + import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; /** * This class tracks the data connection associated with Bluetooth @@ -51,24 +58,29 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; private static final String TAG = "BluetoothTethering"; private static final boolean DBG = true; - private static final boolean VDBG = false; + private static final boolean VDBG = true; private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); + private final Object mLinkPropertiesLock = new Object(); private LinkProperties mLinkProperties; + private LinkCapabilities mLinkCapabilities; + + private final Object mNetworkInfoLock = new Object(); private NetworkInfo mNetworkInfo; private BluetoothPan mBluetoothPan; - private static String mIface; - private Thread mDhcpThread; + private static String mRevTetheredIface; /* For sending events to connectivity service handler */ private Handler mCsHandler; - private Context mContext; - public static BluetoothTetheringDataTracker sInstance; + protected Context mContext; + private static BluetoothTetheringDataTracker sInstance; + private BtdtHandler mBtdtHandler; + private AtomicReference<AsyncChannel> mAsyncChannel = new AtomicReference<AsyncChannel>(null); private BluetoothTetheringDataTracker() { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, ""); @@ -108,6 +120,7 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { if (adapter != null) { adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); } + mBtdtHandler = new BtdtHandler(target.getLooper(), this); } private BluetoothProfile.ServiceListener mProfileServiceListener = @@ -224,15 +237,19 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { /** * Fetch NetworkInfo for the network */ - public synchronized NetworkInfo getNetworkInfo() { - return mNetworkInfo; + public NetworkInfo getNetworkInfo() { + synchronized (mNetworkInfoLock) { + return new NetworkInfo(mNetworkInfo); + } } /** * Fetch LinkProperties for the network */ - public synchronized LinkProperties getLinkProperties() { - return new LinkProperties(mLinkProperties); + public LinkProperties getLinkProperties() { + synchronized (mLinkPropertiesLock) { + return new LinkProperties(mLinkProperties); + } } /** @@ -286,88 +303,68 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { return count; } - - private boolean readLinkProperty(String iface) { - String DhcpPrefix = "dhcp." + iface + "."; - String ip = SystemProperties.get(DhcpPrefix + "ipaddress"); - String dns1 = SystemProperties.get(DhcpPrefix + "dns1"); - String dns2 = SystemProperties.get(DhcpPrefix + "dns2"); - String gateway = SystemProperties.get(DhcpPrefix + "gateway"); - String mask = SystemProperties.get(DhcpPrefix + "mask"); - if(ip.isEmpty() || gateway.isEmpty()) { - Log.e(TAG, "readLinkProperty, ip: " + ip + ", gateway: " + gateway + ", can not be empty"); - return false; + void startReverseTether(final LinkProperties linkProperties) { + if (linkProperties == null || TextUtils.isEmpty(linkProperties.getInterfaceName())) { + Log.e(TAG, "attempted to reverse tether with empty interface"); + return; } - int PrefixLen = countPrefixLength(NetworkUtils.numericToInetAddress(mask).getAddress()); - mLinkProperties.addLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(ip), PrefixLen)); - RouteInfo ri = new RouteInfo(NetworkUtils.numericToInetAddress(gateway)); - mLinkProperties.addRoute(ri); - if(!dns1.isEmpty()) - mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns1)); - if(!dns2.isEmpty()) - mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns2)); - mLinkProperties.setInterfaceName(iface); - return true; - } - public synchronized void startReverseTether(String iface) { - mIface = iface; - if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); - mDhcpThread = new Thread(new Runnable() { + synchronized (mLinkPropertiesLock) { + if (mLinkProperties.getInterfaceName() != null) { + Log.e(TAG, "attempted to reverse tether while already in process"); + return; + } + mLinkProperties = linkProperties; + } + Thread dhcpThread = new Thread(new Runnable() { public void run() { - //TODO(): Add callbacks for failure and success case. //Currently this thread runs independently. - if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); - String DhcpResultName = "dhcp." + mIface + ".result";; - String result = ""; - if (VDBG) Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName); - for(int i = 0; i < 30*5; i++) { - try { Thread.sleep(200); } catch (InterruptedException ie) { return;} - result = SystemProperties.get(DhcpResultName); - if (VDBG) Log.d(TAG, "read " + DhcpResultName + ": " + result); - if(result.equals("failed")) { - Log.e(TAG, "startReverseTether, failed to start dhcp service"); + DhcpResults dhcpResults = new DhcpResults(); + boolean success = NetworkUtils.runDhcp(linkProperties.getInterfaceName(), + dhcpResults); + synchronized (mLinkPropertiesLock) { + if (linkProperties.getInterfaceName() != mLinkProperties.getInterfaceName()) { + Log.e(TAG, "obsolete DHCP run aborted"); return; } - if(result.equals("ok")) { - if (VDBG) Log.d(TAG, "startReverseTether, dhcp resut: " + result); - if(readLinkProperty(mIface)) { - - mNetworkInfo.setIsAvailable(true); - mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); - - if (VDBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler); - if(mCsHandler != null) { - Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); - msg.sendToTarget(); - - msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); - msg.sendToTarget(); - } - } + if (!success) { + Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); return; } + mLinkProperties = dhcpResults.linkProperties; + synchronized (mNetworkInfoLock) { + mNetworkInfo.setIsAvailable(true); + mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); + if (mCsHandler != null) { + Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, + new NetworkInfo(mNetworkInfo)); + msg.sendToTarget(); + } + } + return; } - Log.e(TAG, "startReverseTether, dhcp failed, resut: " + result); } }); - mDhcpThread.start(); + dhcpThread.start(); } - public synchronized void stopReverseTether() { - //NetworkUtils.stopDhcp(iface); - if(mDhcpThread != null && mDhcpThread.isAlive()) { - mDhcpThread.interrupt(); - try { mDhcpThread.join(); } catch (InterruptedException ie) { return; } + void stopReverseTether() { + synchronized (mLinkPropertiesLock) { + if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) { + Log.e(TAG, "attempted to stop reverse tether with nothing tethered"); + return; + } + NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName()); + mLinkProperties.clear(); + synchronized (mNetworkInfoLock) { + mNetworkInfo.setIsAvailable(false); + mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); + + if (mCsHandler != null) { + mCsHandler.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo)). + sendToTarget(); + } + } } - mLinkProperties.clear(); - mNetworkInfo.setIsAvailable(false); - mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); - - Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); - msg.sendToTarget(); - - msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); - msg.sendToTarget(); } public void setDependencyMet(boolean met) { @@ -383,4 +380,54 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker { public void removeStackedLink(LinkProperties link) { mLinkProperties.removeStackedLink(link); } + + static class BtdtHandler extends Handler { + private AsyncChannel mStackChannel; + private final BluetoothTetheringDataTracker mBtdt; + + BtdtHandler(Looper looper, BluetoothTetheringDataTracker parent) { + super(looper); + mBtdt = parent; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + if (VDBG) Log.d(TAG, "got CMD_CHANNEL_HALF_CONNECTED"); + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + AsyncChannel ac = (AsyncChannel)msg.obj; + if (mBtdt.mAsyncChannel.compareAndSet(null, ac) == false) { + Log.e(TAG, "Trying to set mAsyncChannel twice!"); + } else { + ac.sendMessage( + AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + } + } + break; + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: + if (VDBG) Log.d(TAG, "got CMD_CHANNEL_DISCONNECTED"); + mBtdt.stopReverseTether(); + mBtdt.mAsyncChannel.set(null); + break; + case NetworkStateTracker.EVENT_NETWORK_CONNECTED: + LinkProperties linkProperties = (LinkProperties)(msg.obj); + if (VDBG) Log.d(TAG, "got EVENT_NETWORK_CONNECTED, " + linkProperties); + mBtdt.startReverseTether(linkProperties); + break; + case NetworkStateTracker.EVENT_NETWORK_DISCONNECTED: + linkProperties = (LinkProperties)(msg.obj); + if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties); + mBtdt.stopReverseTether(); + break; + } + } + } + + @Override + public void supplyMessenger(Messenger messenger) { + if (messenger != null) { + new AsyncChannel().connect(mContext, mBtdtHandler, messenger); + } + } } diff --git a/core/java/android/bluetooth/IBluetoothInputDevice.aidl b/core/java/android/bluetooth/IBluetoothInputDevice.aidl index 1ebb9ca..1ebb9ca 100755..100644 --- a/core/java/android/bluetooth/IBluetoothInputDevice.aidl +++ b/core/java/android/bluetooth/IBluetoothInputDevice.aidl diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl index ed8777c..493d2f8 100755..100644 --- a/core/java/android/bluetooth/IBluetoothManager.aidl +++ b/core/java/android/bluetooth/IBluetoothManager.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.IBluetooth; +import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothStateChangeCallback; @@ -35,6 +36,7 @@ interface IBluetoothManager boolean enable(); boolean enableNoAutoConnect(); boolean disable(boolean persist); + IBluetoothGatt getBluetoothGatt(); String getAddress(); String getName(); diff --git a/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java b/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java deleted file mode 100644 index c05abb2..0000000 --- a/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.bluetooth; - -import java.util.ArrayList; -import java.util.IllegalFormatConversionException; -import java.util.List; -import java.util.UUID; - -/** - * Mutable variant of a Bluetooth Gatt Characteristic - * @hide - */ -public class MutableBluetoothGattCharacteristic extends BluetoothGattCharacteristic { - - /** - * Create a new MutableBluetoothGattCharacteristic. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param uuid The UUID for this characteristic - * @param properties Properties of this characteristic - * @param permissions Permissions for this characteristic - */ - public MutableBluetoothGattCharacteristic(UUID uuid, int properties, int permissions) { - super(null, uuid, 0, properties, permissions); - } - - /** - * Adds a descriptor to this characteristic. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param descriptor Descriptor to be added to this characteristic. - */ - public void addDescriptor(MutableBluetoothGattDescriptor descriptor) { - mDescriptors.add(descriptor); - descriptor.setCharacteristic(this); - } - - /** - * Set the desired key size. - * @hide - */ - public void setKeySize(int keySize) { - mKeySize = keySize; - } - - /** - * Sets the service associated with this device. - * @hide - */ - /*package*/ void setService(BluetoothGattService service) { - mService = service; - } -} diff --git a/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java b/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java deleted file mode 100644 index e455392..0000000 --- a/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import java.util.UUID; - -/** - * Mutable variant of a Bluetooth Gatt Descriptor - * @hide - */ -public class MutableBluetoothGattDescriptor extends BluetoothGattDescriptor { - - /** - * Create a new BluetoothGattDescriptor. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param uuid The UUID for this descriptor - * @param permissions Permissions for this descriptor - */ - public MutableBluetoothGattDescriptor(UUID uuid, int permissions) { - super(null, uuid, permissions); - } - - /** - * Set the back-reference to the associated characteristic - * @hide - */ - /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) { - mCharacteristic = characteristic; - } -} diff --git a/core/java/android/bluetooth/MutableBluetoothGattService.java b/core/java/android/bluetooth/MutableBluetoothGattService.java deleted file mode 100644 index 927f5ab..0000000 --- a/core/java/android/bluetooth/MutableBluetoothGattService.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.bluetooth; - -import android.bluetooth.BluetoothDevice; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -/** - * Represents a Bluetooth Gatt Service - * @hide - */ -public class MutableBluetoothGattService extends BluetoothGattService { - - /** - * Create a new MutableBluetoothGattService. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param uuid The UUID for this service - * @param serviceType The type of this service (primary/secondary) - */ - public MutableBluetoothGattService(UUID uuid, int serviceType) { - super(uuid, serviceType); - } - - /** - * Add an included service to this service. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param service The service to be added - * @return true, if the included service was added to the service - */ - public boolean addService(BluetoothGattService service) { - mIncludedServices.add(service); - return true; - } - - /** - * Add a characteristic to this service. - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param characteristic The characteristics to be added - * @return true, if the characteristic was added to the service - */ - public boolean addCharacteristic(MutableBluetoothGattCharacteristic characteristic) { - mCharacteristics.add(characteristic); - characteristic.setService(this); - return true; - } - - /** - * Force the instance ID. - * This is needed for conformance testing only. - * @hide - */ - public void setInstanceId(int instanceId) { - mInstanceId = instanceId; - } - - /** - * Force the number of handles to reserve for this service. - * This is needed for conformance testing only. - * @hide - */ - public void setHandles(int handles) { - mHandles = handles; - } -} diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index 4f42d50..9a32fdf 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -520,7 +520,7 @@ public abstract class BroadcastReceiver { IActivityManager am = ActivityManagerNative.getDefault(); IBinder binder = null; try { - service.setAllowFds(false); + service.prepareToLeaveProcess(); binder = am.peekService(service, service.resolveTypeIfNeeded( myContext.getContentResolver())); } catch (RemoteException e) { diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 88f1a3d..50c4fed 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -21,6 +21,7 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.os.StrictMode; import android.text.Html; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -790,6 +791,24 @@ public class ClipData implements Parcelable { return mItems.get(index); } + /** + * Prepare this {@link ClipData} to leave an app process. + * + * @hide + */ + public void prepareToLeaveProcess() { + final int size = mItems.size(); + for (int i = 0; i < size; i++) { + final Item item = mItems.get(i); + if (item.mIntent != null) { + item.mIntent.prepareToLeaveProcess(); + } + if (item.mUri != null && StrictMode.vmFileUriExposureEnabled()) { + item.mUri.checkFileUriExposed("ClipData.Item.getUri()"); + } + } + } + @Override public String toString() { StringBuilder b = new StringBuilder(128); diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index 88a4229..69f9d4a 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -22,6 +22,7 @@ import android.os.RemoteException; import android.os.Handler; import android.os.IBinder; import android.os.ServiceManager; +import android.os.StrictMode; import android.util.Log; import java.util.ArrayList; @@ -118,6 +119,9 @@ public class ClipboardManager extends android.text.ClipboardManager { */ public void setPrimaryClip(ClipData clip) { try { + if (clip != null) { + clip.prepareToLeaveProcess(); + } getService().setPrimaryClip(clip, mContext.getBasePackageName()); } catch (RemoteException e) { } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 7dd76cd..ef9b0bf 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2203,7 +2203,6 @@ public abstract class Context { * {@link android.bluetooth.BluetoothAdapter} for using Bluetooth. * * @see #getSystemService - * @hide */ public static final String BLUETOOTH_SERVICE = "bluetooth"; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e1461e3..97ad7dd 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -32,6 +32,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.os.StrictMode; import android.util.AttributeSet; import android.util.Log; @@ -2416,8 +2417,11 @@ public class Intent implements Parcelable, Cloneable { /** * Broadcast to a specific application to query any supported restrictions to impose - * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS} - * which is of type <code>ArrayList<RestrictionEntry></code>. + * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS}, + * which is of type <code>ArrayList<RestrictionEntry></code>. It can also + * contain an extra {@link #EXTRA_RESTRICTIONS_INTENT}, which is of type <code>Intent</code>. + * The activity specified by that intent will be launched for a result which must contain + * the extra {@link #EXTRA_RESTRICTIONS}. The returned restrictions will be persisted. * @see RestrictionEntry */ public static final String ACTION_GET_RESTRICTION_ENTRIES = @@ -2575,6 +2579,14 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_SHOW_BRIGHTNESS_DIALOG = "android.intent.action.SHOW_BRIGHTNESS_DIALOG"; + /** + * Broadcast Action: A global button was pressed. Includes a single + * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that + * caused the broadcast. + * @hide + */ + public static final String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). @@ -3161,6 +3173,13 @@ public class Intent implements Parcelable, Cloneable { */ public static final String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions"; + /** + * Extra used in the response from a BroadcastReceiver that handles + * {@link #ACTION_GET_RESTRICTION_ENTRIES}. + */ + public static final String EXTRA_RESTRICTIONS_INTENT = + "android.intent.extra.restrictions_intent"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). @@ -6948,6 +6967,32 @@ public class Intent implements Parcelable, Cloneable { } /** + * Prepare this {@link Intent} to leave an app process. + * + * @hide + */ + public void prepareToLeaveProcess() { + setAllowFds(false); + + if (mSelector != null) { + mSelector.prepareToLeaveProcess(); + } + if (mClipData != null) { + mClipData.prepareToLeaveProcess(); + } + + if (mData != null && StrictMode.vmFileUriExposureEnabled()) { + // There are several ACTION_MEDIA_* broadcasts that send file:// + // Uris, so only check common actions. + if (ACTION_VIEW.equals(mAction) || + ACTION_EDIT.equals(mAction) || + ACTION_ATTACH_DATA.equals(mAction)) { + mData.checkFileUriExposed("Intent.getData()"); + } + } + } + + /** * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested * intents in {@link #ACTION_CHOOSER}. diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java index 97c4cb8..af90385 100644 --- a/core/java/android/content/RestrictionEntry.java +++ b/core/java/android/content/RestrictionEntry.java @@ -34,141 +34,278 @@ import java.lang.annotation.Inherited; public class RestrictionEntry implements Parcelable { /** - * A type of restriction. Use this one for information that needs to be transferred across - * but shouldn't be presented to the user in the UI. + * A type of restriction. Use this type for information that needs to be transferred across + * but shouldn't be presented to the user in the UI. Stores a single String value. */ public static final int TYPE_NULL = 0; + /** - * A type of restriction. Use this for storing true/false values, typically presented as + * A type of restriction. Use this for storing a boolean value, typically presented as * a checkbox in the UI. */ public static final int TYPE_BOOLEAN = 1; + /** * A type of restriction. Use this for storing a string value, typically presented as - * a single-select list. The {@link #values} and {@link #choices} need to have the list of - * possible values and the corresponding localized strings, respectively, to present in the UI. + * a single-select list. Call {@link #setChoiceEntries(String[])} and + * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user + * and the corresponding values, respectively. */ public static final int TYPE_CHOICE = 2; + /** * A type of restriction. Use this for storing a string value, typically presented as - * a single-select list. The {@link #values} and {@link #choices} need to have the list of - * possible values and the corresponding localized strings, respectively, to present in the UI. + * a single-select list. Call {@link #setChoiceEntries(String[])} and + * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user + * and the corresponding values, respectively. * The presentation could imply that values in lower array indices are included when a * particular value is chosen. */ public static final int TYPE_CHOICE_LEVEL = 3; + /** * A type of restriction. Use this for presenting a multi-select list where more than one * entry can be selected, such as for choosing specific titles to white-list. - * The {@link #values} and {@link #choices} need to have the list of - * possible values and the corresponding localized strings, respectively, to present in the UI. - * Use {@link #getMultipleValues()} and {@link #setMultipleValues(String[])} to manipulate - * the selections. + * Call {@link #setChoiceEntries(String[])} and + * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user + * and the corresponding values, respectively. + * Use {@link #getAllSelectedStrings()} and {@link #setAllSelectedStrings(String[])} to + * manipulate the selections. */ public static final int TYPE_MULTI_SELECT = 4; /** The type of restriction. */ - public int type; + private int type; /** The unique key that identifies the restriction. */ - public String key; + private String key; /** The user-visible title of the restriction. */ - public String title; + private String title; /** The user-visible secondary description of the restriction. */ - public String description; + private String description; /** The user-visible set of choices used for single-select and multi-select lists. */ - public String [] choices; + private String [] choices; /** The values corresponding to the user-visible choices. The value(s) of this entry will - * one or more of these, returned by {@link #getMultipleValues()} and - * {@link #getStringValue()}. + * one or more of these, returned by {@link #getAllSelectedStrings()} and + * {@link #getSelectedString()}. */ - public String [] values; + private String [] values; /* The chosen value, whose content depends on the type of the restriction. */ private String currentValue; + /* List of selected choices in the multi-select case. */ private String[] currentValues; /** * Constructor for {@link #TYPE_CHOICE} and {@link #TYPE_CHOICE_LEVEL} types. * @param key the unique key for this restriction - * @param value the current value + * @param selectedString the current value */ - public RestrictionEntry(String key, String value) { + public RestrictionEntry(String key, String selectedString) { this.key = key; - this.currentValue = value; + this.currentValue = selectedString; } /** * Constructor for {@link #TYPE_BOOLEAN} type. * @param key the unique key for this restriction - * @param value the current value + * @param selectedState whether this restriction is selected or not */ - public RestrictionEntry(String key, boolean value) { + public RestrictionEntry(String key, boolean selectedState) { this.key = key; - setValue(value); + setSelectedState(selectedState); } /** * Constructor for {@link #TYPE_MULTI_SELECT} type. * @param key the unique key for this restriction - * @param multipleValues the list of values that are currently selected + * @param selectedStrings the list of values that are currently selected */ - public RestrictionEntry(String key, String[] multipleValues) { + public RestrictionEntry(String key, String[] selectedStrings) { this.key = key; - this.currentValues = multipleValues; + this.currentValues = selectedStrings; } /** - * Returns the current value. Null for {@link #TYPE_MULTI_SELECT} type. - * @return the current value + * Sets the type for this restriction. + * @param type the type for this restriction. */ - public String getStringValue() { + public void setType(int type) { + this.type = type; + } + + /** + * Returns the type for this restriction. + * @return the type for this restriction + */ + public int getType() { + return type; + } + + /** + * Returns the currently selected string value. + * @return the currently selected value, which can be null for types that aren't for holding + * single string values. + */ + public String getSelectedString() { return currentValue; } /** - * Returns the list of current selections. Null if the type is not {@link #TYPE_MULTI_SELECT}. - * @return the list of current selections. + * Returns the list of currently selected values. + * @return the list of current selections, if type is {@link #TYPE_MULTI_SELECT}, + * null otherwise. */ - public String[] getMultipleValues() { + public String[] getAllSelectedStrings() { return currentValues; } /** - * Returns the current boolean value for entries of type {@link #TYPE_BOOLEAN}. - * @return the current value + * Returns the current selected state for an entry of type {@link #TYPE_BOOLEAN}. + * @return the current selected state of the entry. */ - public boolean getBooleanValue() { + public boolean getSelectedState() { return Boolean.parseBoolean(currentValue); } /** - * Set the current string value. - * @param s the current value + * Sets the string value to use as the selected value for this restriction. This value will + * be persisted by the system for later use by the application. + * @param selectedString the string value to select. + */ + public void setSelectedString(String selectedString) { + currentValue = selectedString; + } + + /** + * Sets the current selected state for an entry of type {@link #TYPE_BOOLEAN}. This value will + * be persisted by the system for later use by the application. + * @param state the current selected state + */ + public void setSelectedState(boolean state) { + currentValue = Boolean.toString(state); + } + + /** + * Sets the current list of selected values for an entry of type {@link #TYPE_MULTI_SELECT}. + * These values will be persisted by the system for later use by the application. + * @param allSelectedStrings the current list of selected values. + */ + public void setAllSelectedStrings(String[] allSelectedStrings) { + currentValues = allSelectedStrings; + } + + /** + * Sets a list of string values that can be selected by the user. If no user-visible entries + * are set by a call to {@link #setChoiceEntries(String[])}, these values will be the ones + * shown to the user. Values will be chosen from this list as the user's selection and the + * selected values can be retrieved by a call to {@link #getAllSelectedStrings()}, or + * {@link #getSelectedString()}, depending on whether it is a multi-select type or choice type. + * This method is not relevant for types other than {@link #TYPE_CHOICE_LEVEL}, + * {@link #TYPE_CHOICE}, and {@link #TYPE_MULTI_SELECT}. + * @param choiceValues an array of Strings which will be the selected values for the user's + * selections. + * @see #getChoiceValues() + * @see #getAllSelectedStrings() + */ + public void setChoiceValues(String[] choiceValues) { + values = choiceValues; + } + + /** + * Sets a list of string values that can be selected by the user, similar to + * {@link #setChoiceValues(String[])}. + * @param context the application context for retrieving the resources. + * @param stringArrayResId the resource id for a string array containing the possible values. + * @see #setChoiceValues(String[]) + */ + public void setChoiceValues(Context context, int stringArrayResId) { + values = context.getResources().getStringArray(stringArrayResId); + } + + /** + * Returns the list of possible string values set earlier. + * @return the list of possible values. + */ + public String[] getChoiceValues() { + return values; + } + + /** + * Sets a list of strings that will be presented as choices to the user. When the + * user selects one or more of these choices, the corresponding value from the possible values + * are stored as the selected strings. The size of this array must match the size of the array + * set in {@link #setChoiceValues(String[])}. This method is not relevant for types other + * than {@link #TYPE_CHOICE_LEVEL}, {@link #TYPE_CHOICE}, and {@link #TYPE_MULTI_SELECT}. + * @param choiceEntries the list of user-visible choices. + * @see #setChoiceValues(String[]) + */ + public void setChoiceEntries(String[] choiceEntries) { + choices = choiceEntries; + } + + /** Sets a list of strings that will be presented as choices to the user. This is similar to + * {@link #setChoiceEntries(String[])}. + * @param context the application context, used for retrieving the resources. + * @param stringArrayResId the resource id of a string array containing the possible entries. + */ + public void setChoiceEntries(Context context, int stringArrayResId) { + choices = context.getResources().getStringArray(stringArrayResId); + } + + /** + * Returns the list of strings, set earlier, that will be presented as choices to the user. + * @return the list of choices presented to the user. + */ + public String[] getChoiceEntries() { + return choices; + } + + /** + * Returns the provided user-visible description of the entry, if any. + * @return the user-visible description, null if none was set earlier. + */ + public String getDescription() { + return description; + } + + /** + * Sets the user-visible description of the entry, as a possible sub-text for the title. + * You can use this to describe the entry in more detail or to display the current state of + * the restriction. + * @param description the user-visible description string. + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * This is the unique key for the restriction entry. + * @return the key for the restriction. */ - public void setValue(String s) { - currentValue = s; + public String getKey() { + return key; } /** - * Sets the current boolean value. - * @param b the current value + * Returns the user-visible title for the entry, if any. + * @return the user-visible title for the entry, null if none was set earlier. */ - public void setValue(boolean b) { - currentValue = Boolean.toString(b); + public String getTitle() { + return title; } /** - * Sets the current list of selected values. - * @param values the current list of selected values + * Sets the user-visible title for the entry. + * @param title the user-visible title for the entry. */ - public void setMultipleValues(String[] values) { - currentValues = values; + public void setTitle(String title) { + this.title = title; } private boolean equalArrays(String[] one, String[] other) { diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index a32a201..a0e1555 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -239,7 +239,8 @@ interface IPackageManager { /** * As per {@link android.content.pm.PackageManager#setApplicationEnabledSetting}. */ - void setApplicationEnabledSetting(in String packageName, in int newState, int flags, int userId); + void setApplicationEnabledSetting(in String packageName, in int newState, int flags, + int userId, String callingPackage); /** * As per {@link android.content.pm.PackageManager#getApplicationEnabledSetting}. diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java index 75505bc..409b5ae 100644 --- a/core/java/android/content/pm/ManifestDigest.java +++ b/core/java/android/content/pm/ManifestDigest.java @@ -18,10 +18,17 @@ package android.content.pm; import android.os.Parcel; import android.os.Parcelable; -import android.util.Base64; - +import android.util.Slog; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; -import java.util.jar.Attributes; + +import libcore.io.IoUtils; /** * Represents the manifest digest for a package. This is suitable for comparison @@ -30,17 +37,17 @@ import java.util.jar.Attributes; * @hide */ public class ManifestDigest implements Parcelable { + private static final String TAG = "ManifestDigest"; + /** The digest of the manifest in our preferred order. */ private final byte[] mDigest; - /** Digest field names to look for in preferred order. */ - private static final String[] DIGEST_TYPES = { - "SHA1-Digest", "SHA-Digest", "MD5-Digest", - }; - /** What we print out first when toString() is called. */ private static final String TO_STRING_PREFIX = "ManifestDigest {mDigest="; + /** Digest algorithm to use. */ + private static final String DIGEST_ALGORITHM = "SHA-256"; + ManifestDigest(byte[] digest) { mDigest = digest; } @@ -49,26 +56,32 @@ public class ManifestDigest implements Parcelable { mDigest = source.createByteArray(); } - static ManifestDigest fromAttributes(Attributes attributes) { - if (attributes == null) { + static ManifestDigest fromInputStream(InputStream fileIs) { + if (fileIs == null) { return null; } - String encodedDigest = null; - - for (int i = 0; i < DIGEST_TYPES.length; i++) { - final String value = attributes.getValue(DIGEST_TYPES[i]); - if (value != null) { - encodedDigest = value; - break; - } + final MessageDigest md; + try { + md = MessageDigest.getInstance(DIGEST_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(DIGEST_ALGORITHM + " must be available", e); } - if (encodedDigest == null) { + final DigestInputStream dis = new DigestInputStream(new BufferedInputStream(fileIs), md); + try { + byte[] readBuffer = new byte[8192]; + while (dis.read(readBuffer, 0, readBuffer.length) != -1) { + // not using + } + } catch (IOException e) { + Slog.w(TAG, "Could not read manifest"); return null; + } finally { + IoUtils.closeQuietly(dis); } - final byte[] digest = Base64.decode(encodedDigest, Base64.DEFAULT); + final byte[] digest = md.digest(); return new ManifestDigest(digest); } diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 77ca7f6..fb539c5 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -154,7 +154,7 @@ public class PackageInfo implements Parcelable { /** * Flag for {@link #requestedPermissionsFlags}: the requested permission * is required for the application to run; the user can not optionally - * disable it. Currently all permissions are required. + * disable it. */ public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0; @@ -221,6 +221,9 @@ public class PackageInfo implements Parcelable { /** @hide */ public boolean requiredForAllUsers; + /** @hide */ + public String restrictedAccountType; + public PackageInfo() { } @@ -262,6 +265,7 @@ public class PackageInfo implements Parcelable { dest.writeTypedArray(reqFeatures, parcelableFlags); dest.writeInt(installLocation); dest.writeInt(requiredForAllUsers ? 1 : 0); + dest.writeString(restrictedAccountType); } public static final Parcelable.Creator<PackageInfo> CREATOR @@ -301,5 +305,6 @@ public class PackageInfo implements Parcelable { reqFeatures = source.createTypedArray(FeatureInfo.CREATOR); installLocation = source.readInt(); requiredForAllUsers = source.readInt() != 0; + restrictedAccountType = source.readString(); } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 0bea138..da15e3b 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -45,7 +45,7 @@ public abstract class PackageManager { /** * This exception is thrown when a given package, application, or component - * name can not be found. + * name cannot be found. */ public static class NameNotFoundException extends AndroidException { public NameNotFoundException() { @@ -267,7 +267,7 @@ public abstract class PackageManager { * user has explicitly disabled the application, regardless of what it has * specified in its manifest. Because this is due to the user's request, * they may re-enable it if desired through the appropriate system UI. This - * option currently <strong>can not</strong> be used with + * option currently <strong>cannot</strong> be used with * {@link #setComponentEnabledSetting(ComponentName, int, int)}. */ public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; @@ -1264,6 +1264,23 @@ public abstract class PackageManager { = "android.content.pm.extra.VERIFICATION_VERSION_CODE"; /** + * The action used to request that the user approve a permission request + * from the application. + * + * @hide + */ + public static final String ACTION_REQUEST_PERMISSION + = "android.content.pm.action.REQUEST_PERMISSION"; + + /** + * Extra field name for the list of permissions, which the user must approve. + * + * @hide + */ + public static final String EXTRA_REQUEST_PERMISSION_PERMISSION_LIST + = "android.content.pm.extra.PERMISSION_LIST"; + + /** * Retrieve overall information about an application package that is * installed on the system. * <p> @@ -1283,9 +1300,9 @@ public abstract class PackageManager { * package. If flag GET_UNINSTALLED_PACKAGES is set and if the * package is not found in the list of installed applications, the * package information is retrieved from the list of uninstalled - * applications(which includes installed applications as well as - * applications with data directory ie applications which had been - * deleted with DONT_DELTE_DATA flag set). + * applications (which includes installed applications as well as + * applications with data directory i.e. applications which had been + * deleted with {@code DONT_DELETE_DATA} flag set). * @see #GET_ACTIVITIES * @see #GET_GIDS * @see #GET_CONFIGURATIONS @@ -1326,7 +1343,7 @@ public abstract class PackageManager { * null if neither are found. * * <p>Throws {@link NameNotFoundException} if a package with the given - * name can not be found on the system. + * name cannot be found on the system. * * @param packageName The name of the package to inspect. * @@ -1341,7 +1358,7 @@ public abstract class PackageManager { * assigned to a package. * * <p>Throws {@link NameNotFoundException} if a package with the given - * name can not be found on the system. + * name cannot be found on the system. * * @param packageName The full name (i.e. com.google.apps.contacts) of the * desired package. @@ -1372,7 +1389,7 @@ public abstract class PackageManager { * Retrieve all of the information we know about a particular permission. * * <p>Throws {@link NameNotFoundException} if a permission with the given - * name can not be found on the system. + * name cannot be found on the system. * * @param name The fully qualified name (i.e. com.google.permission.LOGIN) * of the permission you are interested in. @@ -1408,7 +1425,7 @@ public abstract class PackageManager { * permissions. * * <p>Throws {@link NameNotFoundException} if a permission group with the given - * name can not be found on the system. + * name cannot be found on the system. * * @param name The fully qualified name (i.e. com.google.permission_group.APPS) * of the permission you are interested in. @@ -1437,7 +1454,7 @@ public abstract class PackageManager { * package/application. * * <p>Throws {@link NameNotFoundException} if an application with the given - * package name can not be found on the system. + * package name cannot be found on the system. * * @param packageName The full name (i.e. com.google.apps.contacts) of an * application. @@ -1453,7 +1470,7 @@ public abstract class PackageManager { * list of uninstalled applications(which includes * installed applications as well as applications * with data directory ie applications which had been - * deleted with DONT_DELTE_DATA flag set). + * deleted with {@code DONT_DELETE_DATA} flag set). * * @see #GET_META_DATA * @see #GET_SHARED_LIBRARY_FILES @@ -1467,7 +1484,7 @@ public abstract class PackageManager { * class. * * <p>Throws {@link NameNotFoundException} if an activity with the given - * class name can not be found on the system. + * class name cannot be found on the system. * * @param component The full component name (i.e. * com.google.apps.contacts/com.google.apps.contacts.ContactsList) of an Activity @@ -1490,7 +1507,7 @@ public abstract class PackageManager { * class. * * <p>Throws {@link NameNotFoundException} if a receiver with the given - * class name can not be found on the system. + * class name cannot be found on the system. * * @param component The full component name (i.e. * com.google.apps.calendar/com.google.apps.calendar.CalendarAlarm) of a Receiver @@ -1513,7 +1530,7 @@ public abstract class PackageManager { * class. * * <p>Throws {@link NameNotFoundException} if a service with the given - * class name can not be found on the system. + * class name cannot be found on the system. * * @param component The full component name (i.e. * com.google.apps.media/com.google.apps.media.BackgroundPlayback) of a Service @@ -1535,7 +1552,7 @@ public abstract class PackageManager { * provider class. * * <p>Throws {@link NameNotFoundException} if a provider with the given - * class name can not be found on the system. + * class name cannot be found on the system. * * @param component The full component name (i.e. * com.google.providers.media/com.google.providers.media.MediaProvider) of a @@ -1572,7 +1589,7 @@ public abstract class PackageManager { * installed on the device. In the unlikely case of there being no * installed packages, an empty list is returned. * If flag GET_UNINSTALLED_PACKAGES is set, a list of all - * applications including those deleted with DONT_DELETE_DATA + * applications including those deleted with {@code DONT_DELETE_DATA} * (partially installed apps with data directory) will be returned. * * @see #GET_ACTIVITIES @@ -1642,7 +1659,7 @@ public abstract class PackageManager { * installed on the device. In the unlikely case of there being no * installed packages, an empty list is returned. * If flag GET_UNINSTALLED_PACKAGES is set, a list of all - * applications including those deleted with DONT_DELETE_DATA + * applications including those deleted with {@code DONT_DELETE_DATA} * (partially installed apps with data directory) will be returned. * * @see #GET_ACTIVITIES @@ -1735,6 +1752,29 @@ public abstract class PackageManager { public abstract void removePermission(String name); /** + * Returns an {@link Intent} suitable for passing to {@code startActivityForResult} + * which prompts the user to grant {@code permissions} to this application. + * + * @throws NullPointerException if {@code permissions} is {@code null}. + * @throws IllegalArgumentException if {@code permissions} contains {@code null}. + */ + public Intent buildPermissionRequestIntent(String... permissions) { + if (permissions == null) { + throw new NullPointerException("permissions cannot be null"); + } + for (String permission : permissions) { + if (permission == null) { + throw new IllegalArgumentException("permissions cannot contain null"); + } + } + + Intent i = new Intent(ACTION_REQUEST_PERMISSION); + i.putExtra(EXTRA_REQUEST_PERMISSION_PERMISSION_LIST, permissions); + i.setPackage("com.android.packageinstaller"); + return i; + } + + /** * Grant a permission to an application which the application does not * already have. The permission must have been requested by the application, * but as an optional permission. If the application is not allowed to @@ -1847,7 +1887,7 @@ public abstract class PackageManager { /** * Return a List of all application packages that are installed on the * device. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all - * applications including those deleted with DONT_DELETE_DATA (partially + * applications including those deleted with {@code DONT_DELETE_DATA} (partially * installed apps with data directory) will be returned. * * @param flags Additional option flags. Use any combination of @@ -1858,7 +1898,7 @@ public abstract class PackageManager { * is installed on the device. In the unlikely case of there being * no installed applications, an empty list is returned. * If flag GET_UNINSTALLED_PACKAGES is set, a list of all - * applications including those deleted with DONT_DELETE_DATA + * applications including those deleted with {@code DONT_DELETE_DATA} * (partially installed apps with data directory) will be returned. * * @see #GET_META_DATA @@ -2164,7 +2204,7 @@ public abstract class PackageManager { * instrumentation class. * * <p>Throws {@link NameNotFoundException} if instrumentation with the - * given class name can not be found on the system. + * given class name cannot be found on the system. * * @param className The full name (i.e. * com.google.apps.contacts.InstrumentList) of an @@ -2201,8 +2241,8 @@ public abstract class PackageManager { * icon. * * @param packageName The name of the package that this icon is coming from. - * Can not be null. - * @param resid The resource identifier of the desired image. Can not be 0. + * Cannot be null. + * @param resid The resource identifier of the desired image. Cannot be 0. * @param appInfo Overall information about <var>packageName</var>. This * may be null, in which case the application information will be retrieved * for you if needed; if you already have this information around, it can @@ -2218,7 +2258,7 @@ public abstract class PackageManager { * Retrieve the icon associated with an activity. Given the full name of * an activity, retrieves the information about it and calls * {@link ComponentInfo#loadIcon ComponentInfo.loadIcon()} to return its icon. - * If the activity can not be found, NameNotFoundException is thrown. + * If the activity cannot be found, NameNotFoundException is thrown. * * @param activityName Name of the activity whose icon is to be retrieved. * @@ -2237,7 +2277,7 @@ public abstract class PackageManager { * set, this simply returns the result of * getActivityIcon(intent.getClassName()). Otherwise it resolves the intent's * component and returns the icon associated with the resolved component. - * If intent.getClassName() can not be found or the Intent can not be resolved + * If intent.getClassName() cannot be found or the Intent cannot be resolved * to a component, NameNotFoundException is thrown. * * @param intent The intent for which you would like to retrieve an icon. @@ -2276,7 +2316,7 @@ public abstract class PackageManager { /** * Retrieve the icon associated with an application. Given the name of the * application's package, retrieves the information about it and calls - * getApplicationIcon() to return its icon. If the application can not be + * getApplicationIcon() to return its icon. If the application cannot be * found, NameNotFoundException is thrown. * * @param packageName Name of the package whose application icon is to be @@ -2296,7 +2336,7 @@ public abstract class PackageManager { * Retrieve the logo associated with an activity. Given the full name of * an activity, retrieves the information about it and calls * {@link ComponentInfo#loadLogo ComponentInfo.loadLogo()} to return its logo. - * If the activity can not be found, NameNotFoundException is thrown. + * If the activity cannot be found, NameNotFoundException is thrown. * * @param activityName Name of the activity whose logo is to be retrieved. * @@ -2316,7 +2356,7 @@ public abstract class PackageManager { * set, this simply returns the result of * getActivityLogo(intent.getClassName()). Otherwise it resolves the intent's * component and returns the logo associated with the resolved component. - * If intent.getClassName() can not be found or the Intent can not be resolved + * If intent.getClassName() cannot be found or the Intent cannot be resolved * to a component, NameNotFoundException is thrown. * * @param intent The intent for which you would like to retrieve a logo. @@ -2348,7 +2388,7 @@ public abstract class PackageManager { /** * Retrieve the logo associated with an application. Given the name of the * application's package, retrieves the information about it and calls - * getApplicationLogo() to return its logo. If the application can not be + * getApplicationLogo() to return its logo. If the application cannot be * found, NameNotFoundException is thrown. * * @param packageName Name of the package whose application logo is to be @@ -2372,8 +2412,8 @@ public abstract class PackageManager { * labels and other text. * * @param packageName The name of the package that this text is coming from. - * Can not be null. - * @param resid The resource identifier of the desired text. Can not be 0. + * Cannot be null. + * @param resid The resource identifier of the desired text. Cannot be 0. * @param appInfo Overall information about <var>packageName</var>. This * may be null, in which case the application information will be retrieved * for you if needed; if you already have this information around, it can @@ -2390,8 +2430,8 @@ public abstract class PackageManager { * retrieve XML meta data. * * @param packageName The name of the package that this xml is coming from. - * Can not be null. - * @param resid The resource identifier of the desired xml. Can not be 0. + * Cannot be null. + * @param resid The resource identifier of the desired xml. Cannot be 0. * @param appInfo Overall information about <var>packageName</var>. This * may be null, in which case the application information will be retrieved * for you if needed; if you already have this information around, it can @@ -2409,7 +2449,7 @@ public abstract class PackageManager { * * @return Returns the label associated with this application, or null if * it could not be found for any reason. - * @param info The application to get the label of + * @param info The application to get the label of. */ public abstract CharSequence getApplicationLabel(ApplicationInfo info); @@ -2417,7 +2457,7 @@ public abstract class PackageManager { * Retrieve the resources associated with an activity. Given the full * name of an activity, retrieves the information about it and calls * getResources() to return its application's resources. If the activity - * can not be found, NameNotFoundException is thrown. + * cannot be found, NameNotFoundException is thrown. * * @param activityName Name of the activity whose resources are to be * retrieved. @@ -2448,7 +2488,7 @@ public abstract class PackageManager { * Retrieve the resources associated with an application. Given the full * package name of an application, retrieves the information about it and * calls getResources() to return its application's resources. If the - * appPackageName can not be found, NameNotFoundException is thrown. + * appPackageName cannot be found, NameNotFoundException is thrown. * * @param appPackageName Package name of the application whose resources * are to be retrieved. @@ -2617,7 +2657,7 @@ public abstract class PackageManager { * {@link PackageManager#VERIFICATION_REJECT}. * * @param id pending package identifier as passed via the - * {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra + * {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra. * @param verificationCode either {@link PackageManager#VERIFICATION_ALLOW} * or {@link PackageManager#VERIFICATION_REJECT}. * @throws SecurityException if the caller does not have the @@ -2638,7 +2678,7 @@ public abstract class PackageManager { * will have no effect. * * @param id pending package identifier as passed via the - * {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra + * {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra. * @param verificationCodeAtTimeout either * {@link PackageManager#VERIFICATION_ALLOW} or * {@link PackageManager#VERIFICATION_REJECT}. If @@ -2822,16 +2862,16 @@ public abstract class PackageManager { /** * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superceeded - * (and conflicts with) the modern activity-based preferences. + * approach to managing preferred activities, which has been superseded + * by (and conflicts with) the modern activity-based preferences. */ @Deprecated public abstract void addPackageToPreferred(String packageName); /** * @deprecated This function no longer does anything; it was an old - * approach to managing preferred activities, which has been superceeded - * (and conflicts with) the modern activity-based preferences. + * approach to managing preferred activities, which has been superseded + * by (and conflicts with) the modern activity-based preferences. */ @Deprecated public abstract void removePackageFromPreferred(String packageName); @@ -2870,7 +2910,7 @@ public abstract class PackageManager { /** * @deprecated This is a protected API that should not have been available * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this can not be directly modified. + * assigning preferred activities and this cannot be directly modified. * * Add a new preferred activity mapping to the system. This will be used * to automatically select the given activity component when @@ -2904,7 +2944,7 @@ public abstract class PackageManager { /** * @deprecated This is a protected API that should not have been available * to third party applications. It is the platform's responsibility for - * assigning preferred activities and this can not be directly modified. + * assigning preferred activities and this cannot be directly modified. * * Replaces an existing preferred activity mapping to the system, and if that were not present * adds a new preferred activity. This will be used diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 149b8e5..4835d05 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -24,7 +24,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.PatternMatcher; @@ -54,10 +53,9 @@ import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.jar.Manifest; +import java.util.zip.ZipEntry; import com.android.internal.util.XmlUtils; @@ -290,6 +288,7 @@ public class PackageParser { pi.applicationInfo = generateApplicationInfo(p, flags, state, userId); pi.installLocation = p.installLocation; pi.requiredForAllUsers = p.mRequiredForAllUsers; + pi.restrictedAccountType = p.mRestrictedAccountType; pi.firstInstallTime = firstInstallTime; pi.lastUpdateTime = lastUpdateTime; if ((flags&PackageManager.GET_GIDS) != 0) { @@ -566,6 +565,28 @@ public class PackageParser { return pkg; } + /** + * Gathers the {@link ManifestDigest} for {@code pkg} if it exists in the + * APK. If it successfully scanned the package and found the + * {@code AndroidManifest.xml}, {@code true} is returned. + */ + public boolean collectManifestDigest(Package pkg) { + try { + final JarFile jarFile = new JarFile(mArchiveSourcePath); + try { + final ZipEntry je = jarFile.getEntry(ANDROID_MANIFEST_FILENAME); + if (je != null) { + pkg.manifestDigest = ManifestDigest.fromInputStream(jarFile.getInputStream(je)); + } + } finally { + jarFile.close(); + } + return true; + } catch (IOException e) { + return false; + } + } + public boolean collectCertificates(Package pkg, int flags) { pkg.mSignatures = null; @@ -617,7 +638,6 @@ public class PackageParser { } } else { Enumeration<JarEntry> entries = jarFile.entries(); - final Manifest manifest = jarFile.getManifest(); while (entries.hasMoreElements()) { final JarEntry je = entries.nextElement(); if (je.isDirectory()) continue; @@ -628,8 +648,8 @@ public class PackageParser { continue; if (ANDROID_MANIFEST_FILENAME.equals(name)) { - final Attributes attributes = manifest.getAttributes(name); - pkg.manifestDigest = ManifestDigest.fromAttributes(attributes); + pkg.manifestDigest = + ManifestDigest.fromInputStream(jarFile.getInputStream(je)); } final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer); @@ -1023,16 +1043,14 @@ public class PackageParser { // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesPermission_name); - /* Not supporting optional permissions yet. boolean required = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true); - */ sa.recycle(); if (name != null && !pkg.requestedPermissions.contains(name)) { pkg.requestedPermissions.add(name.intern()); - pkg.requestedPermissionsRequired.add(Boolean.TRUE); + pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE); } XmlUtils.skipCurrentTag(parser); @@ -1766,6 +1784,11 @@ public class PackageParser { false)) { owner.mRequiredForAllUsers = true; } + String accountType = sa.getString(com.android.internal.R.styleable + .AndroidManifestApplication_restrictedAccountType); + if (accountType != null && accountType.length() > 0) { + owner.mRestrictedAccountType = accountType; + } } if (sa.getBoolean( @@ -3193,6 +3216,7 @@ public class PackageParser { } public final static class Package { + public String packageName; // For now we only support one application per package. @@ -3280,6 +3304,9 @@ public class PackageParser { /* An app that's required for all users and cannot be uninstalled for a user */ public boolean mRequiredForAllUsers; + /* The restricted account authenticator type that is used by this application */ + public String mRestrictedAccountType; + /** * Digest suitable for comparing whether this package's manifest is the * same as another. diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 3579977..dcd54fc 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -30,6 +30,8 @@ public class PackageUserState { public boolean installed; public int enabled; + public String lastDisableAppCaller; + public HashSet<String> disabledComponents; public HashSet<String> enabledComponents; @@ -43,6 +45,7 @@ public class PackageUserState { stopped = o.stopped; notLaunched = o.notLaunched; enabled = o.enabled; + lastDisableAppCaller = o.lastDisableAppCaller; disabledComponents = o.disabledComponents != null ? new HashSet<String>(o.disabledComponents) : null; enabledComponents = o.enabledComponents != null diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index c0d2fae..7f94794 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -572,7 +572,10 @@ public abstract class SensorManager { * are received faster. The value must be one of * {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI}, * {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST} - * or, the desired delay between events in microsecond. + * or, the desired delay between events in microseconds. + * Specifying the delay in microseconds only works from Android + * 2.3 (API level 9) onwards. For earlier releases, you must use + * one of the {@code SENSOR_DELAY_*} constants. * * @return <code>true</code> if the sensor is supported and successfully * enabled. @@ -604,7 +607,10 @@ public abstract class SensorManager { * are received faster. The value must be one of * {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI}, * {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}. - * or, the desired delay between events in microsecond. + * or, the desired delay between events in microseconds. + * Specifying the delay in microseconds only works from Android + * 2.3 (API level 9) onwards. For earlier releases, you must use + * one of the {@code SENSOR_DELAY_*} constants. * * @param handler * The {@link android.os.Handler Handler} the diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index b536490..0856e27 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -17,7 +17,6 @@ package android.hardware.usb; import android.os.ParcelFileDescriptor; -import android.util.Log; import java.io.FileDescriptor; @@ -119,10 +118,41 @@ public class UsbDeviceConnection { * @param timeout in milliseconds * @return length of data transferred (or zero) for success, * or negative value for failure + * + * @deprecated Use {@link #controlTransfer(int, int, int, int, byte[], int, int, int)} + * which accepts a buffer start index. */ + @Deprecated public int controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout) { - return native_control_request(requestType, request, value, index, buffer, length, timeout); + return controlTransfer(requestType, request, value, index, buffer, 0, length, timeout); + } + + /** + * Performs a control transaction on endpoint zero for this device. + * The direction of the transfer is determined by the request type. + * If requestType & {@link UsbConstants#USB_ENDPOINT_DIR_MASK} is + * {@link UsbConstants#USB_DIR_OUT}, then the transfer is a write, + * and if it is {@link UsbConstants#USB_DIR_IN}, then the transfer + * is a read. + * + * @param requestType request type for this transaction + * @param request request ID for this transaction + * @param value value field for this transaction + * @param index index field for this transaction + * @param buffer buffer for data portion of transaction, + * or null if no data needs to be sent or received + * @param start the index of the first byte in the buffer to send or receive + * @param length the length of the data to send or receive + * @param timeout in milliseconds + * @return length of data transferred (or zero) for success, + * or negative value for failure + */ + public int controlTransfer(int requestType, int request, int value, int index, + byte[] buffer, int start, int length, int timeout) { + checkBounds(buffer, start, length); + return native_control_request(requestType, request, value, index, + buffer, start, length, timeout); } /** @@ -130,14 +160,37 @@ public class UsbDeviceConnection { * The direction of the transfer is determined by the direction of the endpoint * * @param endpoint the endpoint for this transaction - * @param buffer buffer for data to send or receive, + * @param buffer buffer for data to send or receive * @param length the length of the data to send or receive * @param timeout in milliseconds * @return length of data transferred (or zero) for success, * or negative value for failure + * + * @deprecated Use {@link #bulkTransfer(UsbEndpoint, byte[], int, int, int)} + * which accepts a buffer start index. */ - public int bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout) { - return native_bulk_request(endpoint.getAddress(), buffer, length, timeout); + @Deprecated + public int bulkTransfer(UsbEndpoint endpoint, + byte[] buffer, int length, int timeout) { + return bulkTransfer(endpoint, buffer, 0, length, timeout); + } + + /** + * Performs a bulk transaction on the given endpoint. + * The direction of the transfer is determined by the direction of the endpoint + * + * @param endpoint the endpoint for this transaction + * @param buffer buffer for data to send or receive + * @param start the index of the first byte in the buffer to send or receive + * @param length the length of the data to send or receive + * @param timeout in milliseconds + * @return length of data transferred (or zero) for success, + * or negative value for failure + */ + public int bulkTransfer(UsbEndpoint endpoint, + byte[] buffer, int start, int length, int timeout) { + checkBounds(buffer, start, length); + return native_bulk_request(endpoint.getAddress(), buffer, start, length, timeout); } /** @@ -168,6 +221,13 @@ public class UsbDeviceConnection { return native_get_serial(); } + private static void checkBounds(byte[] buffer, int start, int length) { + final int bufferLength = (buffer != null ? buffer.length : 0); + if (start < 0 || start + length > bufferLength) { + throw new IllegalArgumentException("Buffer start or length out of bounds."); + } + } + private native boolean native_open(String deviceName, FileDescriptor pfd); private native void native_close(); private native int native_get_fd(); @@ -175,8 +235,9 @@ public class UsbDeviceConnection { private native boolean native_claim_interface(int interfaceID, boolean force); private native boolean native_release_interface(int interfaceID); private native int native_control_request(int requestType, int request, int value, - int index, byte[] buffer, int length, int timeout); - private native int native_bulk_request(int endpoint, byte[] buffer, int length, int timeout); + int index, byte[] buffer, int start, int length, int timeout); + private native int native_bulk_request(int endpoint, byte[] buffer, + int start, int length, int timeout); private native UsbRequest native_request_wait(); private native String native_get_serial(); } diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java index a554611..1165281 100644 --- a/core/java/android/net/BaseNetworkStateTracker.java +++ b/core/java/android/net/BaseNetworkStateTracker.java @@ -18,6 +18,7 @@ package android.net; import android.content.Context; import android.os.Handler; +import android.os.Messenger; import com.android.internal.util.Preconditions; @@ -165,4 +166,9 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { public void removeStackedLink(LinkProperties link) { mLinkProperties.removeStackedLink(link); } + + @Override + public void supplyMessenger(Messenger messenger) { + // not supported on this network + } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 3a04c27..4e4980d 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -23,6 +23,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Binder; import android.os.Build.VERSION_CODES; +import android.os.Messenger; import android.os.RemoteException; import android.provider.Settings; @@ -1280,4 +1281,17 @@ public class ConnectivityManager { } } + /** + * Supply the backend messenger for a network tracker + * + * @param type NetworkType to set + * @param messenger {@link Messenger} + * {@hide} + */ + public void supplyMessenger(int networkType, Messenger messenger) { + try { + mService.supplyMessenger(networkType, messenger); + } catch (RemoteException e) { + } + } } diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java index db8f0bc..15a81f3 100644 --- a/core/java/android/net/DummyDataStateTracker.java +++ b/core/java/android/net/DummyDataStateTracker.java @@ -19,6 +19,7 @@ package android.net; import android.content.Context; import android.os.Handler; import android.os.Message; +import android.os.Messenger; import android.util.Slog; /** @@ -213,6 +214,11 @@ public class DummyDataStateTracker implements NetworkStateTracker { mLinkProperties.removeStackedLink(link); } + @Override + public void supplyMessenger(Messenger messenger) { + // not supported on this network + } + static private void log(String s) { Slog.d(TAG, s); } diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index b744a47..27d5a58 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -22,6 +22,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Message; +import android.os.Messenger; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; @@ -417,4 +418,9 @@ public class EthernetDataTracker implements NetworkStateTracker { public void removeStackedLink(LinkProperties link) { mLinkProperties.removeStackedLink(link); } + + @Override + public void supplyMessenger(Messenger messenger) { + // not supported on this network + } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 056fa03..9e9b43d 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -22,6 +22,7 @@ import android.net.NetworkQuotaInfo; import android.net.NetworkState; import android.net.ProxyProperties; import android.os.IBinder; +import android.os.Messenger; import android.os.ParcelFileDescriptor; import com.android.internal.net.LegacyVpnInfo; @@ -126,4 +127,6 @@ interface IConnectivityManager boolean updateLockdownVpn(); void captivePortalCheckComplete(in NetworkInfo info); + + void supplyMessenger(int networkType, in Messenger messenger); } diff --git a/core/java/android/net/LinkCapabilities.java b/core/java/android/net/LinkCapabilities.java index eb9166f..fb444ea 100644 --- a/core/java/android/net/LinkCapabilities.java +++ b/core/java/android/net/LinkCapabilities.java @@ -314,8 +314,8 @@ public class LinkCapabilities implements Parcelable { sb.append(":\""); sb.append(entry.getValue()); sb.append("\""); - return mCapabilities.toString(); } + sb.append("}"); return sb.toString(); } diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index eedc372..75f8b59 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -290,7 +290,7 @@ public class LinkProperties implements Parcelable { } stacked += "] "; } - return ifaceName + linkAddresses + routes + dns + domainName + proxy + stacked; + return "{" + ifaceName + linkAddresses + routes + dns + domainName + proxy + stacked + "}"; } /** @@ -378,7 +378,7 @@ public class LinkProperties implements Parcelable { * @return {@code true} if both are identical, {@code false} otherwise. */ public boolean isIdenticalStackedLinks(LinkProperties target) { - if (!mStackedLinks.keys().equals(target.mStackedLinks.keys())) { + if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) { return false; } for (LinkProperties stacked : mStackedLinks.values()) { diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index faf739b..e85dbcd 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -74,7 +74,6 @@ public class MobileDataStateTracker implements NetworkStateTracker { private Handler mHandler; private AsyncChannel mDataConnectionTrackerAc; - private Messenger mMessenger; /** * Create a new MobileDataStateTracker @@ -103,7 +102,6 @@ public class MobileDataStateTracker implements NetworkStateTracker { IntentFilter filter = new IntentFilter(); filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); - filter.addAction(DctConstants.ACTION_DATA_CONNECTION_TRACKER_MESSENGER); mContext.registerReceiver(new MobileDataStateReceiver(), filter); mMobileDataState = PhoneConstants.DataState.DISCONNECTED; @@ -285,13 +283,6 @@ public class MobileDataStateTracker implements NetworkStateTracker { " broadcast" + reason == null ? "" : "(" + reason + ")"); } setDetailedState(DetailedState.FAILED, reason, apnName); - } else if (intent.getAction().equals(DctConstants - .ACTION_DATA_CONNECTION_TRACKER_MESSENGER)) { - if (VDBG) log(mApnType + " got ACTION_DATA_CONNECTION_TRACKER_MESSENGER"); - mMessenger = - intent.getParcelableExtra(DctConstants.EXTRA_MESSENGER); - AsyncChannel ac = new AsyncChannel(); - ac.connect(mContext, MobileDataStateTracker.this.mHandler, mMessenger); } else { if (DBG) log("Broadcast received: ignore " + intent.getAction()); } @@ -613,6 +604,12 @@ public class MobileDataStateTracker implements NetworkStateTracker { return new LinkCapabilities(mLinkCapabilities); } + public void supplyMessenger(Messenger messenger) { + if (VDBG) log(mApnType + " got supplyMessenger"); + AsyncChannel ac = new AsyncChannel(); + ac.connect(mContext, MobileDataStateTracker.this.mHandler, messenger); + } + private void log(String s) { Slog.d(TAG, mApnType + ": " + s); } diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index b22159c..cf77a1c 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -18,6 +18,9 @@ package android.net; import android.content.Context; import android.os.Handler; +import android.os.Messenger; + +import static com.android.internal.util.Protocol.BASE_NETWORK_STATE_TRACKER; /** * Interface provides the {@link com.android.server.ConnectivityService} @@ -48,25 +51,38 @@ public interface NetworkStateTracker { * msg.what = EVENT_STATE_CHANGED * msg.obj = NetworkInfo object */ - public static final int EVENT_STATE_CHANGED = 1; + public static final int EVENT_STATE_CHANGED = BASE_NETWORK_STATE_TRACKER; /** * msg.what = EVENT_CONFIGURATION_CHANGED * msg.obj = NetworkInfo object */ - public static final int EVENT_CONFIGURATION_CHANGED = 3; + public static final int EVENT_CONFIGURATION_CHANGED = BASE_NETWORK_STATE_TRACKER + 1; /** * msg.what = EVENT_RESTORE_DEFAULT_NETWORK * msg.obj = FeatureUser object */ - public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6; + public static final int EVENT_RESTORE_DEFAULT_NETWORK = BASE_NETWORK_STATE_TRACKER + 2; /** * msg.what = EVENT_NETWORK_SUBTYPE_CHANGED * msg.obj = NetworkInfo object */ - public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 7; + public static final int EVENT_NETWORK_SUBTYPE_CHANGED = BASE_NETWORK_STATE_TRACKER + 3; + + /** + * msg.what = EVENT_NETWORK_CONNECTED + * msg.obj = LinkProperties object + */ + public static final int EVENT_NETWORK_CONNECTED = BASE_NETWORK_STATE_TRACKER + 4; + + /** + * msg.what = EVENT_NETWORK_CONNECTION_DISCONNECTED + * msg.obj = LinkProperties object, same iface name + */ + public static final int EVENT_NETWORK_DISCONNECTED = BASE_NETWORK_STATE_TRACKER + 5; + /** * ------------------------------------------------------------- @@ -207,4 +223,10 @@ public interface NetworkStateTracker { * Informs the state tracker that a stacked interface has been removed. **/ public void removeStackedLink(LinkProperties link); + + /* + * Called once to setup async channel between this and + * the underlying network specific code. + */ + public void supplyMessenger(Messenger messenger); } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index cc6903d..4b022d9 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -20,6 +20,7 @@ import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; import android.os.Environment.UserEnvironment; +import android.os.StrictMode; import android.util.Log; import java.io.File; import java.io.IOException; @@ -2326,4 +2327,16 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { return this; } } + + /** + * If this is a {@code file://} Uri, it will be reported to + * {@link StrictMode}. + * + * @hide + */ + public void checkFileUriExposed(String location) { + if ("file".equals(getScheme())) { + StrictMode.onFileUriExposed(location); + } + } } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 4b83611..45524c8 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -152,16 +152,6 @@ interface INetworkManagementService boolean isTetheringStarted(); /** - * Start bluetooth reverse tethering services - */ - void startReverseTethering(in String iface); - - /** - * Stop currently running bluetooth reserse tethering services - */ - void stopReverseTethering(); - - /** * Tethers the specified interface */ void tetherInterface(String iface); diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index 02135bc..38f4d5e 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -50,7 +50,7 @@ import android.util.PrefixPrinter; * } * }</pre> */ -public class Looper { +public final class Looper { private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare(). @@ -223,7 +223,7 @@ public class Looper { * * @hide */ - public final int postSyncBarrier() { + public int postSyncBarrier() { return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis()); } @@ -238,7 +238,7 @@ public class Looper { * * @hide */ - public final void removeSyncBarrier(int token) { + public void removeSyncBarrier(int token) { mQueue.removeSyncBarrier(token); } diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index 222578a..e0d40c9 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -29,7 +29,7 @@ import java.util.ArrayList; * <p>You can retrieve the MessageQueue for the current thread with * {@link Looper#myQueue() Looper.myQueue()}. */ -public class MessageQueue { +public final class MessageQueue { // True if the message queue can be quit. private final boolean mQuitAllowed; @@ -78,7 +78,7 @@ public class MessageQueue { * * @param handler The IdleHandler to be added. */ - public final void addIdleHandler(IdleHandler handler) { + public void addIdleHandler(IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } @@ -94,7 +94,7 @@ public class MessageQueue { * * @param handler The IdleHandler to be removed. */ - public final void removeIdleHandler(IdleHandler handler) { + public void removeIdleHandler(IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } @@ -121,7 +121,7 @@ public class MessageQueue { } } - final Message next() { + Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; @@ -218,7 +218,7 @@ public class MessageQueue { } } - final void quit() { + void quit() { if (!mQuitAllowed) { throw new RuntimeException("Main thread not allowed to quit."); } @@ -232,7 +232,7 @@ public class MessageQueue { nativeWake(mPtr); } - final int enqueueSyncBarrier(long when) { + int enqueueSyncBarrier(long when) { // Enqueue a new sync barrier token. // We don't need to wake the queue because the purpose of a barrier is to stall it. synchronized (this) { @@ -259,7 +259,7 @@ public class MessageQueue { } } - final void removeSyncBarrier(int token) { + void removeSyncBarrier(int token) { // Remove a sync barrier token from the queue. // If the queue is no longer stalled by a barrier then wake it. final boolean needWake; @@ -288,7 +288,7 @@ public class MessageQueue { } } - final boolean enqueueMessage(Message msg, long when) { + boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } @@ -338,7 +338,7 @@ public class MessageQueue { return true; } - final boolean hasMessages(Handler h, int what, Object object) { + boolean hasMessages(Handler h, int what, Object object) { if (h == null) { return false; } @@ -355,7 +355,7 @@ public class MessageQueue { } } - final boolean hasMessages(Handler h, Runnable r, Object object) { + boolean hasMessages(Handler h, Runnable r, Object object) { if (h == null) { return false; } @@ -372,7 +372,7 @@ public class MessageQueue { } } - final void removeMessages(Handler h, int what, Object object) { + void removeMessages(Handler h, int what, Object object) { if (h == null) { return; } @@ -406,7 +406,7 @@ public class MessageQueue { } } - final void removeMessages(Handler h, Runnable r, Object object) { + void removeMessages(Handler h, Runnable r, Object object) { if (h == null || r == null) { return; } @@ -440,7 +440,7 @@ public class MessageQueue { } } - final void removeCallbacksAndMessages(Handler h, Object object) { + void removeCallbacksAndMessages(Handler h, Object object) { if (h == null) { return; } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index f682abe..d2943ab 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -203,10 +203,15 @@ public final class StrictMode { */ public static final int DETECT_VM_REGISTRATION_LEAKS = 0x2000; // for VmPolicy + /** + * @hide + */ + private static final int DETECT_VM_FILE_URI_EXPOSURE = 0x4000; // for VmPolicy + private static final int ALL_VM_DETECT_BITS = DETECT_VM_CURSOR_LEAKS | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_INSTANCE_LEAKS | - DETECT_VM_REGISTRATION_LEAKS; + DETECT_VM_REGISTRATION_LEAKS | DETECT_VM_FILE_URI_EXPOSURE; /** * @hide @@ -628,7 +633,8 @@ public final class StrictMode { */ public Builder detectAll() { return enable(DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_CURSOR_LEAKS - | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_REGISTRATION_LEAKS); + | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_REGISTRATION_LEAKS + | DETECT_VM_FILE_URI_EXPOSURE); } /** @@ -666,6 +672,16 @@ public final class StrictMode { } /** + * Detect when a {@code file://} {@link Uri} is exposed beyond this + * app. The receiving app may not have access to the sent path. + * Instead, when sharing files between apps, {@code content://} + * should be used with permission grants. + */ + public Builder detectFileUriExposure() { + return enable(DETECT_VM_FILE_URI_EXPOSURE); + } + + /** * Crashes the whole process on violation. This penalty runs at * the end of all enabled penalties so yo you'll still get * your logging or other violations before the process dies. @@ -1524,6 +1540,13 @@ public final class StrictMode { /** * @hide */ + public static boolean vmFileUriExposureEnabled() { + return (sVmPolicyMask & DETECT_VM_FILE_URI_EXPOSURE) != 0; + } + + /** + * @hide + */ public static void onSqliteObjectLeaked(String message, Throwable originStack) { onVmPolicyViolation(message, originStack); } @@ -1549,6 +1572,14 @@ public final class StrictMode { onVmPolicyViolation(null, originStack); } + /** + * @hide + */ + public static void onFileUriExposed(String location) { + final String message = "file:// Uri exposed through " + location; + onVmPolicyViolation(message, new Throwable(message)); + } + // Map from VM violation fingerprint to uptime millis. private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>(); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 622308f..b9b8f08 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -86,8 +86,50 @@ public class UserManager { * @see #setUserRestrictions(Bundle) * @see #getUserRestrictions() */ + public static final String DISALLOW_SHARE_LOCATION = "no_share_location"; + /** + * Key for user restrictions. Specifies if a user is disallowed from enabling the + * "Unknown Sources" setting, that allows installation of apps from unknown sources. + * The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring bluetooth. + * The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth"; + + + /** + * Key for user restrictions. Specifies if a user is disallowed from transferring files over + * USB. The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer"; + + private static UserManager sInstance = null; + + public synchronized static UserManager get(Context context) { + if (sInstance == null) { + sInstance = (UserManager) context.getSystemService(Context.USER_SERVICE); + } + return sInstance; + } + /** @hide */ public UserManager(Context context, IUserManager service) { mService = service; @@ -130,7 +172,7 @@ public class UserManager { /** * Used to determine whether the user making this call is subject to * teleportations. - * @return whether the user making this call is a goat + * @return whether the user making this call is a goat */ public boolean isUserAGoat() { return false; @@ -272,6 +314,16 @@ public class UserManager { } /** + * @hide + * Returns whether the current user has been disallowed from performing certain actions + * or setting certain settings. + * @param restrictionKey the string key representing the restriction + */ + public boolean hasUserRestriction(String restrictionKey) { + return getUserRestrictions().getBoolean(restrictionKey, false); + } + + /** * Return the serial number for a user. This is a device-unique * number assigned to that user; if the user is deleted and then a new * user created, the new users will not be given the same serial number. @@ -465,7 +517,7 @@ public class UserManager { * Returns the maximum number of users that can be created on this device. A return value * of 1 means that it is a single user device. * @hide - * @return a value greater than or equal to 1 + * @return a value greater than or equal to 1 */ public static int getMaxSupportedUsers() { // Don't allow multiple users on certain builds @@ -508,13 +560,6 @@ public class UserManager { return -1; } - /** - * Returns whether the current user is allowed to toggle location sharing settings. - * @hide - */ - public boolean isLocationSharingToggleAllowed() { - return !getUserRestrictions().getBoolean(DISALLOW_SHARE_LOCATION, false); - } /** * @hide diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index 2dd27f8..25af209 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -106,16 +106,13 @@ public final class CalendarContract { * {@link Activity#RESULT_OK} or {@link Activity#RESULT_CANCELED} to * acknowledge whether the action was handled or not. * - * The custom app should have an intent-filter like the following + * The custom app should have an intent filter like the following: * <pre> - * {@code - * <intent-filter> - * <action android:name="android.provider.calendar.action.HANDLE_CUSTOM_EVENT" /> - * <category android:name="android.intent.category.DEFAULT" /> - * <data android:mimeType="vnd.android.cursor.item/event" /> - * </intent-filter> - * } - * </pre> + * <intent-filter> + * <action android:name="android.provider.calendar.action.HANDLE_CUSTOM_EVENT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.item/event" /> + * </intent-filter></pre> * <p> * Input: {@link Intent#getData} has the event URI. The extra * {@link #EXTRA_EVENT_BEGIN_TIME} has the start time of the instance. The @@ -123,7 +120,7 @@ public final class CalendarContract { * {@link EventsColumns#CUSTOM_APP_URI}. * <p> * Output: {@link Activity#RESULT_OK} if this was handled; otherwise - * {@link Activity#RESULT_CANCELED} + * {@link Activity#RESULT_CANCELED}. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_HANDLE_CUSTOM_EVENT = diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 90bcf1c..66083c8 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1359,7 +1359,7 @@ public final class ContactsContract { * status definitions. Automatically computed as the highest presence of all * constituent raw contacts. The provider may choose not to store this value * in persistent storage. The expectation is that presence status will be - * updated on a regular basic.</td> + * updated on a regular basis.</td> * </tr> * <tr> * <td>String</td> @@ -4181,7 +4181,7 @@ public final class ContactsContract { * all IM rows. See {@link StatusUpdates} for individual status definitions. * The provider may choose not to store this value * in persistent storage. The expectation is that presence status will be - * updated on a regular basic. + * updated on a regular basis. * </td> * </tr> * <tr> diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f9ad8c0..03ee9eb 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5348,6 +5348,62 @@ public final class Settings { public static final String AUDIO_SAFE_VOLUME_STATE = "audio_safe_volume_state"; /** + * URL for tzinfo (time zone) updates + * @hide + */ + public static final String TZINFO_UPDATE_CONTENT_URL = "tzinfo_content_url"; + + /** + * URL for tzinfo (time zone) update metadata + * @hide + */ + public static final String TZINFO_UPDATE_METADATA_URL = "tzinfo_metadata_url"; + + /** + * URL for selinux (mandatory access control) updates + * @hide + */ + public static final String SELINUX_UPDATE_CONTENT_URL = "selinux_content_url"; + + /** + * URL for selinux (mandatory access control) update metadata + * @hide + */ + public static final String SELINUX_UPDATE_METADATA_URL = "selinux_metadata_url"; + + /** + * URL for sms short code updates + * @hide + */ + public static final String SMS_SHORT_CODES_UPDATE_CONTENT_URL = + "sms_short_codes_content_url"; + + /** + * URL for sms short code update metadata + * @hide + */ + public static final String SMS_SHORT_CODES_UPDATE_METADATA_URL = + "sms_short_codes_metadata_url"; + + /** + * URL for cert pinlist updates + * @hide + */ + public static final String CERT_PIN_UPDATE_CONTENT_URL = "cert_pin_content_url"; + + /** + * URL for cert pinlist updates + * @hide + */ + public static final String CERT_PIN_UPDATE_METADATA_URL = "cert_pin_metadata_url"; + + /** + * SELinux enforcement status. If 0, permissive; if 1, enforcing. + * @hide + */ + public static final String SELINUX_STATUS = "selinux_status"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java index a890d9b..e1cc90e 100644 --- a/core/java/android/security/IKeystoreService.java +++ b/core/java/android/security/IKeystoreService.java @@ -427,6 +427,41 @@ public interface IKeystoreService extends IInterface { } return _result; } + + @Override + public int is_hardware_backed() throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + mRemote.transact(Stub.TRANSACTION_is_hardware_backed, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + + @Override + public int clear_uid(long uid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + int _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeLong(uid); + mRemote.transact(Stub.TRANSACTION_clear_uid, _data, _reply, 0); + _reply.readException(); + _result = _reply.readInt(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } } private static final String DESCRIPTOR = "android.security.keystore"; @@ -452,6 +487,8 @@ public interface IKeystoreService extends IInterface { static final int TRANSACTION_ungrant = IBinder.FIRST_CALL_TRANSACTION + 18; static final int TRANSACTION_getmtime = IBinder.FIRST_CALL_TRANSACTION + 19; static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20; + static final int TRANSACTION_is_hardware_backed = IBinder.FIRST_CALL_TRANSACTION + 21; + static final int TRANSACTION_clear_uid = IBinder.FIRST_CALL_TRANSACTION + 22; /** * Cast an IBinder object into an IKeystoreService interface, generating @@ -539,4 +576,8 @@ public interface IKeystoreService extends IInterface { public int duplicate(String srcKey, int srcUid, String destKey, int destUid) throws RemoteException; + + public int is_hardware_backed() throws RemoteException; + + public int clear_uid(long uid) throws RemoteException; } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 9051285..1291279 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -340,7 +340,7 @@ public class StaticLayout extends Layout { w += widths[j - paraStart]; } - boolean isSpaceOrTab = c == CHAR_SPACE || c == CHAR_TAB; + boolean isSpaceOrTab = c == CHAR_SPACE || c == CHAR_TAB || c == CHAR_ZWSP; if (w <= width || isSpaceOrTab) { fitWidth = w; @@ -956,6 +956,7 @@ public class StaticLayout extends Layout { private static final char CHAR_SPACE = ' '; private static final char CHAR_SLASH = '/'; private static final char CHAR_HYPHEN = '-'; + private static final char CHAR_ZWSP = '\u200B'; private static final double EXTRA_ROUNDING = 0.5; diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index f813df3..c497e35 100644 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -31,6 +31,7 @@ import java.util.Locale; import java.util.TimeZone; import java.text.SimpleDateFormat; +import libcore.icu.ICU; import libcore.icu.LocaleData; /** @@ -43,6 +44,9 @@ import libcore.icu.LocaleData; * for both formatting and parsing dates. For the canonical documentation * of format strings, see {@link java.text.SimpleDateFormat}. * + * <p>In cases where the system does not provide a suitable pattern, + * this class offers the {@link #getBestDateTimePattern} method. + * * <p>The {@code format} methods in this class implement a subset of Unicode * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a> patterns. * The subset currently supported by this class includes the following format characters: @@ -164,6 +168,37 @@ public class DateFormat { } /** + * Returns the best possible localized form of the given skeleton for the given + * locale. A skeleton is similar to, and uses the same format characters as, a Unicode + * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a> + * pattern. + * + * <p>One difference is that order is irrelevant. For example, "MMMMd" will return + * "MMMM d" in the {@code en_US} locale, but "d. MMMM" in the {@code de_CH} locale. + * + * <p>Note also in that second example that the necessary punctuation for German was + * added. For the same input in {@code es_ES}, we'd have even more extra text: + * "d 'de' MMMM". + * + * <p>This method will automatically correct for grammatical necessity. Given the + * same "MMMMd" input, this method will return "d LLLL" in the {@code fa_IR} locale, + * where stand-alone months are necessary. Lengths are preserved where meaningful, + * so "Md" would give a different result to "MMMd", say, except in a locale such as + * {@code ja_JP} where there is only one length of month. + * + * <p>This method will only return patterns that are in CLDR, and is useful whenever + * you know what elements you want in your format string but don't want to make your + * code specific to any one locale. + * + * @param locale the locale into which the skeleton should be localized + * @param skeleton a skeleton as described above + * @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}. + */ + public static String getBestDateTimePattern(Locale locale, String skeleton) { + return ICU.getBestDateTimePattern(skeleton, locale.toString()); + } + + /** * Returns a {@link java.text.DateFormat} object that can format the time according * to the current locale and the user's 12-/24-hour clock preference. * @param context the application context diff --git a/core/java/android/util/AttributeSet.java b/core/java/android/util/AttributeSet.java index 470526c..74942ba 100644 --- a/core/java/android/util/AttributeSet.java +++ b/core/java/android/util/AttributeSet.java @@ -151,7 +151,7 @@ public interface AttributeSet { * Return the value of 'attribute' as a resource identifier. * * <p>Note that this is different than {@link #getAttributeNameResource} - * in that it returns a the value contained in this attribute as a + * in that it returns the value contained in this attribute as a * resource identifier (i.e., a value originally of the form * "@package:type/resource"); the other method returns a resource * identifier that identifies the name of the attribute. @@ -230,7 +230,7 @@ public interface AttributeSet { * Return the value of attribute at 'index' as a resource identifier. * * <p>Note that this is different than {@link #getAttributeNameResource} - * in that it returns a the value contained in this attribute as a + * in that it returns the value contained in this attribute as a * resource identifier (i.e., a value originally of the form * "@package:type/resource"); the other method returns a resource * identifier that identifies the name of the attribute. diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index a85a558..8ed4a86 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -175,6 +175,12 @@ interface IWindowManager int watchRotation(IRotationWatcher watcher); /** + * Remove a rotation watcher set using watchRotation. + * @hide + */ + void removeRotationWatcher(IRotationWatcher watcher); + + /** * Determine the preferred edge of the screen to pin the compact options menu against. * @return a Gravity value for the options menu panel * @hide diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 117c101..f5ee7ed 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -23,6 +23,8 @@ import android.os.MessageQueue; import android.util.Log; import android.util.SparseIntArray; +import java.lang.ref.WeakReference; + /** * Provides a low-level mechanism for an application to receive input events. * @hide @@ -42,7 +44,7 @@ public abstract class InputEventReceiver { // Map from InputEvent sequence numbers to dispatcher sequence numbers. private final SparseIntArray mSeqMap = new SparseIntArray(); - private static native int nativeInit(InputEventReceiver receiver, + private static native int nativeInit(WeakReference<InputEventReceiver> receiver, InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(int receiverPtr); private static native void nativeFinishInputEvent(int receiverPtr, int seq, boolean handled); @@ -65,7 +67,8 @@ public abstract class InputEventReceiver { mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); - mReceiverPtr = nativeInit(this, inputChannel, mMessageQueue); + mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this), + inputChannel, mMessageQueue); mCloseGuard.open("dispose"); } diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java index adf63fe..be6a623 100644 --- a/core/java/android/view/InputEventSender.java +++ b/core/java/android/view/InputEventSender.java @@ -22,6 +22,8 @@ import android.os.Looper; import android.os.MessageQueue; import android.util.Log; +import java.lang.ref.WeakReference; + /** * Provides a low-level mechanism for an application to send input events. * @hide @@ -38,7 +40,7 @@ public abstract class InputEventSender { private InputChannel mInputChannel; private MessageQueue mMessageQueue; - private static native int nativeInit(InputEventSender sender, + private static native int nativeInit(WeakReference<InputEventSender> sender, InputChannel inputChannel, MessageQueue messageQueue); private static native void nativeDispose(int senderPtr); private static native boolean nativeSendKeyEvent(int senderPtr, int seq, KeyEvent event); @@ -60,7 +62,8 @@ public abstract class InputEventSender { mInputChannel = inputChannel; mMessageQueue = looper.getQueue(); - mSenderPtr = nativeInit(this, inputChannel, mMessageQueue); + mSenderPtr = nativeInit(new WeakReference<InputEventSender>(this), + inputChannel, mMessageQueue); mCloseGuard.open("dispose"); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a5b3c8f..3b06da7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6889,7 +6889,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Adds the children of a given View for accessibility. Since some Views are * not important for accessibility the children for accessibility are not - * necessarily direct children of the riew, rather they are the first level of + * necessarily direct children of the view, rather they are the first level of * descendants important for accessibility. * * @param children The list of children for accessibility. @@ -9485,17 +9485,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is * completely transparent and 1 means the view is completely opaque.</p> * + * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant + * performance implications, especially for large views. It is best to use the alpha property + * sparingly and transiently, as in the case of fading animations.</p> + * + * <p>For a view with a frequently changing alpha, such as during a fading animation, it is + * strongly recommended for performance reasons to either override + * {@link #hasOverlappingRendering()} to return false if appropriate, or setting a + * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view.</p> + * * <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is - * responsible for applying the opacity itself. Otherwise, calling this method is - * equivalent to calling {@link #setLayerType(int, android.graphics.Paint)} and - * setting a hardware layer.</p> + * responsible for applying the opacity itself.</p> * - * <p>Note that setting alpha to a translucent value (0 < alpha < 1) may have - * performance implications. It is generally best to use the alpha property sparingly and - * transiently, as in the case of fading animations.</p> + * <p>Note that if the view is backed by a + * {@link #setLayerType(int, android.graphics.Paint) layer} and is associated with a + * {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an alpha value less than + * 1.0 will supercede the alpha of the layer paint.</p> * * @param alpha The opacity of the view. * + * @see #hasOverlappingRendering() * @see #setLayerType(int, android.graphics.Paint) * * @attr ref android.R.styleable#View_alpha @@ -12365,13 +12374,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * </ul> * * <p>If this view has an alpha value set to < 1.0 by calling - * {@link #setAlpha(float)}, the alpha value of the layer's paint is replaced by - * this view's alpha value. Calling {@link #setAlpha(float)} is therefore - * equivalent to setting a hardware layer on this view and providing a paint with - * the desired alpha value.</p> + * {@link #setAlpha(float)}, the alpha value of the layer's paint is superceded + * by this view's alpha value.</p> * - * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE disabled}, - * {@link #LAYER_TYPE_SOFTWARE software} and {@link #LAYER_TYPE_HARDWARE hardware} + * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE}, + * {@link #LAYER_TYPE_SOFTWARE} and {@link #LAYER_TYPE_HARDWARE} * for more information on when and how to use layers.</p> * * @param layerType The type of layer to use with this view, must be one of @@ -12441,11 +12448,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li> * </ul> * - * <p>If this view has an alpha value set to < 1.0 by calling - * {@link #setAlpha(float)}, the alpha value of the layer's paint is replaced by - * this view's alpha value. Calling {@link #setAlpha(float)} is therefore - * equivalent to setting a hardware layer on this view and providing a paint with - * the desired alpha value.</p> + * <p>If this view has an alpha value set to < 1.0 by calling {@link #setAlpha(float)}, the + * alpha value of the layer's paint is superceded by this view's alpha value.</p> * * @param paint The paint used to compose the layer. This argument is optional * and can be null. It is ignored when the layer type is diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 987ff78..40b6a08 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -1054,8 +1054,7 @@ public class ViewDebug { String categoryPrefix = property.category().length() != 0 ? property.category() + ":" : ""; - if (type == int.class) { - + if (type == int.class || type == byte.class) { if (property.resolveId() && context != null) { final int id = field.getInt(view); fieldValue = resolveId(context, id); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 98edeae..725502d 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -5882,13 +5882,40 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE; - // Layout direction is LTR by default - private int mLayoutDirection = LAYOUT_DIRECTION_LTR; + /** + * Bit 0: layout direction + * Bit 1: layout direction + * Bit 2: left margin undefined + * Bit 3: right margin undefined + * Bit 4: is RTL compatibility mode + * Bit 5: need resolution + * + * Bit 6 to 7 not used + * + * @hide + */ + @ViewDebug.ExportedProperty(category = "layout", flagMapping = { + @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK, + equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"), + @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK, + equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"), + @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK, + equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"), + @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK, + equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"), + @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK, + equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK") + }) + byte mMarginFlags; - private static int DEFAULT_MARGIN_RESOLVED = 0; + private static final int LAYOUT_DIRECTION_MASK = 0x00000003; + private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004; + private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008; + private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010; + private static final int NEED_RESOLUTION_MASK = 0x00000020; - private boolean mNeedResolution = false; - private boolean mIsRtlCompatibilityMode = true; + private static final int DEFAULT_MARGIN_RESOLVED = 0; + private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE; /** * Creates a new set of layout parameters. The values are extracted from @@ -5916,16 +5943,26 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } else { leftMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginLeft, - DEFAULT_MARGIN_RESOLVED); - topMargin = a.getDimensionPixelSize( - R.styleable.ViewGroup_MarginLayout_layout_marginTop, - DEFAULT_MARGIN_RESOLVED); + UNDEFINED_MARGIN); + if (leftMargin == UNDEFINED_MARGIN) { + mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK; + leftMargin = DEFAULT_MARGIN_RESOLVED; + } rightMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginRight, + UNDEFINED_MARGIN); + if (rightMargin == UNDEFINED_MARGIN) { + mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK; + rightMargin = DEFAULT_MARGIN_RESOLVED; + } + + topMargin = a.getDimensionPixelSize( + R.styleable.ViewGroup_MarginLayout_layout_marginTop, DEFAULT_MARGIN_RESOLVED); bottomMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginBottom, DEFAULT_MARGIN_RESOLVED); + startMargin = a.getDimensionPixelSize( R.styleable.ViewGroup_MarginLayout_layout_marginStart, DEFAULT_MARGIN_RELATIVE); @@ -5933,12 +5970,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager R.styleable.ViewGroup_MarginLayout_layout_marginEnd, DEFAULT_MARGIN_RELATIVE); - mNeedResolution = isMarginRelative(); + if (isMarginRelative()) { + mMarginFlags |= NEED_RESOLUTION_MASK; + } } final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport(); final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion; - mIsRtlCompatibilityMode = targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport; + if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) { + mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK; + } + + // Layout direction is LTR by default + mMarginFlags |= LAYOUT_DIRECTION_LTR; a.recycle(); } @@ -5949,8 +5993,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager public MarginLayoutParams(int width, int height) { super(width, height); - mNeedResolution = false; - mIsRtlCompatibilityMode = false; + mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK; + mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK; + + mMarginFlags &= ~NEED_RESOLUTION_MASK; + mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK; } /** @@ -5969,10 +6016,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager this.startMargin = source.startMargin; this.endMargin = source.endMargin; - this.mNeedResolution = source.mNeedResolution; - this.mIsRtlCompatibilityMode = source.mIsRtlCompatibilityMode; - - setLayoutDirection(source.mLayoutDirection); + this.mMarginFlags = source.mMarginFlags; } /** @@ -5981,8 +6025,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager public MarginLayoutParams(LayoutParams source) { super(source); - mNeedResolution = false; - mIsRtlCompatibilityMode = false; + mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK; + mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK; + + mMarginFlags &= ~NEED_RESOLUTION_MASK; + mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK; } /** @@ -6005,7 +6052,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager topMargin = top; rightMargin = right; bottomMargin = bottom; - mNeedResolution = isMarginRelative(); + mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK; + mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK; + if (isMarginRelative()) { + mMarginFlags |= NEED_RESOLUTION_MASK; + } else { + mMarginFlags &= ~NEED_RESOLUTION_MASK; + } } /** @@ -6031,7 +6084,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager topMargin = top; endMargin = end; bottomMargin = bottom; - mNeedResolution = true; + mMarginFlags |= NEED_RESOLUTION_MASK; } /** @@ -6043,7 +6096,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ public void setMarginStart(int start) { startMargin = start; - mNeedResolution = true; + mMarginFlags |= NEED_RESOLUTION_MASK; } /** @@ -6055,10 +6108,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ public int getMarginStart() { if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin; - if (mNeedResolution) { + if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) { doResolveMargins(); } - switch(mLayoutDirection) { + switch(mMarginFlags & LAYOUT_DIRECTION_MASK) { case View.LAYOUT_DIRECTION_RTL: return rightMargin; case View.LAYOUT_DIRECTION_LTR: @@ -6076,7 +6129,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ public void setMarginEnd(int end) { endMargin = end; - mNeedResolution = true; + mMarginFlags |= NEED_RESOLUTION_MASK; } /** @@ -6088,10 +6141,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ public int getMarginEnd() { if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin; - if (mNeedResolution) { + if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) { doResolveMargins(); } - switch(mLayoutDirection) { + switch(mMarginFlags & LAYOUT_DIRECTION_MASK) { case View.LAYOUT_DIRECTION_RTL: return leftMargin; case View.LAYOUT_DIRECTION_LTR: @@ -6121,9 +6174,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager public void setLayoutDirection(int layoutDirection) { if (layoutDirection != View.LAYOUT_DIRECTION_LTR && layoutDirection != View.LAYOUT_DIRECTION_RTL) return; - if (layoutDirection != this.mLayoutDirection) { - this.mLayoutDirection = layoutDirection; - this.mNeedResolution = isMarginRelative(); + if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) { + mMarginFlags &= ~LAYOUT_DIRECTION_MASK; + mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK); + if (isMarginRelative()) { + mMarginFlags |= NEED_RESOLUTION_MASK; + } else { + mMarginFlags &= ~NEED_RESOLUTION_MASK; + } } } @@ -6134,7 +6192,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @return the layout direction. */ public int getLayoutDirection() { - return mLayoutDirection; + return (mMarginFlags & LAYOUT_DIRECTION_MASK); } /** @@ -6147,39 +6205,53 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything // Will use the left and right margins if no relative margin is defined. - if (!isMarginRelative() || !mNeedResolution || mIsRtlCompatibilityMode) return; + if (!isMarginRelative() || + (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return; // Proceed with resolution doResolveMargins(); } private void doResolveMargins() { - // We have some relative margins (either the start one or the end one or both). So use - // them and override what has been defined for left and right margins. If either start - // or end margin is not defined, just set it to default "0". - switch(mLayoutDirection) { - case View.LAYOUT_DIRECTION_RTL: - leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ? - endMargin : DEFAULT_MARGIN_RESOLVED; - rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ? - startMargin : DEFAULT_MARGIN_RESOLVED; - break; - case View.LAYOUT_DIRECTION_LTR: - default: - leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ? - startMargin : DEFAULT_MARGIN_RESOLVED; - rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ? - endMargin : DEFAULT_MARGIN_RESOLVED; - break; + if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) { + // if left or right margins are not defined and if we have some start or end margin + // defined then use those start and end margins. + if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK + && startMargin > DEFAULT_MARGIN_RELATIVE) { + leftMargin = startMargin; + } + if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK + && endMargin > DEFAULT_MARGIN_RELATIVE) { + rightMargin = endMargin; + } + } else { + // We have some relative margins (either the start one or the end one or both). So use + // them and override what has been defined for left and right margins. If either start + // or end margin is not defined, just set it to default "0". + switch(mMarginFlags & LAYOUT_DIRECTION_MASK) { + case View.LAYOUT_DIRECTION_RTL: + leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ? + endMargin : DEFAULT_MARGIN_RESOLVED; + rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ? + startMargin : DEFAULT_MARGIN_RESOLVED; + break; + case View.LAYOUT_DIRECTION_LTR: + default: + leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ? + startMargin : DEFAULT_MARGIN_RESOLVED; + rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ? + endMargin : DEFAULT_MARGIN_RESOLVED; + break; + } } - mNeedResolution = false; + mMarginFlags &= ~NEED_RESOLUTION_MASK; } /** * @hide */ public boolean isLayoutRtl() { - return (mLayoutDirection == View.LAYOUT_DIRECTION_RTL); + return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL); } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7b34ce1..9387624 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -258,12 +258,12 @@ public final class ViewRootImpl implements ViewParent, QueuedInputEvent mPendingInputEventHead; QueuedInputEvent mPendingInputEventTail; int mPendingInputEventCount; - QueuedInputEvent mCurrentInputEventHead; - QueuedInputEvent mCurrentInputEventTail; - int mCurrentInputEventCount; + QueuedInputEvent mActiveInputEventHead; + QueuedInputEvent mActiveInputEventTail; + int mActiveInputEventCount; boolean mProcessInputEventsScheduled; String mPendingInputEventQueueLengthCounterName = "pq"; - String mCurrentInputEventQueueLengthCounterName = "cq"; + String mActiveInputEventQueueLengthCounterName = "aq"; boolean mWindowAttributesChanged = false; int mWindowAttributesChangesFlag = 0; @@ -661,7 +661,7 @@ public final class ViewRootImpl implements ViewParent, } mPendingInputEventQueueLengthCounterName = "pq:" + attrs.getTitle(); - mCurrentInputEventQueueLengthCounterName = "cq:" + attrs.getTitle(); + mActiveInputEventQueueLengthCounterName = "aq:" + attrs.getTitle(); } } } @@ -1425,6 +1425,8 @@ public final class ViewRootImpl implements ViewParent, final int surfaceGenerationId = mSurface.getGenerationId(); relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); + mWindowsAnimating |= + (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0; if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString() + " overscan=" + mPendingOverscanInsets.toShortString() @@ -4443,7 +4445,7 @@ public final class ViewRootImpl implements ViewParent, void doProcessInputEvents() { // Handle all of the available pending input events. Currently this will immediately // process all of the events it can until it encounters one that must go through the IME. - // After that it will continue adding events to the current input queue but will wait for a + // After that it will continue adding events to the active input queue but will wait for a // response from the IME, regardless of whether that particular event needs it or not, in // order to guarantee ordering consistency. This could be slightly improved by only // queueing events whose source has previously encountered something that needs to be @@ -4466,18 +4468,18 @@ public final class ViewRootImpl implements ViewParent, if (result == EVENT_HANDLED || result == EVENT_NOT_HANDLED) { finishInputEvent(q, result == EVENT_HANDLED); } else if (result == EVENT_PENDING_IME) { - enqueueCurrentInputEvent(q); + enqueueActiveInputEvent(q); } else { q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME; // If the IME decided not to handle this event, and we have no events already being // handled by the IME, go ahead and handle this one and then continue to the next // input event. Otherwise, queue it up and handle it after whatever in front of it // in the queue has been handled. - if (mCurrentInputEventHead == null) { + if (mActiveInputEventHead == null) { result = deliverInputEventPostIme(q); finishInputEvent(q, result == EVENT_HANDLED); } else { - enqueueCurrentInputEvent(q); + enqueueActiveInputEvent(q); } } } @@ -4490,29 +4492,54 @@ public final class ViewRootImpl implements ViewParent, } } - private void enqueueCurrentInputEvent(QueuedInputEvent q) { - if (mCurrentInputEventHead == null) { - mCurrentInputEventHead = q; - mCurrentInputEventTail = q; + private void enqueueActiveInputEvent(QueuedInputEvent q) { + if (mActiveInputEventHead == null) { + mActiveInputEventHead = q; + mActiveInputEventTail = q; } else { - mCurrentInputEventTail.mNext = q; - mCurrentInputEventTail = q; + mActiveInputEventTail.mNext = q; + mActiveInputEventTail = q; } - mCurrentInputEventCount += 1; - Trace.traceCounter(Trace.TRACE_TAG_INPUT, mCurrentInputEventQueueLengthCounterName, - mCurrentInputEventCount); + mActiveInputEventCount += 1; + Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName, + mActiveInputEventCount); } - private QueuedInputEvent dequeueCurrentInputEvent() { - QueuedInputEvent q = mCurrentInputEventHead; - mCurrentInputEventHead = q.mNext; - if (mCurrentInputEventHead == null) { - mCurrentInputEventTail = null; + private QueuedInputEvent dequeueActiveInputEvent() { + return dequeueActiveInputEvent(mActiveInputEventHead); + } + + + private QueuedInputEvent dequeueActiveInputEvent(QueuedInputEvent q) { + QueuedInputEvent curr = mActiveInputEventHead; + QueuedInputEvent prev = null; + while (curr != null && curr != q) { + prev = curr; + curr = curr.mNext; + } + if (curr != null) { + if (mActiveInputEventHead == curr) { + mActiveInputEventHead = curr.mNext; + } else { + prev.mNext = curr.mNext; + } + if (mActiveInputEventTail == curr) { + mActiveInputEventTail = prev; + } + curr.mNext = null; + + mActiveInputEventCount -= 1; + Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName, + mActiveInputEventCount); + } + return curr; + } + + private QueuedInputEvent findActiveInputEvent(int seq) { + QueuedInputEvent q = mActiveInputEventHead; + while (q != null && q.mEvent.getSequenceNumber() != seq) { + q = q.mNext; } - q.mNext = null; - mCurrentInputEventCount -= 1; - Trace.traceCounter(Trace.TRACE_TAG_INPUT, mCurrentInputEventQueueLengthCounterName, - mCurrentInputEventCount); return q; } @@ -4532,36 +4559,35 @@ public final class ViewRootImpl implements ViewParent, } void handleImeFinishedEvent(int seq, boolean handled) { - QueuedInputEvent q = mCurrentInputEventHead; - if (q != null && q.mEvent.getSequenceNumber() == seq) { - dequeueCurrentInputEvent(); + QueuedInputEvent q = findActiveInputEvent(seq); + if (q != null) { if (DEBUG_IMF) { Log.v(TAG, "IME finished event: seq=" + seq + " handled=" + handled + " event=" + q); } - if (!handled) { + if (handled) { + dequeueActiveInputEvent(q); + finishInputEvent(q, true); + } else { + q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME; + } + + + // Flush all of the input events that are no longer waiting on the IME + while (mActiveInputEventHead != null && (mActiveInputEventHead.mFlags & + QueuedInputEvent.FLAG_DELIVER_POST_IME) != 0) { + q = dequeueActiveInputEvent(); // If the window doesn't currently have input focus, then drop // this event. This could be an event that came back from the // IME dispatch but the window has lost focus in the meantime. + handled = false; if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) { Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent); } else { - final int result = deliverInputEventPostIme(q); - if (result == EVENT_HANDLED) { - handled = true; - } + handled = (deliverInputEventPostIme(q) == EVENT_HANDLED); } - } - - finishInputEvent(q, handled); - - // Flush all of the input events that are no longer waiting on the IME - while (mCurrentInputEventHead != null && (mCurrentInputEventHead.mFlags & - QueuedInputEvent.FLAG_DELIVER_POST_IME) != 0) { - q = dequeueCurrentInputEvent(); - final int result = deliverInputEventPostIme(q); - finishInputEvent(q, result == EVENT_HANDLED); + finishInputEvent(q, handled); } } else { if (DEBUG_IMF) { diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index e711b94..9c00b7f 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -468,7 +468,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie // Force reloading the image resource sc.icon.setImageDrawable(null); sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes); - if (sc.streamType == AudioManager.STREAM_RING && + if (((sc.streamType == AudioManager.STREAM_RING) || + (sc.streamType == AudioManager.STREAM_NOTIFICATION)) && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) { sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate); } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 4207832..7f9969c 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -372,6 +372,7 @@ public final class InputMethodManager { return; } + flushPendingEventsLocked(); mCurMethod = res.method; if (mCurChannel != null) { mCurChannel.dispose(); @@ -718,6 +719,7 @@ public final class InputMethodManager { */ void clearBindingLocked() { clearConnectionLocked(); + flushPendingEventsLocked(); mBindSequence = -1; mCurId = null; mCurMethod = null; @@ -1712,6 +1714,30 @@ public final class InputMethodManager { } } + private void flushPendingEventsLocked() { + mH.removeMessages(MSG_EVENT_TIMEOUT); + PendingEvent curr, prev, next; + curr = mFirstPendingEvent; + prev = null; + while (curr != null) { + next = curr.mNext; + curr.mNext = prev; + prev = curr; + curr = next; + } + curr = prev; + prev = null; + while (curr != null) { + Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, curr.mSeq, 0, curr); + msg.setAsynchronous(true); + mH.sendMessage(msg); + next = curr.mNext; + curr.mNext = prev; + prev = curr; + curr = next; + } + } + public void showInputMethodPicker() { synchronized (mH) { showInputMethodPickerLocked(); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 79fc51e..83e2e79 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -34,6 +34,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.StrictMode; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; @@ -2263,8 +2264,13 @@ public class RemoteViews implements Parcelable, Filter { * @param value The value to pass to the method. */ public void setUri(int viewId, String methodName, Uri value) { - // Resolve any filesystem path before sending remotely - value = value.getCanonicalUri(); + if (value != null) { + // Resolve any filesystem path before sending remotely + value = value.getCanonicalUri(); + if (StrictMode.vmFileUriExposureEnabled()) { + value.checkFileUriExposed("RemoteViews.setUri()"); + } + } addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value)); } diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index 91b109e..b380403 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -52,5 +52,6 @@ public class Protocol { public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000; public static final int BASE_DNS_PINGER = 0x00050000; public static final int BASE_NSD_MANAGER = 0x00060000; + public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000; //TODO: define all used protocols } diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java index c33f038..ca797eb 100644 --- a/core/java/com/android/internal/widget/TransportControlView.java +++ b/core/java/com/android/internal/widget/TransportControlView.java @@ -143,7 +143,8 @@ public class TransportControlView extends FrameLayout implements OnClickListener mLocalHandler = new WeakReference<Handler>(handler); } - public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) { + public void setPlaybackState(int generationId, int state, long stateChangeTimeMs, + long currentPosMs, float speed) { Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget(); @@ -157,7 +158,7 @@ public class TransportControlView extends FrameLayout implements OnClickListener } } - public void setTransportControlFlags(int generationId, int flags) { + public void setTransportControlInfo(int generationId, int flags, int posCapabilities) { Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags) diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 90161fd..5454c08 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -383,7 +383,8 @@ public: return descent - ascent + leading; } - static jfloat measureText_CII(JNIEnv* env, jobject jpaint, jcharArray text, int index, int count) { + static jfloat measureText_CIII(JNIEnv* env, jobject jpaint, jcharArray text, int index, int count, + jint bidiFlags) { NPE_CHECK_RETURN_ZERO(env, jpaint); NPE_CHECK_RETURN_ZERO(env, text); @@ -401,13 +402,14 @@ public: jfloat result = 0; TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength, - paint->getFlags(), NULL /* dont need all advances */, &result); + bidiFlags, NULL /* dont need all advances */, &result); env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); return result; } - static jfloat measureText_StringII(JNIEnv* env, jobject jpaint, jstring text, int start, int end) { + static jfloat measureText_StringIII(JNIEnv* env, jobject jpaint, jstring text, int start, int end, + jint bidiFlags) { NPE_CHECK_RETURN_ZERO(env, jpaint); NPE_CHECK_RETURN_ZERO(env, text); @@ -426,13 +428,13 @@ public: jfloat width = 0; TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength, - paint->getFlags(), NULL /* dont need all advances */, &width); + bidiFlags, NULL /* dont need all advances */, &width); env->ReleaseStringChars(text, textArray); return width; } - static jfloat measureText_String(JNIEnv* env, jobject jpaint, jstring text) { + static jfloat measureText_StringI(JNIEnv* env, jobject jpaint, jstring text, jint bidiFlags) { NPE_CHECK_RETURN_ZERO(env, jpaint); NPE_CHECK_RETURN_ZERO(env, text); @@ -446,13 +448,14 @@ public: jfloat width = 0; TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength, - paint->getFlags(), NULL /* dont need all advances */, &width); + bidiFlags, NULL /* dont need all advances */, &width); env->ReleaseStringChars(text, textArray); return width; } - static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths) { + static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths, + jint bidiFlags) { NPE_CHECK_RETURN_ZERO(env, paint); NPE_CHECK_RETURN_ZERO(env, text); @@ -473,23 +476,24 @@ public: jfloat* widthsArray = autoWidths.ptr(); TextLayout::getTextRunAdvances(paint, text, 0, count, count, - paint->getFlags(), widthsArray, NULL /* dont need totalAdvance */); + bidiFlags, widthsArray, NULL /* dont need totalAdvance */); return count; } - static int getTextWidths___CII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloatArray widths) { + static int getTextWidths___CIII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, + int index, int count, jint bidiFlags, jfloatArray widths) { const jchar* textArray = env->GetCharArrayElements(text, NULL); - count = dotextwidths(env, paint, textArray + index, count, widths); + count = dotextwidths(env, paint, textArray + index, count, widths, bidiFlags); env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); return count; } - static int getTextWidths__StringII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, - int start, int end, jfloatArray widths) { + static int getTextWidths__StringIII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, + int start, int end, jint bidiFlags, jfloatArray widths) { const jchar* textArray = env->GetStringChars(text, NULL); - int count = dotextwidths(env, paint, textArray + start, end - start, widths); + int count = dotextwidths(env, paint, textArray + start, end - start, widths, bidiFlags); env->ReleaseStringChars(text, textArray); return count; } @@ -685,10 +689,10 @@ public: } static int breakText(JNIEnv* env, SkPaint& paint, const jchar text[], - int count, float maxWidth, jfloatArray jmeasured, + int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured, SkPaint::TextBufferDirection tbd) { sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint, - text, 0, count, count, paint.getFlags()); + text, 0, count, count, bidiFlags); if (value == NULL) { return 0; } @@ -706,7 +710,7 @@ public: } static int breakTextC(JNIEnv* env, jobject jpaint, jcharArray jtext, - int index, int count, float maxWidth, jfloatArray jmeasuredWidth) { + int index, int count, float maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) { NPE_CHECK_RETURN_ZERO(env, jpaint); NPE_CHECK_RETURN_ZERO(env, jtext); @@ -727,14 +731,14 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); const jchar* text = env->GetCharArrayElements(jtext, NULL); count = breakText(env, *paint, text + index, count, maxWidth, - jmeasuredWidth, tbd); + bidiFlags, jmeasuredWidth, tbd); env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text), JNI_ABORT); return count; } static int breakTextS(JNIEnv* env, jobject jpaint, jstring jtext, - bool forwards, float maxWidth, jfloatArray jmeasuredWidth) { + bool forwards, float maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) { NPE_CHECK_RETURN_ZERO(env, jpaint); NPE_CHECK_RETURN_ZERO(env, jtext); @@ -745,22 +749,20 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); int count = env->GetStringLength(jtext); const jchar* text = env->GetStringChars(jtext, NULL); - count = breakText(env, *paint, text, count, maxWidth, - jmeasuredWidth, tbd); + count = breakText(env, *paint, text, count, maxWidth, bidiFlags, jmeasuredWidth, tbd); env->ReleaseStringChars(jtext, text); return count; } static void doTextBounds(JNIEnv* env, const jchar* text, int count, - jobject bounds, const SkPaint& paint) - { + jobject bounds, const SkPaint& paint, jint bidiFlags) { SkRect r; r.set(0,0,0,0); SkIRect ir; sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint, - text, 0, count, count, paint.getFlags()); + text, 0, count, count, bidiFlags); if (value == NULL) { return; } @@ -770,18 +772,16 @@ public: } static void getStringBounds(JNIEnv* env, jobject, const SkPaint* paint, - jstring text, int start, int end, jobject bounds) - { + jstring text, int start, int end, jint bidiFlags, jobject bounds) { const jchar* textArray = env->GetStringChars(text, NULL); - doTextBounds(env, textArray + start, end - start, bounds, *paint); + doTextBounds(env, textArray + start, end - start, bounds, *paint, bidiFlags); env->ReleaseStringChars(text, textArray); } static void getCharArrayBounds(JNIEnv* env, jobject, const SkPaint* paint, - jcharArray text, int index, int count, jobject bounds) - { + jcharArray text, int index, int count, jint bidiFlags, jobject bounds) { const jchar* textArray = env->GetCharArrayElements(text, NULL); - doTextBounds(env, textArray + index, count, bounds, *paint); + doTextBounds(env, textArray + index, count, bounds, *paint, bidiFlags); env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); } @@ -841,13 +841,13 @@ static JNINativeMethod methods[] = { {"descent","()F", (void*) SkPaintGlue::descent}, {"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", (void*)SkPaintGlue::getFontMetrics}, {"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", (void*)SkPaintGlue::getFontMetricsInt}, - {"native_measureText","([CII)F", (void*) SkPaintGlue::measureText_CII}, - {"native_measureText","(Ljava/lang/String;)F", (void*) SkPaintGlue::measureText_String}, - {"native_measureText","(Ljava/lang/String;II)F", (void*) SkPaintGlue::measureText_StringII}, - {"native_breakText","([CIIF[F)I", (void*) SkPaintGlue::breakTextC}, - {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS}, - {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F}, - {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F}, + {"native_measureText","([CIII)F", (void*) SkPaintGlue::measureText_CIII}, + {"native_measureText","(Ljava/lang/String;I)F", (void*) SkPaintGlue::measureText_StringI}, + {"native_measureText","(Ljava/lang/String;III)F", (void*) SkPaintGlue::measureText_StringIII}, + {"native_breakText","([CIIFI[F)I", (void*) SkPaintGlue::breakTextC}, + {"native_breakText","(Ljava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS}, + {"native_getTextWidths","(I[CIII[F)I", (void*) SkPaintGlue::getTextWidths___CIII_F}, + {"native_getTextWidths","(ILjava/lang/String;III[F)I", (void*) SkPaintGlue::getTextWidths__StringIII_F}, {"native_getTextRunAdvances","(I[CIIIII[FI)F", (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI}, {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F", @@ -861,9 +861,9 @@ static JNINativeMethod methods[] = { (void*) SkPaintGlue::getTextRunCursor__String}, {"native_getTextPath","(II[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C}, {"native_getTextPath","(IILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String}, - {"nativeGetStringBounds", "(ILjava/lang/String;IILandroid/graphics/Rect;)V", + {"nativeGetStringBounds", "(ILjava/lang/String;IIILandroid/graphics/Rect;)V", (void*) SkPaintGlue::getStringBounds }, - {"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V", + {"nativeGetCharArrayBounds", "(I[CIIILandroid/graphics/Rect;)V", (void*) SkPaintGlue::getCharArrayBounds }, {"nSetShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer} }; diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp index 923781e..cea5bbf 100644 --- a/core/jni/android_hardware_UsbDeviceConnection.cpp +++ b/core/jni/android_hardware_UsbDeviceConnection.cpp @@ -142,7 +142,7 @@ android_hardware_UsbDeviceConnection_release_interface(JNIEnv *env, jobject thiz static jint android_hardware_UsbDeviceConnection_control_request(JNIEnv *env, jobject thiz, jint requestType, jint request, jint value, jint index, - jbyteArray buffer, jint length, jint timeout) + jbyteArray buffer, jint start, jint length, jint timeout) { struct usb_device* device = get_device_from_object(env, thiz); if (!device) { @@ -152,25 +152,22 @@ android_hardware_UsbDeviceConnection_control_request(JNIEnv *env, jobject thiz, jbyte* bufferBytes = NULL; if (buffer) { - if (env->GetArrayLength(buffer) < length) { - jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); - return -1; - } - bufferBytes = env->GetByteArrayElements(buffer, 0); + bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL); } jint result = usb_device_control_transfer(device, requestType, request, - value, index, bufferBytes, length, timeout); + value, index, bufferBytes + start, length, timeout); - if (bufferBytes) - env->ReleaseByteArrayElements(buffer, bufferBytes, 0); + if (bufferBytes) { + env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0); + } return result; } static jint android_hardware_UsbDeviceConnection_bulk_request(JNIEnv *env, jobject thiz, - jint endpoint, jbyteArray buffer, jint length, jint timeout) + jint endpoint, jbyteArray buffer, jint start, jint length, jint timeout) { struct usb_device* device = get_device_from_object(env, thiz); if (!device) { @@ -180,17 +177,14 @@ android_hardware_UsbDeviceConnection_bulk_request(JNIEnv *env, jobject thiz, jbyte* bufferBytes = NULL; if (buffer) { - if (env->GetArrayLength(buffer) < length) { - jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); - return -1; - } - bufferBytes = env->GetByteArrayElements(buffer, 0); + bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL); } - jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes, length, timeout); + jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes + start, length, timeout); - if (bufferBytes) - env->ReleaseByteArrayElements(buffer, bufferBytes, 0); + if (bufferBytes) { + env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0); + } return result; } @@ -235,9 +229,9 @@ static JNINativeMethod method_table[] = { {"native_get_desc", "()[B", (void *)android_hardware_UsbDeviceConnection_get_desc}, {"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface}, {"native_release_interface","(I)Z", (void *)android_hardware_UsbDeviceConnection_release_interface}, - {"native_control_request", "(IIII[BII)I", + {"native_control_request", "(IIII[BIII)I", (void *)android_hardware_UsbDeviceConnection_control_request}, - {"native_bulk_request", "(I[BII)I", + {"native_bulk_request", "(I[BIII)I", (void *)android_hardware_UsbDeviceConnection_bulk_request}, {"native_request_wait", "()Landroid/hardware/usb/UsbRequest;", (void *)android_hardware_UsbDeviceConnection_request_wait}, diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index a7eede2..8766cf9 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -93,14 +93,6 @@ static struct debug_offsets_t // ---------------------------------------------------------------------------- -static struct weakreference_offsets_t -{ - // Class state. - jclass mClass; - jmethodID mGet; - -} gWeakReferenceOffsets; - static struct error_offsets_t { jclass mClass; @@ -570,7 +562,7 @@ jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) // Someone else's... do we know about it? jobject object = (jobject)val->findObject(&gBinderProxyOffsets); if (object != NULL) { - jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet); + jobject res = jniGetReferent(env, object); if (res != NULL) { ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res); return res; @@ -1211,13 +1203,6 @@ static int int_register_android_os_BinderProxy(JNIEnv* env) { jclass clazz; - clazz = env->FindClass("java/lang/ref/WeakReference"); - LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.ref.WeakReference"); - gWeakReferenceOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gWeakReferenceOffsets.mGet - = env->GetMethodID(clazz, "get", "()Ljava/lang/Object;"); - assert(gWeakReferenceOffsets.mGet); - clazz = env->FindClass("java/lang/Error"); LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.Error"); gErrorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 198814a..c350521 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -34,6 +34,8 @@ #include "android_view_KeyEvent.h" #include "android_view_MotionEvent.h" +#include <ScopedLocalRef.h> + namespace android { static struct { @@ -47,7 +49,7 @@ static struct { class NativeInputEventReceiver : public LooperCallback { public: NativeInputEventReceiver(JNIEnv* env, - jobject receiverObj, const sp<InputChannel>& inputChannel, + jobject receiverWeak, const sp<InputChannel>& inputChannel, const sp<MessageQueue>& messageQueue); status_t initialize(); @@ -59,7 +61,7 @@ protected: virtual ~NativeInputEventReceiver(); private: - jobject mReceiverObjGlobal; + jobject mReceiverWeakGlobal; InputConsumer mInputConsumer; sp<MessageQueue> mMessageQueue; PreallocatedInputEventFactory mInputEventFactory; @@ -74,9 +76,9 @@ private: NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, - jobject receiverObj, const sp<InputChannel>& inputChannel, + jobject receiverWeak, const sp<InputChannel>& inputChannel, const sp<MessageQueue>& messageQueue) : - mReceiverObjGlobal(env->NewGlobalRef(receiverObj)), + mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mInputConsumer(inputChannel), mMessageQueue(messageQueue), mBatchedInputEventPending(false) { #if DEBUG_DISPATCH_CYCLE @@ -86,7 +88,7 @@ NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env, NativeInputEventReceiver::~NativeInputEventReceiver() { JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->DeleteGlobalRef(mReceiverObjGlobal); + env->DeleteGlobalRef(mReceiverWeakGlobal); } status_t NativeInputEventReceiver::initialize() { @@ -151,6 +153,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, mBatchedInputEventPending = false; } + ScopedLocalRef<jobject> receiverObj(env, NULL); bool skipCallbacks = false; for (;;) { uint32_t seq; @@ -162,12 +165,21 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, if (!skipCallbacks && !mBatchedInputEventPending && mInputConsumer.hasPendingBatch()) { // There is a pending batch. Come back later. + if (!receiverObj.get()) { + receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal)); + if (!receiverObj.get()) { + ALOGW("channel '%s' ~ Receiver object was finalized " + "without being disposed.", getInputChannelName()); + return DEAD_OBJECT; + } + } + mBatchedInputEventPending = true; #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Dispatching batched input event pending notification.", getInputChannelName()); #endif - env->CallVoidMethod(mReceiverObjGlobal, + env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchBatchedInputEventPending); if (env->ExceptionCheck()) { ALOGE("Exception dispatching batched input events."); @@ -183,6 +195,15 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, assert(inputEvent); if (!skipCallbacks) { + if (!receiverObj.get()) { + receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal)); + if (!receiverObj.get()) { + ALOGW("channel '%s' ~ Receiver object was finalized " + "without being disposed.", getInputChannelName()); + return DEAD_OBJECT; + } + } + jobject inputEventObj; switch (inputEvent->getType()) { case AINPUT_EVENT_TYPE_KEY: @@ -210,12 +231,13 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName()); #endif - env->CallVoidMethod(mReceiverObjGlobal, + env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); if (env->ExceptionCheck()) { ALOGE("Exception dispatching input event."); skipCallbacks = true; } + env->DeleteLocalRef(inputEventObj); } else { ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName()); skipCallbacks = true; @@ -229,7 +251,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, } -static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, +static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject inputChannelObj, jobject messageQueueObj) { sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); @@ -245,7 +267,7 @@ static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj, } sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, - receiverObj, inputChannel, messageQueue); + receiverWeak, inputChannel, messageQueue); status_t status = receiver->initialize(); if (status) { String8 message; @@ -293,7 +315,7 @@ static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint rece static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "nativeInit", - "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", + "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", (void*)nativeInit }, { "nativeDispose", "(I)V", (void*)nativeDispose }, diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 2306cf2..b46eb4b 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -35,6 +35,8 @@ #include "android_view_KeyEvent.h" #include "android_view_MotionEvent.h" +#include <ScopedLocalRef.h> + namespace android { static struct { @@ -47,7 +49,7 @@ static struct { class NativeInputEventSender : public LooperCallback { public: NativeInputEventSender(JNIEnv* env, - jobject senderObj, const sp<InputChannel>& inputChannel, + jobject senderWeak, const sp<InputChannel>& inputChannel, const sp<MessageQueue>& messageQueue); status_t initialize(); @@ -59,7 +61,7 @@ protected: virtual ~NativeInputEventSender(); private: - jobject mSenderObjGlobal; + jobject mSenderWeakGlobal; InputPublisher mInputPublisher; sp<MessageQueue> mMessageQueue; KeyedVector<uint32_t, uint32_t> mPublishedSeqMap; @@ -75,11 +77,11 @@ private: NativeInputEventSender::NativeInputEventSender(JNIEnv* env, - jobject senderObj, const sp<InputChannel>& inputChannel, + jobject senderWeak, const sp<InputChannel>& inputChannel, const sp<MessageQueue>& messageQueue) : - mSenderObjGlobal(env->NewGlobalRef(senderObj)), + mSenderWeakGlobal(env->NewGlobalRef(senderWeak)), mInputPublisher(inputChannel), mMessageQueue(messageQueue), - mNextPublishedSeq(0) { + mNextPublishedSeq(1) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName()); #endif @@ -87,7 +89,7 @@ NativeInputEventSender::NativeInputEventSender(JNIEnv* env, NativeInputEventSender::~NativeInputEventSender() { JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->DeleteGlobalRef(mSenderObjGlobal); + env->DeleteGlobalRef(mSenderWeakGlobal); } status_t NativeInputEventSender::initialize() { @@ -178,6 +180,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName()); #endif + ScopedLocalRef<jobject> senderObj(env, NULL); bool skipCallbacks = false; for (;;) { uint32_t publishedSeq; @@ -205,7 +208,16 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { #endif if (!skipCallbacks) { - env->CallVoidMethod(mSenderObjGlobal, + if (!senderObj.get()) { + senderObj.reset(jniGetReferent(env, mSenderWeakGlobal)); + if (!senderObj.get()) { + ALOGW("channel '%s' ~ Sender object was finalized " + "without being disposed.", getInputChannelName()); + return DEAD_OBJECT; + } + } + + env->CallVoidMethod(senderObj.get(), gInputEventSenderClassInfo.dispatchInputEventFinished, jint(seq), jboolean(handled)); if (env->ExceptionCheck()) { @@ -218,7 +230,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { } -static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderObj, +static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak, jobject inputChannelObj, jobject messageQueueObj) { sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); @@ -234,7 +246,7 @@ static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderObj, } sp<NativeInputEventSender> sender = new NativeInputEventSender(env, - senderObj, inputChannel, messageQueue); + senderWeak, inputChannel, messageQueue); status_t status = sender->initialize(); if (status) { String8 message; @@ -277,7 +289,7 @@ static jboolean nativeSendMotionEvent(JNIEnv* env, jclass clazz, jint senderPtr, static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "nativeInit", - "(Landroid/view/InputEventSender;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", + "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I", (void*)nativeInit }, { "nativeDispose", "(I)V", (void*)nativeDispose }, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9eca0ce..666d1c6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2313,6 +2313,12 @@ </intent-filter> </receiver> + <receiver android:name="com.android.server.updates.SELinuxPolicyInstallReceiver" > + <intent-filter> + <action android:name="android.intent.action.UPDATE_SEPOLICY" /> + </intent-filter> + </receiver> + <receiver android:name="com.android.server.MasterClearReceiver" android:permission="android.permission.MASTER_CLEAR" android:priority="100" > diff --git a/core/res/res/layout-xlarge/activity_list.xml b/core/res/res/layout-xlarge/activity_list.xml index edf6ee6..bf46af8 100644 --- a/core/res/res/layout-xlarge/activity_list.xml +++ b/core/res/res/layout-xlarge/activity_list.xml @@ -52,7 +52,8 @@ android:singleLine="true" android:ellipsize="end" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + android:textAlignment="viewStart" /> </LinearLayout> <View android:id="@+id/titleDivider" android:layout_width="match_parent" diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml index 35552d3..59e56af 100644 --- a/core/res/res/layout/alert_dialog.xml +++ b/core/res/res/layout/alert_dialog.xml @@ -55,7 +55,8 @@ android:singleLine="true" android:ellipsize="end" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + android:textAlignment="viewStart" /> </LinearLayout> <ImageView android:id="@+id/titleDivider" android:layout_width="match_parent" diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml index 3f1fa3c..34cb21d 100644 --- a/core/res/res/layout/alert_dialog_holo.xml +++ b/core/res/res/layout/alert_dialog_holo.xml @@ -53,7 +53,8 @@ android:singleLine="true" android:ellipsize="end" android:layout_width="match_parent" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + android:textAlignment="viewStart" /> </LinearLayout> <View android:id="@+id/titleDivider" android:layout_width="match_parent" diff --git a/core/res/res/layout/app_not_authorized.xml b/core/res/res/layout/app_not_authorized.xml new file mode 100644 index 0000000..bd40eeb --- /dev/null +++ b/core/res/res/layout/app_not_authorized.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <!-- Customizable description text --> + <TextView android:id="@+id/description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_gravity="start|center_vertical" + android:paddingTop="16dip" + android:paddingBottom="16dip" + android:paddingStart="16dip" + android:paddingEnd="16dip" + android:text="@string/app_no_restricted_accounts" + /> + + <!-- Horizontal divider line --> + <View android:layout_height="1dip" + android:layout_width="match_parent" + android:background="?android:attr/dividerHorizontal" /> + + <!-- Alert dialog style buttons along the bottom. --> + <LinearLayout android:id="@+id/button_bar" + style="?android:attr/buttonBarStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:measureWithLargestChild="true"> + <Button android:id="@android:id/button1" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@android:string/yes" + android:onClick="onCancelButtonClicked" /> + </LinearLayout> +</LinearLayout> diff --git a/core/res/res/layout/wifi_p2p_dialog_row.xml b/core/res/res/layout/wifi_p2p_dialog_row.xml index ef810a0..2c88b10 100644 --- a/core/res/res/layout/wifi_p2p_dialog_row.xml +++ b/core/res/res/layout/wifi_p2p_dialog_row.xml @@ -23,5 +23,6 @@ <TextView android:id="@+id/value" style="@style/wifi_item_content" - android:textStyle="bold" /> + android:textStyle="bold" + android:textAlignment="viewStart" /> </LinearLayout> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 57e1797..f72fddb 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direkte toegang tot die mikrofoon om oudio op te neem."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direkte toegang tot kamera vir die neem van foto of video."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Sluitskerm"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Vermoë om die gedrag van die sluitskerm op jou toestel te beïnvloed."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Jou programme-inligting"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Vermoë om die gedrag van ander programme op jou toestel te beïnvloed."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Muurpapier"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksaksies"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Bergingspasie word min"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sommige stelselfunksies werk moontlik nie"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop tans"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Kanselleer"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Aan:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer die vereiste PIN in:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Die tablet sal tydelik van Wi-Fi ontkoppel terwyl dit aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gekoppel is"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Die foon sal tydelik van Wi-Fi ontkoppel terwyl dit aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gekoppel is"</string> <string name="select_character" msgid="3365550120617701745">"Voeg karakter in"</string> <string name="sms_control_title" msgid="7296612781128917719">"Stuur SMS-boodskappe"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur middel van \'n e-posrekening."\n\n" Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwyder"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Verhoog volume bo aanbevole vlak?"\n"Deur vir lang tydperke teen hoë volume te luister, kan jou gehoor beskadig word."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Hou aan met twee vingers inhou om toeganklikheid te aktiveer."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Toeganklikheid geaktiveer."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toeganklikheid gekanselleer."</string> <string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g> ."</string> <string name="owner_name" msgid="2716755460376028154">"Eienaar"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 103f0b7..73c323c 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"ድምጽ ለመቅረጽ ወደ ማይክሮፎኑ ቀጥተኛ መዳረሻ።"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"ካሜራ"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"ለካሜራ ምስል ወይም ቪዲዮ ለመቅረጽ ቀጥተኛ መዳረሻ።"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"ማያ ገጽ ቆልፍ"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"በመሣሪያዎ ላይ ያለውን የመቆለፊያ ማያ ገጽ ባህሪያት ላይ ተጽዕኖ የመፍጠር ችሎታ።"</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"የመተግበሪያዎችህ መረጃ"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"በመሣሪያህ ላይ ያሉ የሌሎች መተግበሪያዎች ባህሪዎች ላይ ተፅዕኖ የማሳረፍ ችሎታ።"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"ልጣፍ"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"የፅሁፍ እርምጃዎች"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"የማከማቻ ቦታ እያለቀ ነው"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> በማሄድ ላይ"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> በአሁኑ ጊዜ እያሄደ ነው"</string> <string name="ok" msgid="5970060430562524910">"እሺ"</string> <string name="cancel" msgid="6442560571259935130">"ይቅር"</string> <string name="yes" msgid="5362982303337969312">"እሺ"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"ለ፦"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"የሚፈለገውን ፒን ተይብ፦"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ፒን፦"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"ጡባዊው ከ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ጋር ተገናኝቶ ባለበት ጊዜ በጊዜያዊነት ከWi-Fi ጋር ይላቀቃል"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"ስልኩ ከ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ጋር ተገናኝቶ ባለበት ጊዜ በጊዜያዊነት ከWi-Fi ጋር ያለው ግንኙነት ይቋረጣል"</string> <string name="select_character" msgid="3365550120617701745">"ቁምፊ አስገባ"</string> <string name="sms_control_title" msgid="7296612781128917719">"የSMS መልዕክቶች መበላክ ላይ"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"የመክፈቻ ስርዓተ ጥለቱን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉትም። ከ<xliff:g id="NUMBER_1">%d</xliff:g> ተጨማሪ ያልተሳኩ ሙከራዎች በኋላ የኢሜይል መለያ ተጠቅመው ስልክዎን እንዲከፍቱ ይጠየቃሉ።"\n\n"እባክዎ ከ<xliff:g id="NUMBER_2">%d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"አስወግድ"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"ድምጽ ከሚመከረው መጠን በላይ ይጨመር?"\n"ለረጅም ጊዜ በከፍተኛ ድምጽ መስማት የመስማት ችሎታዎን ሊጎዳ ይችላል።"</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"ተደራሽነትን ለማንቃት ሁለት ጣቶችዎን ባሉበት ያቆዩዋቸው።"</string> <string name="accessibility_enabled" msgid="1381972048564547685">"ተደራሽነት ነቅቷል።"</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"ተደራሽነት ተሰርዟል።"</string> <string name="user_switched" msgid="3768006783166984410">"የአሁኑ ተጠቃሚ <xliff:g id="NAME">%1$s</xliff:g>።"</string> <string name="owner_name" msgid="2716755460376028154">"ባለቤት"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index b8fc281..942968b 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"الدخول المباشر إلى الميكروفون لتسجيل الصوت."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"الكاميرا"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"الدخول المباشر إلى الكاميرا لالتقاط صورة أو تصوير مقطع فيديو."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"تأمين الشاشة"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"القدرة على التأثير على سلوك شاشة التأمين في جهازك."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"معلومات التطبيقات"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"القدرة على التأثير في سلوك التطبيقات الأخرى بجهازك."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"الخلفية"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"إجراءات النص"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"مساحة التخزين منخفضة"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"قد لا تعمل بعض وظائف النظام"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> قيد التشغيل"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> قيد التشغيل حاليًا"</string> <string name="ok" msgid="5970060430562524910">"موافق"</string> <string name="cancel" msgid="6442560571259935130">"إلغاء"</string> <string name="yes" msgid="5362982303337969312">"موافق"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"إلى:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"اكتب رقم التعريف الشخصي المطلوب:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"رقم التعريف الشخصي:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"سيتم قطع اتصال الجهاز اللوحي مؤقتًا بشبكة Wi-Fi في الوقت الذي يكون فيه متصلاً بـ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"سيتم قطع اتصال الهاتف مؤقتًا بشبكة Wi-Fi في الوقت الذي يكون فيه متصلاً بـ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"إدراج حرف"</string> <string name="sms_control_title" msgid="7296612781128917719">"إرسال رسائل قصيرة SMS"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"لقد رسمت نقش إلغاء التأمين بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%d</xliff:g> من المحاولات غير الناجحة الأخرى، ستُطالب بإلغاء تأمين الهاتف باستخدام حساب بريد إلكتروني لإلغاء تأمين الهاتف."\n\n" أعد المحاولة خلال <xliff:g id="NUMBER_2">%d</xliff:g> ثانية."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"إزالة"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"هل تريد رفع مستوى الصوت فوق المستوى الموصى به؟"\n"قد يضر سماع صوت عالٍ لفترات طويلة بسمعك."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"اضغط بإصبعين لأسفل مع الاستمرار لتمكين تسهيل الدخول."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"تم تمكين إمكانية الدخول."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"تم إلغاء تسهيل الدخول."</string> <string name="user_switched" msgid="3768006783166984410">"المستخدم الحالي <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"المالك"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 3746e91..e6c9ee7 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Прамы доступ да мікрафону для запісу гуку."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Прамы доступ да камеры, каб зрабіць здымак ці зняць відэа."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Экран блакіроўкі"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Магчымасць уплываць на паводзіны экрана блакіроўкi на вашай прыладзе."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Інфармацыя аб вашых прыкладаннях"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Магчымасць уплываць на паводзіны іншых прыкладанняў на вашай прыладзе."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Шпалеры"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Дзеянні з тэкстам"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Месца для захавання на зыходзе"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некаторыя сістэмныя функцыі могуць не працаваць"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g> працуе"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g> зараз працуе"</string> <string name="ok" msgid="5970060430562524910">"ОК"</string> <string name="cancel" msgid="6442560571259935130">"Адмяніць"</string> <string name="yes" msgid="5362982303337969312">"ОК"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Каму:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Увядзіце патрэбны PIN-код:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Тэлефон будзе часова адключаны ад сеткі Wi-Fi, пакуль ён падлучаны да прылады <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Тэлефон будзе часова адключаны ад Wi-Fi, пакуль ён падлучаны да прылады <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Уставіць сімвал"</string> <string name="sms_control_title" msgid="7296612781128917719">"Адпраўка SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Выдалiць"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Павялiчыць гук больш за рэкамендаваны ўзровень?"\n"Доўгае слуханне музыкi на вялiкай гучнасцi можа пашкодзiць ваш слых."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Утрымлiвайце два пальцы, каб уключыць доступ."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Даступнасць уключана."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Даступнасць адменена."</string> <string name="user_switched" msgid="3768006783166984410">"Бягучы карыстальнік <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Уладальнік"</string> + <string name="error_message_title" msgid="4510373083082500195">"Памылка"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Гэтае прыкладанне не падтрымлівае уліковыя запісы для карыстальнікаў з абмежаванымі правамі"</string> + <string name="app_not_found" msgid="3429141853498927379">"Прыкладанне для гэтага дзеяння не знойдзенае"</string> </resources> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 4831909..79600f7 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Осъществяване на директен достъп до микрофона с цел записване на звук."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Осъществяване на директен достъп до камерата с цел заснемане на снимки или видеоклипове."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Заключване на екрана"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Възможност за оказване на влияние върху поведението на заключения екран на устройството ви."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Информация за приложенията ви"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Възможност за оказване на влияние върху поведението на други приложения на устройството ви."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Тапет"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Действия с текста"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Мястото в хранилището е на изчерпване"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Възможно е някои функции на системата да не работят"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> се изпълнява"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Понастоящем <xliff:g id="APP_NAME">%1$s</xliff:g> се изпълнява"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Отказ"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"До:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Въведете задължителния ПИН:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ПИН:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Таблетът временно ще прекъсне връзката с Wi-Fi, докато е свързан с/ъс <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Телефонът временно ще прекрати връзката с Wi-Fi, докато е свързан с/ъс <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Вмъкване на знак"</string> <string name="sms_control_title" msgid="7296612781128917719">"Изпращане на SMS съобщения"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. След още <xliff:g id="NUMBER_1">%d</xliff:g> неуспешни опита ще бъдете помолени да отключите телефона посредством имейл адрес."\n\n" Опитайте отново след <xliff:g id="NUMBER_2">%d</xliff:g> секунди."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Премахване"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Да се увеличи ли силата на звука над препоръчаното ниво?"\n"Продължителното слушане при висока сила на звука може да увреди слуха ви."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Продължете да натискате с два пръста, за да активирате функцията за достъпност."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Достъпността е активирана."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Функцията за достъпност е анулирана."</string> <string name="user_switched" msgid="3768006783166984410">"Текущ потребител <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Собственик"</string> + <string name="error_message_title" msgid="4510373083082500195">"Грешка"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Това приложение не поддържа профили за потребители с ограничения"</string> + <string name="app_not_found" msgid="3429141853498927379">"Няма намерено приложение за извършване на това действие"</string> </resources> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 59323c7..975dc40 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Accés directe al micròfon per enregistrar àudio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Càmera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Accés directe a la càmera per a la captura d\'imatges o de vídeos."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Bloqueig de pantalla"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Capacitat d\'influir en el comportament de la pantalla de bloqueig al dispositiu."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informació de les aplicacions"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Capacitat d\'afectar el rendiment d\'altres aplicacions del dispositiu."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fons de pantalla"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Accions de text"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"S\'està acabant l\'espai d\'emmagatzematge"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant en aquests moments"</string> <string name="ok" msgid="5970060430562524910">"D\'acord"</string> <string name="cancel" msgid="6442560571259935130">"Cancel·la"</string> <string name="yes" msgid="5362982303337969312">"D\'acord"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Per a:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introdueix el PIN sol·licitat:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"La tauleta es desconnectarà temporalment de la Wi-Fi mentre estigui connectada a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"El telèfon es desconnectarà temporalment de la Wi-Fi mentre estigui connectat a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Insereix un caràcter"</string> <string name="sms_control_title" msgid="7296612781128917719">"S\'estan enviant missatges SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Després de <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, se\'t demanarà que desbloquegis el telèfon amb un compte de correu electrònic."\n\n" Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Elimina"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Vols augmentar el volum per sobre del nivell de seguretat?"\n"Escoltar música a un volum alt durant períodes llargs pot danyar l\'oïda."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Mantén premuts els dos dits per activar l\'accessibilitat."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"S\'ha activat l\'accessibilitat."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibilitat cancel·lada."</string> <string name="user_switched" msgid="3768006783166984410">"Usuari actual: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Propietari"</string> + <string name="error_message_title" msgid="4510373083082500195">"Error"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Aquesta aplicació no admet comptes per a usuaris limitats"</string> + <string name="app_not_found" msgid="3429141853498927379">"No s\'ha trobat cap aplicació per processar aquesta acció"</string> </resources> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 6e067b5..0adebe9 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Přímý přístup k mikrofonu a možnost nahrávání zvuku"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparát"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Přímý přístup k fotoaparátu a možnost pořizování fotografií a videí"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Obrazovka uzamčení"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Možnost ovlivnit chování obrazovky uzamčení v zařízení."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informace o vašich aplikacích"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Možnost ovlivnit chování dalších aplikací v zařízení"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Tapeta"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Operace s textem"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je spuštěna"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je aktuálně spuštěná"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Zrušit"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Komu:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadejte požadovaný kód PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tablet se při připojení k zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> dočasně odpojí od sítě Wi-Fi"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon se při připojení k zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> dočasně odpojí od sítě Wi-Fi"</string> <string name="select_character" msgid="3365550120617701745">"Vkládání znaků"</string> <string name="sms_control_title" msgid="7296612781128917719">"Odesílání zpráv SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech budete požádáni o odemčení telefonu pomocí e-mailového účtu."\n\n" Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odebrat"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Zvýšit hlasitost nad doporučenou úroveň?"\n"Dlouhodobý poslech hlasitého zvuku může poškodit sluch."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Usnadnění zapnete dlouhým stisknutím dvěma prsty."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Usnadnění přístupu je aktivováno."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Usnadnění zrušeno."</string> <string name="user_switched" msgid="3768006783166984410">"Aktuální uživatel je <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Vlastník"</string> + <string name="error_message_title" msgid="4510373083082500195">"Chyba"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Tato aplikace u omezeného počtu uživatelů nepodporuje účty"</string> + <string name="app_not_found" msgid="3429141853498927379">"Aplikace potřebná k provedení této akce nebyla nalezena"</string> </resources> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index ea9c3ad..bb4eb72 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direkte adgang til mikrofonen, så der kan optages lyd."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direkte adgang til kamera, så der kan tages billeder eller optages video."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Lås skærm"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Evne til at påvirke skærmlåsens adfærd på enheden."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Oplysninger om dine applikationer"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Evne til at påvirke andre applikationers adfærd på din enhed."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Baggrund"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der er snart ikke mere lagerplads"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nogle systemfunktioner virker måske ikke"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører i øjeblikket"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Annuller"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Til:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv den påkrævede pinkode:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pinkode:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Wi-Fi-forbindelse til tabletten vil midlertidigt blive afbrudt, når den er tilsluttet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonens Wi-Fi-forbindelse vil midlertidigt blive afbrudt, når den er tilsluttet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Indsæt tegn"</string> <string name="sms_control_title" msgid="7296612781128917719">"Sender sms-beskeder"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg til vil du blive bedt om at låse din telefon op ved hjælp af en e-mailkonto."\n\n" Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Fjern"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Skal lydstyrken være over det anbefalede niveau?"\n"Du kan skade din hørelse ved at lytte ved høj lydstyrke i længere tid."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Hold fortsat to fingre nede for at aktivere tilgængelighed."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Tilgængelighed aktiveret."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Tilgængelighed er annulleret."</string> <string name="user_switched" msgid="3768006783166984410">"Nuværende bruger <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Ejer"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index f3f897a..6a488c7 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direkter Zugriff auf das Mikrofon zur Audioaufnahme"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direkter Zugriff auf Kamera für Bild- oder Videoaufnahmen"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Bildschirm sperren"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Kann die Bildschirmsperre auf Ihrem Gerät beeinflussen"</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informationen zu Ihren Apps"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Einflussnahme auf das Verhalten anderer Apps auf Ihrem Gerät"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Hintergrund"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Textaktionen"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der Speicherplatz wird knapp"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Einige Systemfunktionen funktionieren möglicherweise nicht."</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird ausgeführt."</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird gerade ausgeführt."</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Abbrechen"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"An:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Geben Sie die erforderliche PIN ein:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Das Tablet wird vorübergehend vom WLAN getrennt, während eine Verbindung mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> besteht."</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Das Telefon wird vorübergehend vom WLAN getrennt, während eine Verbindung mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> hergestellt wird."</string> <string name="select_character" msgid="3365550120617701745">"Zeichen einfügen"</string> <string name="sms_control_title" msgid="7296612781128917719">"SMS werden gesendet"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe eines E-Mail-Kontos zu entsperren."\n\n" Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Entfernen"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Lautstärke über den Schwellenwert anheben?"\n"Wenn Sie über längere Zeiträume hinweg Musik in hoher Lautstärke hören, kann dies Ihr Gehör schädigen."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Drücken Sie mit zwei Fingern, um die Bedienungshilfen zu aktivieren."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Bedienungshilfen aktiviert"</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Bedienungshilfen abgebrochen"</string> <string name="user_switched" msgid="3768006783166984410">"Aktueller Nutzer <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="owner_name" msgid="2716755460376028154">"Eigentümer"</string> + <string name="error_message_title" msgid="4510373083082500195">"Fehler"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Diese App unterstützt keine Konten für eingeschränkte Nutzer."</string> + <string name="app_not_found" msgid="3429141853498927379">"Für diese Aktion wurde keine App gefunden."</string> </resources> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 00fbd8f..21b014c 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Άμεση πρόσβαση στο μικρόφωνο για την εγγραφή ήχου."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Κάμερα"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Άμεση πρόσβαση σε κάμερα για λήψη εικόνας ή βίντεο."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Κλείδωμα οθόνης"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Δυνατότητα επίδρασης της συμπεριφοράς της οθόνης κλειδώματος στη συσκευή σας."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Οι πληροφορίες των εφαρμογών σας"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Δυνατότητα επιρροής συμπεριφοράς άλλων εφαρμογών στη συσκευή σας."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Ταπετσαρία"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Ενέργειες κειμένου"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ο χώρος αποθήκευσης εξαντλείται"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται τώρα."</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Προς:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Πληκτρολογήστε τον απαιτούμενο κωδικό PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Το tablet θα αποσυνδεθεί προσωρινά από το δίκτυο Wi-Fi ενώ συνδέεται στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Το τηλέφωνο θα αποσυνδεθεί προσωρινά από το δίκτυο Wi-Fi ενώ συνδέεται στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Εισαγωγή χαρακτήρα"</string> <string name="sms_control_title" msgid="7296612781128917719">"Αποστολή μηνυμάτων SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου."\n\n" Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Κατάργηση"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Αυξάνετε την ένταση ήχου πάνω από το επίπεδο ασφαλείας;"\n"Αν ακούτε μουσική σε υψηλή ένταση για μεγάλο χρονικό διάστημα ενδέχεται να προκληθεί βλάβη στην ακοή σας."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Αγγίξτε παρατεταμένα με δύο δάχτυλα για να ενεργοποιήσετε τη λειτουργία προσβασιμότητας."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Ενεργοποιήθηκε η προσβασιμότητα."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Η λειτουργία προσβασιμότητας ακυρώθηκε."</string> <string name="user_switched" msgid="3768006783166984410">"Τρέχων χρήστης <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Κάτοχος"</string> + <string name="error_message_title" msgid="4510373083082500195">"Σφάλμα"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Αυτή η εφαρμογή δεν υποστηρίζει λογαριασμούς για περιορισμένους χρήστες"</string> + <string name="app_not_found" msgid="3429141853498927379">"Δεν υπάρχει εφαρμογή για τη διαχείριση αυτής της ενέργειας"</string> </resources> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 0314fc9..ae698e1 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direct access to the microphone to record audio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direct access to camera for image or video capture."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Lock screen"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Ability to affect behaviour of the lock screen on your device."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Your applications information"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Ability to affect behaviour of other applications on your device."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Wallpaper"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Text actions"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> running"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> is currently running"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Cancel"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"To:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Type the required PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"The tablet will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"The phone will temporarily disconnect from Wi-FI while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Insert character"</string> <string name="sms_control_title" msgid="7296612781128917719">"Sending SMS messages"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account."\n\n" Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remove"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Raise volume above recommended level?"\n"Listening at high volume for long periods may damage your hearing."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Keep holding down two fingers to enable accessibility."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Accessibility enabled."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibility cancelled."</string> <string name="user_switched" msgid="3768006783166984410">"Current user <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Owner"</string> + <string name="error_message_title" msgid="4510373083082500195">"Error"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"This application does not support accounts for limited users"</string> + <string name="app_not_found" msgid="3429141853498927379">"No application found to handle this action"</string> </resources> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index c26998a..61d768d 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Acceso directo a micrófono para grabar audio"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Cámara"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Acceso directo a cámara para imagen o captura de video"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Bloquear pantalla"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Capacidad para afectar el comportamiento de la pantalla de bloqueo del dispositivo."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Información de tus aplicaciones"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Capacidad para influir en el comportamiento de otras aplicaciones en el dispositivo"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fondo de pantalla"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio de almacenamiento"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no estén disponibles."</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> en ejecución"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando en este momento."</string> <string name="ok" msgid="5970060430562524910">"Aceptar"</string> <string name="cancel" msgid="6442560571259935130">"Cancelar"</string> <string name="yes" msgid="5362982303337969312">"Aceptar"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"La tableta se desconectará temporalmente de la red Wi-Fi mientras esté conectada a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"El dispositivo se desconectará temporalmente de la red Wi-Fi mientras esté conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="select_character" msgid="3365550120617701745">"Insertar caracteres"</string> <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu dispositivo mediante el uso de una cuenta de correo."\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminar"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"¿Quieres subir el volumen por encima del nivel recomendado?"\n"Si escuchas música con el volumen alto durante períodos prolongados, puedes dañar tu audición."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Mantén presionado con dos dedos para activar la accesibilidad."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Se activó la accesibilidad."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Se canceló la accesibilidad."</string> <string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="owner_name" msgid="2716755460376028154">"Propietario"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index b709983..bdeed44 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Acceder directamente al micrófono para grabar audio"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Cámara"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Acceder directamente a la cámara para hacer fotos o grabar vídeos"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Pantalla de bloqueo"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Posibilidad de modificar el comportamiento de la pantalla de bloqueo del dispositivo"</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Información de tus aplicaciones"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Posibilidad de influir en el funcionamiento de otras aplicaciones del dispositivo"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fondo de pantalla"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando."</string> <string name="ok" msgid="5970060430562524910">"Aceptar"</string> <string name="cancel" msgid="6442560571259935130">"Cancelar"</string> <string name="yes" msgid="5362982303337969312">"Aceptar"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"El tablet se desconectará temporalmente de la red Wi-Fi mientras esté conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"El teléfono se desconectará temporalmente de la red Wi-Fi mientras está conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="select_character" msgid="3365550120617701745">"Insertar carácter"</string> <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS..."</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el teléfono."\n\n" Inténtalo de nuevo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminar"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"¿Quieres subir el volumen por encima del nivel recomendado?"\n"Escuchar sonidos a alto volumen durante largos períodos de tiempo puede dañar tus oídos."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Mantén la pantalla pulsada con dos dedos para habilitar las funciones de accesibilidad."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Accesibilidad habilitada"</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accesibilidad cancelada"</string> <string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="owner_name" msgid="2716755460376028154">"Propietario"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 3abaf62..9c7a28f 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Otsene juurdepääs mikrofonile heli salvestamiseks."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kaamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Otsene juurdepääs kaamerale fotode või videote jäädvustamiseks."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Lukustuskuva"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Võime mõjutada lukustuskuva käitumist seadmes."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Teie rakenduste teave"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Võime mõjutada teiste seadmes olevate rakenduste käitumist."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Taustapilt"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstitoimingud"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Talletusruum saab täis"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Mõned süsteemifunktsioonid ei pruugi töötada"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> töötab"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> töötab praegu"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Tühista"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Saaja:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Sisestage nõutav PIN-kood:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kood:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tahvelarvuti ühendus WiFi-ga katkestatakse ajutiselt, kui see on ühendatud seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefoni ühendus WiFi-ga katkestatakse ajutiselt, kui see on ühendatud seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Sisesta tähemärk"</string> <string name="sms_control_title" msgid="7296612781128917719">"SMS-sõnumite saatmine"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti. Pärast veel <xliff:g id="NUMBER_1">%d</xliff:g> ebaõnnestunud katset palutakse teil telefon avada meilikontoga."\n\n" Proovige uuesti <xliff:g id="NUMBER_2">%d</xliff:g> sekundi pärast."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eemalda"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Kas suurendada helitugevust üle soovitatud taseme?"\n"Pikaajaline suure helitugevusega muusika kuulamine võib kahjustada kuulmist."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Hõlbustuse lubamiseks hoidke kaht sõrme all."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Hõlbustus on lubatud."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Hõlbustus on tühistatud."</string> <string name="user_switched" msgid="3768006783166984410">"Praegune kasutaja <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Omanik"</string> + <string name="error_message_title" msgid="4510373083082500195">"Viga"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Rakendus ei toeta piiratud arvu kasutajate kontot"</string> + <string name="app_not_found" msgid="3429141853498927379">"Selle toimingu käsitlemiseks ei leitud ühtegi rakendust"</string> </resources> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index f8c1629..4a09361 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"مستقیم به میکروفن برای ضبط صدا دسترسی داشته باشید."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"دوربین"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"مستقیم به دوربین برای عکس گرفتن یا ضبط فیلم دسترسی داشته باشید."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"صفحه قفل"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"امکان تاثیرگذاری بر روی رفتار دستگاه در زمانی که صفحه آن قفل شده است."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"اطلاعات برنامههای شما"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"میتواند بر عملکرد برنامههای دیگر روی دستگاه اثر بگذارد."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"تصویر زمینه"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"عملکردهای متنی"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"فضای ذخیرهسازی رو به اتمام است"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> در حال اجرا"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> در حال حاضر در حال اجرا است"</string> <string name="ok" msgid="5970060430562524910">"تأیید"</string> <string name="cancel" msgid="6442560571259935130">"لغو"</string> <string name="yes" msgid="5362982303337969312">"تأیید"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"به:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"پین لازم را تایپ کنید:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"پین:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"در حین اتصال به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ارتباط این رایانه لوحی با Wi-Fi موقتاً قطع خواهد شد."</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"این گوشی بهطور موقت از Wi-Fi قطع خواهد شد، در حالی که به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> وصل است"</string> <string name="select_character" msgid="3365550120617701745">"درج نویسه"</string> <string name="sms_control_title" msgid="7296612781128917719">"ارسال پیامک ها"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدهاید. پس از <xliff:g id="NUMBER_1">%d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل تلفن خود را باز کنید."\n\n" لطفاً پس از <xliff:g id="NUMBER_2">%d</xliff:g> ثانیه دوباره امتحان کنید."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"حذف"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"صدا به بالاتر از سطح توصیه شده افزایش یابد؟"\n"گوش دادن به صدای بلند برای مدت طولانی میتواند به شنوایی شما آسیب برساند."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"برای فعال کردن قابلیت دسترسی، با دو انگشت خود همچنان به طرف پایین فشار دهید."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"قابلیت دسترسی فعال شد."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"قابلیت دسترسی لغو شد."</string> <string name="user_switched" msgid="3768006783166984410">"کاربر کنونی <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"دارنده"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index da5c2db..0f71020 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Äänen tallentamiseen käytettävän mikrofonin käyttöoikeus."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Kuvien tai videon tallentamiseen käytettävän kameran käyttöoikeus."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Lukitusruutu"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Lupa vaikuttaa laitteesi lukitusruudun toimintaan."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Sovelluksiesi tiedot"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Mahdollisuus vaikuttaa muiden laitteen sovelluksien käytökseen."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Taustakuva"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstitoiminnot"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Tallennustila loppumassa"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> on käynnissä"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> on käynnissä"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Peruuta"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kohde:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Kirjoita pyydetty PIN-koodi:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-koodi:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tablet-laitteen yhteys wifi-verkkoon katkaistaan väliaikaisesti tabletin ollessa yhdistettynä laitteeseen <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Puhelimen yhteys wifi-verkkoon katkaistaan väliaikaisesti puhelimen ollessa yhdistettynä laitteeseen <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Lisää merkki"</string> <string name="sms_control_title" msgid="7296612781128917719">"Tekstiviestien lähettäminen"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Piirsit lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. Jos piirrät kuvion väärin vielä <xliff:g id="NUMBER_1">%d</xliff:g> kertaa, sinua pyydetään poistamaan puhelimesi lukitus sähköpostitilin avulla."\n\n" Yritä uudelleen <xliff:g id="NUMBER_2">%d</xliff:g> sekunnin kuluttua."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Poista"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Nostetaanko äänenvoimakkuus suositeltua tasoa voimakkaammaksi?"\n"Jos kuuntelet suurella äänenvoimakkuudella pitkiä aikoja, kuulosi voi vahingoittua."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Ota esteettömyystila käyttöön koskettamalla pitkään kahdella sormella."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Esteettömyystila käytössä."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Esteettömyystila peruutettu."</string> <string name="user_switched" msgid="3768006783166984410">"Nykyinen käyttäjä: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Omistaja"</string> + <string name="error_message_title" msgid="4510373083082500195">"Virhe"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Tämä sovellus ei tue rajoitettujen käyttäjien tilejä"</string> + <string name="app_not_found" msgid="3429141853498927379">"Tätä toimintoa käsittelevää sovellusta ei löydy"</string> </resources> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index c9a0eb5..2026f56 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Accès direct au microphone pour enregistrer du contenu audio"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Appareil photo"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Accès direct à la caméra pour la capture d\'images ou de vidéos"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Écran de verrouillage"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Modifier le comportement de l\'écran de verrouillage sur votre appareil"</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informations relatives à vos applications"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Possibilité de modifier le comportement des autres applications sur votre appareil"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fond d\'écran"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> en cours d\'exécution"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> est en cours d\'exécution."</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Annuler"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"À :"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Saisissez le code PIN requis :"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Code PIN :"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"La tablette sera déconnectée du réseau Wi-Fi tant qu\'elle sera connectée à l\'appareil \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Le téléphone sera déconnecté du réseau Wi-Fi tant qu\'il sera connecté à l\'appareil <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="select_character" msgid="3365550120617701745">"Insérer un caractère"</string> <string name="sms_control_title" msgid="7296612781128917719">"Envoi de messages SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique."\n\n" Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Supprimer"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Augmenter le volume au-dessus du niveau recommandé ?"\n"L\'écoute à un volume élevé pendant des périodes prolongées peut endommager votre audition."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Pour activer l\'accessibilité, appuyez de manière prolongée avec deux doigts."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"L\'accessibilité a bien été activée."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibilité annulée."</string> <string name="user_switched" msgid="3768006783166984410">"Utilisateur actuel : <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="owner_name" msgid="2716755460376028154">"Propriétaire"</string> + <string name="error_message_title" msgid="4510373083082500195">"Erreur"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Les comptes des utilisateurs en accès limité ne sont pas acceptés pour cette application."</string> + <string name="app_not_found" msgid="3429141853498927379">"Aucune application trouvée pour gérer cette action."</string> </resources> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index cbec3fe..b70bbc0 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"ऑडियो रिकॉर्ड करने के लिए माइक्रोफ़ोन पर सीधी पहुंच."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"कैमरा"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"चित्र या वीडियो कैप्चर के लिए कैमरे पर सीधी पहुंच."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"स्क्रीन लॉक करें"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"आपके उपकरण की लॉक स्क्रीन का व्यवहार प्रभावित करने की क्षमता."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"आपके एप्लिकेशन की जानकारी"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"अपने उपकरण पर अन्य एप्लिकेशन के व्यवहार को प्रभावित करने की क्षमता."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"वॉलपेपर"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"पाठ क्रियाएं"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"संग्रहण स्थान समाप्त हो रहा है"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"हो सकता है कुछ सिस्टम फ़ंक्शन कार्य न करें"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> चल रहा है"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> वर्तमान में चल रहा है"</string> <string name="ok" msgid="5970060430562524910">"ठीक"</string> <string name="cancel" msgid="6442560571259935130">"रद्द करें"</string> <string name="yes" msgid="5362982303337969312">"ठीक"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"प्रति:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"आवश्यक पिन लिखें:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"पिन:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> से कनेक्ट रहने पर टेबलेट Wi-Fi से अस्थायी रूप से डिस्कनेक्ट हो जाएगा"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"फ़ोन <xliff:g id="DEVICE_NAME">%1$s</xliff:g> से कनेक्ट रहते समय Wi-Fi से अस्थायी रूप से डिस्कनेक्ट हो जाएगा"</string> <string name="select_character" msgid="3365550120617701745">"वर्ण सम्मिलित करें"</string> <string name="sms_control_title" msgid="7296612781128917719">"SMS संदेश भेज रहा है"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"आपने अपने अनलॉक प्रतिमान को <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से आरेखित किया है. <xliff:g id="NUMBER_1">%d</xliff:g> और असफल प्रयासों के बाद, आपसे अपने फ़ोन को किसी ईमेल खाते का उपयोग करके अनलॉक करने के लिए कहा जाएगा."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"निकालें"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"वॉल्यूम को उपरोक्त अनुशंसित स्तर तक बढ़ाएं?"\n"लंबे समय तक अधिक वॉल्यूम पर सुनने से आपकी सुनने की क्षमता को क्षति पहुंच सकती है."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"पहुंच-योग्यता को सक्षम करने के लिए दो अंगुलियों से नीचे दबाए रखें."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"पहुंच-योग्यता सक्षम कर दी है."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"पहुंच-योग्यता रद्द की गई."</string> <string name="user_switched" msgid="3768006783166984410">"वर्तमान उपयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"स्वामी"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 6eb3863..7b1eee3 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Izravan pristup mikrofonu za snimanje zvuka."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparat"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Izravan pristup fotoaparatu za slikanje ili snimanje videozapisa."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Zaključan zaslon"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Mogućnost utjecanja na ponašanje zaključanog zaslona na uređaju."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informacije o vašoj aplikaciji"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Sposobnost da utječu na postupanje drugih aplikacija na vašem uređaju."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Pozadinska slika"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Radnje s tekstom"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda neće raditi"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Izvodi se aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Trenutačno se izvodi aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="ok" msgid="5970060430562524910">"U redu"</string> <string name="cancel" msgid="6442560571259935130">"Odustani"</string> <string name="yes" msgid="5362982303337969312">"U redu"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Prima:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Upišite potreban PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tablet će se privremeno isključiti s Wi-Fija dok je povezan s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon će se privremeno isključiti s Wi-Fija dok je povezan s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Umetni znak"</string> <string name="sms_control_title" msgid="7296612781128917719">"Slanje SMS poruka"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> morat ćete otključati telefon pomoću računa e-pošte."\n\n" Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ukloni"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Želite li pojačati iznad preporučene razine?"\n"Dulje slušanje preglasne glazbe može vam oštetiti sluh."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Nastavite držati s dva prsta kako biste omogućili pristupačnost."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Dostupnost je omogućena."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Pristupačnost otkazana."</string> <string name="user_switched" msgid="3768006783166984410">"Trenutačni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Vlasnik"</string> + <string name="error_message_title" msgid="4510373083082500195">"Pogreška"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Aplikacija ne podržava račune za ograničene korisnike"</string> + <string name="app_not_found" msgid="3429141853498927379">"Nije pronađena aplikacija za upravljanje ovom radnjom"</string> </resources> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index b621899..fc1d663 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Közvetlen hozzáférés a mikrofonhoz hangrögzítés céljából"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Fényképezőgép"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Közvetlen hozzáférés a fényképezőgéphez kép vagy videó rögzítése céljából"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Lezárási képernyő"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Képes a lezárási képernyő viselkedésének befolyásolására az eszközön."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Az Ön alkalmazásainak információi"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Képes az eszközön a többi alkalmazás viselkedését befolyásolni."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Háttérkép"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Műveletek szöveggel"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kevés a szabad terület"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> fut"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg fut"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Mégse"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Címzett:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Adja meg a szükséges PIN kódot:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kód:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"A táblagép ideiglenesen lecsatlakozik a Wi-Fi hálózatról, míg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"A telefon ideiglenesen kilép a Wi-Fi hálózatról, míg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik."</string> <string name="select_character" msgid="3365550120617701745">"Karakter beszúrása"</string> <string name="sms_control_title" msgid="7296612781128917719">"SMS-ek küldése"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a telefonját."\n\n" Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eltávolítás"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"A javasolt szint fölé emeli a hangerőt?"\n"Ha hosszú ideig hangosan hallgatja a zenét, az károsíthatja a hallását."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Továbbra is tartsa lenyomva két ujját a hozzáférés engedélyezéséhez."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Hozzáférés engedélyezve"</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Hozzáférés megszakítva."</string> <string name="user_switched" msgid="3768006783166984410">"<xliff:g id="NAME">%1$s</xliff:g> az aktuális felhasználó."</string> <string name="owner_name" msgid="2716755460376028154">"Tulajdonos"</string> + <string name="error_message_title" msgid="4510373083082500195">"Hiba"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ez az alkalmazás nem támogatja a korlátozott jogokkal rendelkező felhasználói fiókokat."</string> + <string name="app_not_found" msgid="3429141853498927379">"Nincs megfelelő alkalmazás a művelet elvégzésére."</string> </resources> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 2d8d75d..2a9f2fc 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Akses langsung ke mikrofon untuk merekam audio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Akses langsung ke kamera untuk gambar atau tangkapan video."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Layar terkunci"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Kemampuan untuk memengaruhi perilaku layar terkunci di perangkat Anda."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informasi aplikasi Anda"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Kemampuan untuk memengaruhi perilaku aplikasi lain pada perangkat Anda."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Wallpaper"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Tindakan teks"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> berjalan"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan"</string> <string name="ok" msgid="5970060430562524910">"Oke"</string> <string name="cancel" msgid="6442560571259935130">"Batal"</string> <string name="yes" msgid="5362982303337969312">"Oke"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kepada:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ketik PIN yang diminta:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Sambungan tablet akan diputuskan dari Wi-Fi untuk sementara saat tersambung dengan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Ponsel akan terputus sementara dari Wi-Fi saat tersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Sisipkan huruf"</string> <string name="sms_control_title" msgid="7296612781128917719">"Mengirim pesan SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email."\n\n"Coba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Hapus"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Naikkan volume di atas tingkat aman?"\n"Mendengarkan volume tinggi dalam jangka waktu yang lama dapat merusak pendengaran Anda."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Tahan terus dua jari untuk mengaktifkan aksesibilitas."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Aksesibilitas diaktifkan."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Aksesibilitas dibatalkan."</string> <string name="user_switched" msgid="3768006783166984410">"Pengguna saat ini <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Pemilik"</string> + <string name="error_message_title" msgid="4510373083082500195">"Kesalahan"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Aplikasi ini tidak mendukung akun untuk pengguna terbatas"</string> + <string name="app_not_found" msgid="3429141853498927379">"Tidak ada aplikasi yang ditemukan untuk menangani tindakan ini"</string> </resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 4486e69..2273459 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Accesso diretto al microfono per registrare audio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotocamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Accesso diretto alla fotocamera per acquisizione di immagini o video."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Schermata di blocco"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Possibilità di influenzare il comportamento della schermata di blocco sul dispositivo."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informazioni sulle tue applicazioni"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Possibilità di influenzare il comportamento di altre applicazioni sul dispositivo."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Sfondo"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Azioni testo"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spazio di archiviazione in esaurimento"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Alcune funzioni di sistema potrebbero non funzionare"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> in esecuzione"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> è attualmente in esecuzione"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Annulla"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"A:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Inserisci il PIN richiesto:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Il tablet verrà momentaneamente scollegato dalla rete Wi-Fi durante il collegamento a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Il telefono verrà momentaneamente scollegato dalla rete Wi-Fi durante il collegamento a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="select_character" msgid="3365550120617701745">"Inserisci carattere"</string> <string name="sms_control_title" msgid="7296612781128917719">"Invio SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono con un account email."\n\n" Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Rimuovi"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Aumentare il volume oltre il livello di sicurezza?"\n"Ascoltare musica ad alto volume per lunghi periodi potrebbe danneggiare l\'udito."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Continua a tenere premuto con due dita per attivare l\'accessibilità."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Accessibilità attivata."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibilità annullata."</string> <string name="user_switched" msgid="3768006783166984410">"Utente corrente <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Proprietario"</string> + <string name="error_message_title" msgid="4510373083082500195">"Errore"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Questa applicazione non supporta account di utenti con limitazioni"</string> + <string name="app_not_found" msgid="3429141853498927379">"Nessuna applicazione trovata in grado di gestire questa azione"</string> </resources> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 17e8a37..8c7c74e 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"גישה ישירה אל המיקרופון להקלטת אודיו."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"מצלמה"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"גישה ישירה למצלמה לצילום תמונות או וידאו."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"מסך נעילה"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"היכולת להשפיע על אופן ההתנהגות של מסך הנעילה של המכשיר."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"מידע על היישומים שלך"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"יכולת להשפיע על התנהגותם של יישומים אחרים במכשיר."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"טפט"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"פעולות טקסט"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל כרגע"</string> <string name="ok" msgid="5970060430562524910">"אישור"</string> <string name="cancel" msgid="6442560571259935130">"ביטול"</string> <string name="yes" msgid="5362982303337969312">"אישור"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"אל:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"הקלד את קוד ה-PIN הנדרש."</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"הטאבלט יתנתק מרשת ה-Wi-Fi באופן זמני בשעה שהוא מחובר אל <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"הטלפון יתנתק מרשת ה-Wi-Fi באופן זמני בשעה שהוא מחובר אל <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"הוסף תו"</string> <string name="sms_control_title" msgid="7296612781128917719">"שולח הודעות SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות חשבון דוא\"ל."\n\n"נסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"הסר"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"האם להעלות את עוצמת הקול מעל לרמה המומלצת?"\n"האזנה בעוצמת קול גבוהה למשך זמן ארוך עלולה לפגוע בשמיעה."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"המשך לגעת בשתי אצבעות כדי להפעיל נגישות."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"נגישות הופעלה."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"נגישות בוטלה."</string> <string name="user_switched" msgid="3768006783166984410">"המשתמש הנוכחי <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"בעלים"</string> + <string name="error_message_title" msgid="4510373083082500195">"שגיאה"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"היישום הזה לא תומך בחשבונות עבור משתמשים מוגבלים"</string> + <string name="app_not_found" msgid="3429141853498927379">"לא נמצא יישום שתומך בפעולה זו"</string> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 2b6cb00..4ec756e 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"マイクに直接アクセスして音声を記録します。"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"カメラ"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"カメラに直接アクセスして画像または動画を撮影します。"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"ロック画面"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"端末でのロック画面の動作に影響を与えることができます。"</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"アプリ情報"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"端末上の他のアプリの動作に影響を及ぼします。"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"壁紙"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"テキスト操作"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"空き容量わずか"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"一部のシステム機能が動作しない可能性があります"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g>を実行中"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"現在<xliff:g id="APP_NAME">%1$s</xliff:g>を実行しています"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"キャンセル"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"To:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"必要なPINを入力してください:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"タブレットが<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に接続されている間は一時的にWi-Fi接続が切断されます"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"携帯端末が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に接続されている間は一時的にWi-Fi接続が解除されます。"</string> <string name="select_character" msgid="3365550120617701745">"文字を挿入"</string> <string name="sms_control_title" msgid="7296612781128917719">"SMSメッセージの送信中"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、携帯端末のロック解除にメールアカウントが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"削除"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"推奨レベルを超えるまで音量を上げますか?"\n"大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"ユーザー補助機能を有効にするには2本の指で押し続けてください。"</string> <string name="accessibility_enabled" msgid="1381972048564547685">"ユーザー補助が有効になりました。"</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"ユーザー補助をキャンセルしました。"</string> <string name="user_switched" msgid="3768006783166984410">"現在のユーザーは<xliff:g id="NAME">%1$s</xliff:g>です。"</string> <string name="owner_name" msgid="2716755460376028154">"所有者"</string> + <string name="error_message_title" msgid="4510373083082500195">"エラー"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"このアプリでは限定ユーザー用のアカウントはサポートしていません"</string> + <string name="app_not_found" msgid="3429141853498927379">"この操作を行うアプリが見つかりません"</string> </resources> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index a810269..e6010ac 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"오디오를 녹음하기 위해 마이크에 직접 액세스합니다."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"카메라"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"이미지 및 동영상을 캡처하기 위해 카메라에 직접 액세스합니다."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"화면 잠금"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"기기의 잠금 화면 동작에 영향을 줄 수 있는 기능입니다."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"애플리케이션 정보"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"기기의 다른 애플리케이션의 작동에 영향을 줍니다."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"배경화면"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"텍스트 작업"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"저장 공간이 부족함"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> 실행 중"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g>이(가) 현재 실행 중입니다."</string> <string name="ok" msgid="5970060430562524910">"확인"</string> <string name="cancel" msgid="6442560571259935130">"취소"</string> <string name="yes" msgid="5362982303337969312">"확인"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"받는사람:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"필수 PIN 입력:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 연결되어 있는 동안 일시적으로 태블릿의 Wi-Fi 연결이 해제됩니다."</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 연결되어 있는 동안 일시적으로 휴대전화의 Wi-Fi 연결이 해제됩니다."</string> <string name="select_character" msgid="3365550120617701745">"문자 삽입"</string> <string name="sms_control_title" msgid="7296612781128917719">"SMS 메시지를 보내는 중"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 이메일 계정을 사용하여 휴대전화를 잠금해제해야 합니다."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도해 주세요."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"삭제"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"안전한 수준 이상으로 볼륨을 높이시겠습니까?"\n"높은 볼륨으로 장시간 청취하면 청력에 손상이 올 수 있습니다."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"두 손가락으로 길게 누르면 접근성을 사용하도록 설정됩니다."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"접근성을 사용 설정했습니다."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"접근성이 취소되었습니다."</string> <string name="user_switched" msgid="3768006783166984410">"현재 사용자는 <xliff:g id="NAME">%1$s</xliff:g>님입니다."</string> <string name="owner_name" msgid="2716755460376028154">"소유자"</string> + <string name="error_message_title" msgid="4510373083082500195">"오류"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"이 애플리케이션은 제한된 사용자를 위한 계정을 지원하지 않습니다."</string> + <string name="app_not_found" msgid="3429141853498927379">"이 작업을 처리하는 애플리케이션을 찾을 수 없습니다."</string> </resources> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 1a48fac..e8d7c26 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Tiesioginė prieiga prie mikrofono, kad būtų galima įrašyti garsą."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparatas"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Tiesioginė prieiga prie fotoaparato, kad būtų galima fotografuoti vaizdus arba įrašyti vaizdo įrašus."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Užrakinti ekraną"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Galimybė paveikti užrakinimo ekrano veikimą įrenginyje."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Programų informacija"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Galimybė paveikti kitų įrenginio programų veikimą."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Ekrano fonas"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksto veiksmai"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Mažėja laisvos saugyklos vietos"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kai kurios sistemos funkcijos gali neveikti"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ paleista"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ šiuo metu paleista"</string> <string name="ok" msgid="5970060430562524910">"Gerai"</string> <string name="cancel" msgid="6442560571259935130">"Atšaukti"</string> <string name="yes" msgid="5362982303337969312">"Gerai"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Skirta:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Įveskite reikiamą PIN kodą:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kodas:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Planšetinis kompiuteris bus laikinai atjungtas nuo „Wi-Fi“, kol jis prijungtas prie „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonas bus laikinai atjungtas nuo „Wi-Fi“, kol bus prijungtas prie „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string> <string name="select_character" msgid="3365550120617701745">"Įterpti simbolį"</string> <string name="sms_control_title" msgid="7296612781128917719">"SMS pranešimų siuntimas"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. Po dar <xliff:g id="NUMBER_1">%d</xliff:g> nesėkm. band. būsite paprašyti atrakinti telefoną naudodami „Google“ prisijungimo duomenis."\n\n" Bandykite dar kartą po <xliff:g id="NUMBER_2">%d</xliff:g> sek."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Pašalinti"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Padidinti garsumą viršijant saugų lygį?"\n"Ilgai klausantis dideliu garsumu gali sutrikti klausa."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Laikykite palietę dviem pirštais, kad įgalintumėte pritaikymo neįgaliesiems režimą."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Pritaikymas neįgaliesiems įgalintas."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Pritaikymo neįgaliesiems režimas atšauktas."</string> <string name="user_switched" msgid="3768006783166984410">"Dabartinis naudotojas: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Savininkas"</string> + <string name="error_message_title" msgid="4510373083082500195">"Klaida"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ši programa nepalaiko apribotų naudotojų paskyrų"</string> + <string name="app_not_found" msgid="3429141853498927379">"Nerasta programa šiam veiksmui apdoroti"</string> </resources> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index dcaf8e3..f134005 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Tieša piekļuve mikrofonam, lai ierakstītu audio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Tieša piekļuve kamerai, lai uzņemtu attēlus vai videoklipus."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Ekrāna bloķēšana"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Spēja ietekmēt bloķēšanas ekrāna darbību jūsu ierīcē."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informācija par jūsu lietojumprogrammām"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Spēja ietekmēt citu ierīcē esošo lietojumprogrammu darbību."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fona tapete"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksta darbības"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Paliek maz brīvas vietas"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Dažas sistēmas funkcijas var nedarboties."</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> darbojas"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik darbojas"</string> <string name="ok" msgid="5970060430562524910">"Labi"</string> <string name="cancel" msgid="6442560571259935130">"Atcelt"</string> <string name="yes" msgid="5362982303337969312">"Labi"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kam:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ierakstiet pieprasīto PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Planšetdators tiks īslaicīgi atvienots no Wi-Fi tīkla, kamēr būs izveidots savienojums ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Tālrunis tiks īslaicīgi atvienots no Wi-Fi tīkla, kamēr būs izveidots savienojums ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="select_character" msgid="3365550120617701745">"Ievietojiet rakstzīmi"</string> <string name="sms_control_title" msgid="7296612781128917719">"Īsziņu sūtīšana"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot e-pasta kontu."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Noņemt"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Vai palielināt skaļumu virs ieteicamā līmeņa?"\n"Ilgstoši klausoties skaņu lielā skaļumā, var tikt bojāta dzirde."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Lai iespējotu pieejamību, turiet nospiestus divus pirkstus."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Pieejamības režīms ir iespējots."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Pieejamība ir atcelta."</string> <string name="user_switched" msgid="3768006783166984410">"Pašreizējais lietotājs: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Īpašnieks"</string> + <string name="error_message_title" msgid="4510373083082500195">"Kļūda"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Šajā lietojumprogrammā netiek atbalstīti ierobežotu lietotāju konti."</string> + <string name="app_not_found" msgid="3429141853498927379">"Netika atrasta neviena lietojumprogramma, kas var veikt šo darbību."</string> </resources> diff --git a/core/res/res/values-mcc286/config.xml b/core/res/res/values-mcc286/config.xml index d99d051..d99d051 100755..100644 --- a/core/res/res/values-mcc286/config.xml +++ b/core/res/res/values-mcc286/config.xml diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index be04f9d..3091e7d 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Akses langsung ke mikrofon untuk merakam audio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Akses langsung ke kamera untuk merakam imej atau video."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Kunci skrin"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Keupayaan untuk mempengaruhi kelakuan skrin kunci pada peranti anda."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Maklumat aplikasi anda"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Keupayaan untuk mempengaruhi tingkah laku aplikasi lain pada peranti anda."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Kertas dinding"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Tindakan teks"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang storan semakin berkurangan"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak berfungsi"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> berjalan"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Batal"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kepada:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Taipkan PIN yang diperlukan:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Sambungan tablet ke Wi-Fi akan diputuskan buat sementara waktu semasa tablet bersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Sambungan telefon ke Wi-Fi akan diputuskan buat sementara waktu semasa telefon bersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Masukkan aksara"</string> <string name="sms_control_title" msgid="7296612781128917719">"Menghantar mesej SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah tersilap lukis corak buka kunci sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. Selepas <xliff:g id="NUMBER_1">%d</xliff:g> lagi percubaan yang tidak berjaya, anda akan diminta membuka kunci telefon anda menggunakan log masuk Google anda."\n\n" Cuba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> saat."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Alih keluar"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Tingkatkan kelantangan melebihi aras yang dicadangkan?"\n"Mendengar pada kelantangan tinggi untuk tempoh yang panjang boleh merosakkan pendengaran anda."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Teruskan menahan dengan dua jari untuk mendayakan kebolehcapaian."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Kebolehcapaian didayakan."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Kebolehcapaian dibatalkan."</string> <string name="user_switched" msgid="3768006783166984410">"Pengguna semasa <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Pemilik"</string> + <string name="error_message_title" msgid="4510373083082500195">"Ralat"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Aplikasi ini tidak menyokong akaun untuk pengguna terhad"</string> + <string name="app_not_found" msgid="3429141853498927379">"Tidak menemui aplikasi untuk mengendalikan tindakan ini"</string> </resources> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 387676f..9d39ac8 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direkte tilgang til mikrofonen for å ta opp lyd."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kameraet"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direkte tilgang til kamera for bilde- eller videoopptak."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Låse skjermen"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Evne til å påvirke atferden til den låste skjermen på enheten din."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Appinformasjonen din"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Ha muligheten til å påvirke andre apper på enheten din."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Bakgrunnen"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører for øyeblikket"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Avbryt"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Til:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv inn påkrevd PIN-kode:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Nettbrettet frakobles Wi-Fi midlertidig mens den er tilkoblet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonen frakobles Wi-Fi midlertidig mens den er tilkoblet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Sett inn tegn"</string> <string name="sms_control_title" msgid="7296612781128917719">"Sender SMS-meldinger"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, blir du bedt om å låse opp telefonen via en e-postkonto."\n\n" Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Fjern"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Vil du øke lydnivået over det anbefalte nivået?"\n"Et høyt lydnivå i lengre perioder kan skade hørselen din."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Fortsett å holde nede to fingre for å aktivere tilgjengelighet."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Tilgjengelighet er aktivert."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Tilgjengelighetstjenesten ble avbrutt."</string> <string name="user_switched" msgid="3768006783166984410">"Gjeldende bruker: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Eier"</string> + <string name="error_message_title" msgid="4510373083082500195">"Feil"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Denne appen støtter ikke kontoer for brukere med begrensninger"</string> + <string name="app_not_found" msgid="3429141853498927379">"Finner ingen apper som kan utføre denne handlingen"</string> </resources> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 38619e5..b0a6d30 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Rechtstreeks toegang krijgen tot de microfoon om geluid op te nemen."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Rechtstreeks toegang krijgen tot de camera om afbeeldingen of video\'s vast te leggen."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Scherm vergrendelen"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Mogelijkheid om de werking van het vergrendelingsscherm op uw apparaat te beïnvloeden."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informatie over uw applicaties"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Mogelijkheid om het gedrag van andere applicaties op uw apparaat te beïnvloeden."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Achtergrond"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstacties"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Opslagruimte is bijna vol"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bepaalde systeemfuncties werken mogelijk niet"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> is actief"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> is momenteel actief"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Annuleren"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Naar:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer de gewenste pincode in:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pincode"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"De verbinding met het wifi-netwerk wordt tijdelijk uitgeschakeld terwijl de telefoon is verbonden met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"De verbinding met het wifi-netwerk wordt tijdelijk uitgeschakeld terwijl de telefoon verbonden is met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Teken invoegen"</string> <string name="sms_control_title" msgid="7296612781128917719">"SMS-berichten verzenden"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd uw telefoon te ontgrendelen via een e-mailaccount."\n\n" Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwijderen"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Wilt u het volume verhogen tot boven het aanbevolen geluidsniveau?"\n"Te lang luisteren op een te hoog volume kan leiden tot gehoorbeschadiging."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Blijf het scherm met twee vingers aanraken om toegankelijkheid in te schakelen."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Toegankelijkheid ingeschakeld."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toegankelijkheid geannuleerd."</string> <string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Eigenaar"</string> + <string name="error_message_title" msgid="4510373083082500195">"Fout"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Deze app ondersteunt geen accounts voor beperkte gebruikers"</string> + <string name="app_not_found" msgid="3429141853498927379">"Er is geen app gevonden om deze actie uit te voeren"</string> </resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index a491104..8d3122f 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Bezpośredni dostęp do mikrofonu i nagrywanie dźwięku."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Aparat"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Bezpośredni dostęp do aparatu – robienie zdjęć i nagrywanie filmów."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Ekran blokady"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Możliwość wpływania na zachowanie ekranu blokady urządzenia."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informacje o aplikacjach"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Możliwość zmiany działania innych aplikacji na urządzeniu."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Tapeta"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Działania na tekście"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kończy się miejsce"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektóre funkcje systemu mogą nie działać"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> uruchomiona"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest uruchomiona"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Anuluj"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Do:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Wpisz wymagany kod PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Kod PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Na czas połączenia z <xliff:g id="DEVICE_NAME">%1$s</xliff:g> tablet zostanie tymczasowo odłączony od Wi-Fi"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Na czas połączenia z <xliff:g id="DEVICE_NAME">%1$s</xliff:g> telefon zostanie tymczasowo odłączony od Wi-Fi"</string> <string name="select_character" msgid="3365550120617701745">"Wstaw znak"</string> <string name="sms_control_title" msgid="7296612781128917719">"Wysyłanie wiadomości SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google."\n\n" Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Usuń"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Chcesz ustawić głośność powyżej bezpiecznego poziomu?"\n"Słuchanie przy dużym poziomie głośności przez dłuższy czas może doprowadzić do uszkodzenia słuchu."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Aby włączyć ułatwienia dostępu, przytrzymaj dwa palce."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Włączono ułatwienia dostępu."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ułatwienia dostępu zostały anulowane."</string> <string name="user_switched" msgid="3768006783166984410">"Bieżący użytkownik: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Właściciel"</string> + <string name="error_message_title" msgid="4510373083082500195">"Błąd"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ta aplikacja nie obsługuje kont użytkowników z ograniczeniami"</string> + <string name="app_not_found" msgid="3429141853498927379">"Nie znaleziono aplikacji do obsługi tej akcji"</string> </resources> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 646bc3a..5eba00c 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Acesso direto ao microfone para gravar áudio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmara"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Acesso direto à câmara para captura de imagens ou vídeos."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Bloquear ecrã"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Capacidade de influenciar o comportamento do ecrã de bloqueio no seu dispositivo."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"As informações das suas aplicações"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Capacidade de afetar o comportamento de outras aplicações no seu dispositivo."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Imagem de fundo"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Acções de texto"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> em execução"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> está a ser executado"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Cancelar"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduza o PIN solicitado:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"O tablet sera temporariamente desligado da rede Wi-Fi enquanto estiver ligado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"O telemóvel irá desligar-se temporariamente da rede Wi-Fi enquanto está ligado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Introduzir carácter"</string> <string name="sms_control_title" msgid="7296612781128917719">"A enviar mensagens SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email."\n\n" Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remover"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Aumentar o volume acima do nível recomendado?"\n"Ouvir em volume alto durante longos períodos de tempo poderá prejudicar a sua audição."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Mantenha os dois dedos para ativar a acessibilidade."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Acessibilidade ativada."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Acessibilidade cancelada."</string> <string name="user_switched" msgid="3768006783166984410">"<xliff:g id="NAME">%1$s</xliff:g> do utilizador atual."</string> <string name="owner_name" msgid="2716755460376028154">"Proprietário"</string> + <string name="error_message_title" msgid="4510373083082500195">"Erro"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Esta aplicação não suporta contas de utilizadores limitados"</string> + <string name="app_not_found" msgid="3429141853498927379">"Não foram encontradas aplicações para executar esta ação"</string> </resources> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 873c9db..6b0d18a 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Acesso direto ao microfone para gravação de áudio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Acesso direto à câmera para captura de imagens ou vídeo."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Tela de bloqueio"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Capacidade de afetar o comportamento da tela de bloqueio no dispositivo."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informações sobre seus aplicativos"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Capacidade de afetar o comportamento de outros aplicativos no dispositivo."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Plano de fundo"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Ações de texto"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> em execução"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> está em execução"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Cancelar"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Digite o PIN obrigatório:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"O tablet desconectará temporariamente da rede Wi-Fi enquanto estiver conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"O telefone desconectará temporariamente da rede Wi-Fi enquanto estiver conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Inserir caractere"</string> <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensagens SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear."\n\n" Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remover"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Aumentar o volume acima do nível recomendado?"\n"A audição em volume elevado por períodos longos pode prejudicar sua audição."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Mantenha pressionado com dois dedos para ativar a acessibilidade."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Acessibilidade ativada."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Acessibilidade cancelada."</string> <string name="user_switched" msgid="3768006783166984410">"Usuário atual <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Proprietário"</string> + <string name="error_message_title" msgid="4510373083082500195">"Erro"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"O aplicativo não suporta contas para usuários limitados"</string> + <string name="app_not_found" msgid="3429141853498927379">"Nenhum aplicativo encontrado para executar a ação"</string> </resources> diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml index e934a08..99fd59d 100644 --- a/core/res/res/values-rm/strings.xml +++ b/core/res/res/values-rm/strings.xml @@ -2391,4 +2391,10 @@ <!-- no translation found for owner_name (2716755460376028154) --> <!-- no translation found for owner_name (3879126011135546571) --> <skip /> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index c44b362..b11cc2f 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Acces direct la microfon pentru înregistrări audio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera foto"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Acces direct la camera foto pentru a realiza fotografii şi videoclipuri."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Blocare ecran"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Capacitatea de a afecta comportamentul ecranului de blocare pe dispozitivul dvs."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informaţiile despre aplicaţiile dvs."</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Capacitatea de a influenţa comportamentul altor aplicaţii de pe dispozitiv."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Imaginea de fundal"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Acţiuni pentru text"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spaţiul de stocare aproape ocupat"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Este posibil ca unele funcţii de sistem să nu funcţioneze"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează acum"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Anulaţi"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Către:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduceţi codul PIN necesar:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Cod PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tableta se va deconecta temporar de la rețeaua Wi-Fi cât timp este conectată la <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonul se va deconecta temporar de la reţeaua Wi-Fi cât timp este conectat la <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Introduceţi caracterul"</string> <string name="sms_control_title" msgid="7296612781128917719">"Se trimit mesaje SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi telefonul cu ajutorul unui cont de e-mail."\n\n" Încercaţi din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminaţi"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Ridicați volumul mai sus de nivelul recomandat?"\n"Ascultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Menţineţi două degete pe ecran pentru a activa accesibilitatea."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"S-a activat accesibilitatea."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accesibilitatea a fost anulată"</string> <string name="user_switched" msgid="3768006783166984410">"Utilizator curent: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Proprietar"</string> + <string name="error_message_title" msgid="4510373083082500195">"Eroare"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Această aplicație nu acceptă conturile pentru utilizatori cu permisiuni limitate"</string> + <string name="app_not_found" msgid="3429141853498927379">"Nicio aplicație pentru gestionarea acestei acțiuni"</string> </resources> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 7152bb8..3da7211 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Прямой доступ к микрофону для записи звука."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Прямой доступ к камере для фото- и видеосъемки."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Блокировка экрана"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Возможность управлять блокировкой экрана на устройстве."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Информация о приложениях"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Возможность влиять на поведение других приложений на устройстве."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Обои"</string> @@ -1050,18 +1048,16 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Операции с текстом"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Заканчивается свободное место"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некоторые системные функции могут не работать"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> выполняется"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> выполняется в данный момент"</string> <string name="ok" msgid="5970060430562524910">"ОК"</string> <string name="cancel" msgid="6442560571259935130">"Отмена"</string> <string name="yes" msgid="5362982303337969312">"ОК"</string> <string name="no" msgid="5141531044935541497">"Отмена"</string> <string name="dialog_alert_title" msgid="2049658708609043103">"Внимание"</string> <string name="loading" msgid="7933681260296021180">"Загрузка…"</string> - <string name="capital_on" msgid="1544682755514494298">"ВКЛ"</string> - <string name="capital_off" msgid="6815870386972805832">"ВЫКЛ"</string> + <string name="capital_on" msgid="1544682755514494298">"I"</string> + <string name="capital_off" msgid="6815870386972805832">"O"</string> <string name="whichApplication" msgid="4533185947064773386">"Что использовать?"</string> <string name="alwaysUse" msgid="4583018368000610438">"По умолчанию для этого действия"</string> <string name="clearDefaultHintMsg" msgid="3252584689512077257">"Удаляет настройки по умолчанию в меню \"Настройки > Приложения > Загруженные\"."</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Кому:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введите PIN-код:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"При подключении к устройству <xliff:g id="DEVICE_NAME">%1$s</xliff:g> планшетный ПК будет временно отключаться от сети Wi-Fi"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"При подключении к устройству <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон будет временно отключаться от сети Wi-Fi"</string> <string name="select_character" msgid="3365550120617701745">"Введите символ"</string> <string name="sms_control_title" msgid="7296612781128917719">"Отправка SMS-сообщений"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток для разблокировки телефона потребуется войти в аккаунт Google."\n\n"Повтор через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Удалить"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Увеличить громкость до небезопасного уровня?"\n"Долговременное прослушивание на такой громкости может повредить слух."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Чтобы включить специальные возможности, удерживайте пальцы на экране."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Специальные возможности включены."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Специальные возможности не будут включены."</string> <string name="user_switched" msgid="3768006783166984410">"Выбран аккаунт пользователя <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Владелец"</string> + <string name="error_message_title" msgid="4510373083082500195">"Ошибка"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Приложение не поддерживает аккаунты с ограниченным доступом"</string> + <string name="app_not_found" msgid="3429141853498927379">"Невозможно обработать это действие"</string> </resources> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 6f95ff6..9ffe30d 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Priamy prístup k mikrofónu na záznam zvuku."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparát"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Priamy prístup k fotoaparátu na nasnímanie fotografií alebo natočenie videí."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Uzamknúť obrazovku"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Možnosť ovplyvniť správanie zámky obrazovky na vašom zariadení."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informácie o vašich aplikáciách"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Schopnosť ovplyvniť správanie ďalších aplikácií na vašom zariadení."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Tapeta"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Operácie s textom"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nedostatok ukladacieho priestoru"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektoré systémové funkcie nemusia fungovať"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je spustená"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je práve spustená"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Zrušiť"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Komu:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadajte požadovaný kód PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tablet bude počas pripojenia k zariadeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> od siete Wi-Fi dočasne odpojený."</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefón bude počas pripojenia k zariadeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> od siete Wi-Fi dočasne odpojený."</string> <string name="select_character" msgid="3365550120617701745">"Vkladanie znakov"</string> <string name="sms_control_title" msgid="7296612781128917719">"Odosielanie správ SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou e-mailového účtu."\n\n" Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odstrániť"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Zvýšiť hlasitosť nad odporúčanú úroveň?"\n"Dlhodobé počúvanie pri vysokej hlasitosti môže poškodiť váš sluch."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Zjednodušenie ovládania povolíte dlhým stlačením dvoma prstami."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Zjednodušenie ovládania je povolené."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Zjednodušenie ovládania bolo zrušené."</string> <string name="user_switched" msgid="3768006783166984410">"Aktuálny používateľ je <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Vlastník"</string> + <string name="error_message_title" msgid="4510373083082500195">"Chyba"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Táto aplikácia nepodporuje účty v prípade používateľov s obmedzením"</string> + <string name="app_not_found" msgid="3429141853498927379">"Aplikácia potrebná na spracovanie tejto akcie sa nenašla"</string> </resources> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 1486341..ac1b6ad 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Neposreden dostop do mikrofona za snemanje zvoka."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparat"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Neposreden dostop do fotoaparata za fotografiranje ali snemanje videoposnetkov."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Zaklepanje zaslona"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Lahko vpliva na delovanje zaklepanja zaslona v napravi."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Podatki o vaših aplikacijah"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Zmožnost vpliva na delovanje drugih aplikacij v napravi."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Slika za ozadje"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Besedilna dejanja"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Prostor za shranjevanje bo pošel"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nekatere sistemske funkcije morda ne delujejo"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Izvaja se aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Trenutno se izvaja aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="ok" msgid="5970060430562524910">"V redu"</string> <string name="cancel" msgid="6442560571259935130">"Prekliči"</string> <string name="yes" msgid="5362982303337969312">"V redu"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Za:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Vnesite zahtevano kodo PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tablični računalnik bo začasno prekinil povezavo z Wi-Fi-jem, medtem ko je povezan z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon bo začasno prekinil povezavo z Wi-Fi-jem, ko je povezan z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Vstavljanje znaka"</string> <string name="sms_control_title" msgid="7296612781128917719">"Pošiljanje sporočil SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vzorec za odklepanje ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat napačno vnesli. Po nadaljnjih <xliff:g id="NUMBER_1">%d</xliff:g> neuspešnih poskusih boste pozvani, da odklenete telefon z Googlovimi podatki za prijavo."\n\n"Poskusite znova čez <xliff:g id="NUMBER_2">%d</xliff:g> s."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odstrani"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Želite povečati glasnost nad varno raven?"\n"Dolgotrajna izpostavljenost glasnemu predvajanju lahko poškoduje sluh."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Če želite omogočiti pripomočke za ljudi s posebnimi potrebami, na zaslonu pridržite z dvema prstoma."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Pripomočki za ljudi s posebnimi potrebami so omogočeni."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Omogočanje pripomočkov za ljudi s posebnimi potrebami preklicano."</string> <string name="user_switched" msgid="3768006783166984410">"Trenutni uporabnik <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Lastnik"</string> + <string name="error_message_title" msgid="4510373083082500195">"Napaka"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ta aplikacija ne podpira računov za uporabnike z omejitvami"</string> + <string name="app_not_found" msgid="3429141853498927379">"Najdena ni bila nobena aplikacija za izvedbo tega dejanja"</string> </resources> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index a1b869f..7e8cf01 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Директан приступ микрофону за снимање звука."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Директан приступ камери за снимање слика или видео снимака."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Закључавање екрана"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Могућност да утиче на понашање закључаног екрана на уређају."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Информације о апликацијама"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Способност да се утиче на понашање других апликација на уређају."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Позадина"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Радње у вези са текстом"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Простор за складиштење је на измаку"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Неке системске функције можда не функционишу"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> је покренута"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> је тренутно покренута"</string> <string name="ok" msgid="5970060430562524910">"Потврди"</string> <string name="cancel" msgid="6442560571259935130">"Откажи"</string> <string name="yes" msgid="5362982303337969312">"Потврди"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Коме:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Унесите потребни PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Таблет ће привремено прекинути везу са Wi-Fi-јем док је повезан са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Телефон ће привремено прекинути везу са Wi-Fi-јем док је повезан са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Уметање знака"</string> <string name="sms_control_title" msgid="7296612781128917719">"Слање SMS порука"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу налога е-поште."\n\n"Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Уклони"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Желите ли да појачате звук преко препорученог нивоа?"\n"Ако слушате гласну музику током дужег периода, може да дође до оштећења слуха."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Држите са два прста да бисте омогућили приступачност."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Приступачност је омогућена."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Приступачност је отказана."</string> <string name="user_switched" msgid="3768006783166984410">"Актуелни корисник <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Власник"</string> + <string name="error_message_title" msgid="4510373083082500195">"Грешка"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ова апликација не подржава налоге за кориснике са ограничењем"</string> + <string name="app_not_found" msgid="3429141853498927379">"Није пронађена ниједна апликација која би могла да обави ову радњу"</string> </resources> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 4c98712..5a54f71 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direktåtkomst till mikrofonen för att spela in ljud."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direktåtkomst till kamera för att ta bilder eller spela in video."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Låsa skärmen"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Möjlighet att påverka funktionen för enhetens låsskärm."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Information i dina appar"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Kan påverka beteendet hos andra appar på enheten."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Bakgrund"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Textåtgärder"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lagringsutrymmet börjar ta slut"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Det kan hända att vissa systemfunktioner inte fungerar"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Avbryt"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Till:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ange den obligatoriska PIN-koden:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kod:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Surfplattans Wi-Fi-anslutning kommer tillfälligt att avbrytas när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Infoga tecken"</string> <string name="sms_control_title" msgid="7296612781128917719">"Skickar SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ta bort"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Vill du höja volymen över den rekommenderade nivån?"\n"Om du lyssnar på hög volym under långa perioder kan din hörsel skadas."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Fortsätt trycka med två fingrar om du vill aktivera tillgänglighetsläget."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Tillgänglighetsläget har aktiverats."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Byte till tillgänglighetsläge avbrutet."</string> <string name="user_switched" msgid="3768006783166984410">"Nuvarande användare: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Ägare"</string> + <string name="error_message_title" msgid="4510373083082500195">"Fel"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Appen har inte stöd för användarkonton med begränsningar"</string> + <string name="app_not_found" msgid="3429141853498927379">"Ingen app som kan hantera åtgärden hittades"</string> </resources> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index d1e4270..96b013f 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Kufikia moja kwa moja kipokea sauti ili kurekodi sauti."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Kufikia moja kwa moja kamera ya kunasa taswira au video."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Funga skrini"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Uwezo wa kuathiri tabia ya skrini iliyofungwa kwenye kifaa chako."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Taarifa ya programu zako"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Uwezo wa kuathiri tabia ya programu nyingine kwenye kifaa chako."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Taswira"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Vitendo vya maandishi"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nafasi ya kuhafadhi inakwisha"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaendeshwa"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaendeshwa kwa sasa"</string> <string name="ok" msgid="5970060430562524910">"Sawa"</string> <string name="cancel" msgid="6442560571259935130">"Ghairi"</string> <string name="yes" msgid="5362982303337969312">"Sawa"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kwa:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Charaza PIN inayohitajika:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Kompyuta ndogo itaukata muunganisho kwa muda kutoka kwenye Wi-Fi inapokuwa imeunganishwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Simu itaukata muunganisho kwa muda kutoka kwenye Wi-Fi inapokuwa imeunganishwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Ingiza kibambo"</string> <string name="sms_control_title" msgid="7296612781128917719">"Inatuma ujumbe wa SMS"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Umekosea kuchora mchoro wako wa kufungua mara <xliff:g id="NUMBER_0">%d</xliff:g>. Baada ya majaribio <xliff:g id="NUMBER_1">%d</xliff:g> yasiyofaulu, utaombwa kufungua simu yako kwa kutumia akaunti ya barua pepe."\n\n" Jaribu tena baada ya sekunde <xliff:g id="NUMBER_2">%d</xliff:g>."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ondoa"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Iongeza sauti zaidi ya kiwango kinachopendekezwa?"\n"Kusikiliza kwa sauti ya juu kwa muda mrefu kunaweza kuharibu uwezo wako wa kusikia."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Endelea kushikilia chini kwa vidole vyako viwili ili kuwezesha ufikivu."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Ufikivu umewezeshwa."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ufikivu umeghairiwa."</string> <string name="user_switched" msgid="3768006783166984410">"Mtumiaji wa sasa <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Mmiliki"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 4bca1b1..43fdeac 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"เข้าถึงไมโครโฟนเพื่อบันทึกเสียงโดยตรง"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"กล้องถ่ายรูป"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"เข้าถึงกล้องถ่ายรูปเพื่อดูภาพและวิดีโอที่ถ่ายไว้โดยตรง"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"ล็อกหน้าจอ"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"ความสามารถในการส่งผลกระทบต่อพฤติกรรมของหน้าจอล็อกบนอุปกรณ์"</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"ข้อมูลแอปพลิเคชันของคุณ"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"สามารถส่งผลต่อการทำงานของแอปพลิเคชันอื่นในอุปกรณ์ของคุณ"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"วอลเปเปอร์"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"การทำงานของข้อความ"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังทำงาน"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังทำงานในขณะนี้"</string> <string name="ok" msgid="5970060430562524910">"ตกลง"</string> <string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string> <string name="yes" msgid="5362982303337969312">"ตกลง"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"ถึง:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"พิมพ์ PIN ที่ต้องการ:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"แท็บเล็ตนี้จะยกเลิกการเชื่อมต่อกับ Wi-Fi ชั่วคราวในขณะที่เชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"โทรศัพท์จะยกเลิกการเชื่อมต่อกับ Wi-Fi ชั่วคราวในขณะที่เชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"ใส่อักขระ"</string> <string name="sms_control_title" msgid="7296612781128917719">"กำลังส่งข้อความ SMS"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้อง <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว หากทำไม่สำเร็จอีก <xliff:g id="NUMBER_1">%d</xliff:g> ครั้ง ระบบจะขอให้คุณปลดล็อกโทรศัพท์โดยใช้ับัญชีอีเมล"\n\n" โปรดลองอีกครั้งในอีก <xliff:g id="NUMBER_2">%d</xliff:g> วินาที"</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"นำออก"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"ในกรณีที่ต้องการเพิ่มระดับเสียงจนเกินระดับที่แนะนำ"\n"การฟังเสียงดังเป็นเวลานานอาจทำให้การได้ยินของคุณบกพร่องได้"</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"ใช้สองนิ้วแตะค้างไว้เพื่อเปิดใช้งานการเข้าถึง"</string> <string name="accessibility_enabled" msgid="1381972048564547685">"เปิดใช้งานการเข้าถึงแล้ว"</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"ยกเลิกการเข้าถึงแล้ว"</string> <string name="user_switched" msgid="3768006783166984410">"ผู้ใช้ปัจจุบัน <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="owner_name" msgid="2716755460376028154">"เจ้าของ"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 00c9ef8..beeb6c0 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direktang access sa mikropono upang mag-record ng audio."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direktang access sa camera para sa pagkuha ng larawan o video."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"I-lock ang screen"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Kakayahang maapektuhan ang pagkilos ng lock screen sa iyong device."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Ang impormasyon ng iyong mga application"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Kakayahang maapektuhan ang pag-uugali ng iba pang mga application sa iyong device."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Wallpaper"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Pagkilos ng teksto"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"Tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"Kasalukuyang tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Kanselahin"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kay:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"I-type ang kinakailangang PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Pansamantalang madidiskoneta ang tablet sa Wi-Fi habang nakakonekta ito sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Pansamantalang madidiskoneta ang telepono sa Wi-Fi habang nakakonekta ito sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Magpasok ng character"</string> <string name="sms_control_title" msgid="7296612781128917719">"Nagpapadala ng mga SMS na mensahe"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang telepono mo gamit ang isang email account."\n\n" Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Alisin"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Lakasan ang volume nang lagpas sa ligtas na antas?"\n"Maaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Panatilihing nakapindot nang matagal ang iyong dalawang daliri upang paganahin ang pagiging naa-access."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Pinagana ang accessibility."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Nakansela ang pagiging naa-access."</string> <string name="user_switched" msgid="3768006783166984410">"Kasalukuyang user <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"May-ari"</string> + <string name="error_message_title" msgid="4510373083082500195">"Error"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Hindi sinusuportahan ng application na ito ang mga account para sa mga limitadong user"</string> + <string name="app_not_found" msgid="3429141853498927379">"Walang nakitang application na mangangasiwa sa pagkilos na ito"</string> </resources> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 5595a7a..2fddf7d 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Ses kaydetmek için mikrofona doğrudan erişim."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Resim ve video kaydı için kameraya doğrudan erişim."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Kilit ekranı"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Cihazınızdaki kilit ekranının çalışma biçimini etkileyebilme özelliği."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Uygulama bilgileriniz"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Cihazınızdaki diğer uygulamaların davranışlarını etkileyebilme."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Duvar Kağıdı"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Metin eylemleri"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Depolama alanı bitiyor"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bazı sistem işlevleri çalışmayabilir"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> çalışıyor"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> şu anda çalışıyor"</string> <string name="ok" msgid="5970060430562524910">"Tamam"</string> <string name="cancel" msgid="6442560571259935130">"İptal"</string> <string name="yes" msgid="5362982303337969312">"Tamam"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Alıcı:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Gerekli PIN\'i yazın:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tablet <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaza bağlıyken Kablosuz ağ bağlantısı geçici olarak kesilecektir"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaza bağlıyken Kablosuz ağ bağlantısı geçici olarak kesilecektir"</string> <string name="select_character" msgid="3365550120617701745">"Karakter ekle"</string> <string name="sms_control_title" msgid="7296612781128917719">"SMS mesajları gönderiliyor"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra telefonunuzu bir e-posta hesabı kullanarak açmanız istenir."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Kaldır"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Ses düzeyi önerilen seviyenin üzerine çıkarılsın mı?"\n"Uzun süre yüksek sesle dinlemek işitme duyunuza zarar verebilir."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Erişilebilirliği etkinleştirmek için iki parmağınızı basılı tutmaya devam edin."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Erişilebilirlik etkinleştirildi."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Erişilebilirlik iptal edildi."</string> <string name="user_switched" msgid="3768006783166984410">"Geçerli kullanıcı: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Sahibi"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 49bc0c3..6f505b1 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Безпосередній доступ до мікрофона для запису звуку."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Безпосередній доступ до камери для здійснення фото- чи відеозйомки."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Екран блокування"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Можливість впливати на поведінку екрана блокування вашого пристрою."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Інформація про програми"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Здатність впливати на роботу інших програм на пристрої."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Фоновий малюнок"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Дії з текстом"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> працює"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> зараз працює"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Скасувати"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Кому:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введіть потрібний PIN-код:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Під час з’єднання з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g> планшетний ПК тимчасово від’єднається від мережі Wi-Fi"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Під час з’єднання з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон тимчасово від’єднається від мережі Wi-Fi"</string> <string name="select_character" msgid="3365550120617701745">"Вставл-ня символу"</string> <string name="sms_control_title" msgid="7296612781128917719">"Надсил. SMS повідомлень"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі з’явиться запит розблокувати телефон за допомогою облікового запису електронної пошти."\n\n" Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Вилучити"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Збільшити гучність понад рекомендований рівень?"\n"Якщо слухати надто гучну музику тривалий час, можна пошкодити слух."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Утримуйте двома пальцями, щоб увімкнути доступність."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Доступність увімкнено."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Доступність скасовано."</string> <string name="user_switched" msgid="3768006783166984410">"Поточний користувач: <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Власник"</string> + <string name="error_message_title" msgid="4510373083082500195">"Помилка"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ця програма не підтримує облікові записи для обмежених користувачів"</string> + <string name="app_not_found" msgid="3429141853498927379">"Не знайдено програму для обробки цієї дії"</string> </resources> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 48e3fca..4fe8d65 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Truy cập trực tiếp vào micrô để ghi âm."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Máy ảnh"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Truy cập trực tiếp vào máy ảnh để chụp ảnh hoặc quay video."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Khóa màn hình"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Có thể ảnh hưởng đến thao tác của màn hình khóa trên thiết bị của bạn."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Thông tin về các ứng dụng của bạn"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Khả năng ảnh hưởng tới hoạt động của các ứng dụng khác trên thiết bị của bạn."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Hình nền"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Tác vụ văn bản"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Sắp hết dung lượng lưu trữ"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Một số chức năng hệ thống có thể không hoạt động"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang chạy"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện đang chạy"</string> <string name="ok" msgid="5970060430562524910">"OK"</string> <string name="cancel" msgid="6442560571259935130">"Hủy"</string> <string name="yes" msgid="5362982303337969312">"OK"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Người nhận:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Nhập PIN bắt buộc:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Mã PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Máy tính bảng sẽ tạm thời ngắt kết nối khỏi Wi-Fi trong khi máy tính bảng được kết nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Điện thoại sẽ tạm thời ngắt kết nối khỏi Wi-Fi trong khi điện thoại được kết nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Chèn ký tự"</string> <string name="sms_control_title" msgid="7296612781128917719">"Đang gửi tin nhắn SMS"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng tài khoản email."\n\n" Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Xóa"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Tăng âm lượng trên mức được đề xuất?"\n"Nghe ở mức âm lượng cao trong thời gian dài có thể gây hại cho thính giác của bạn."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Tiếp tục giữ hai ngón tay để bật trợ năng."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Trợ năng đã được bật."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Đã hủy trợ năng."</string> <string name="user_switched" msgid="3768006783166984410">"Người dùng hiện tại <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Chủ sở hữu"</string> + <string name="error_message_title" msgid="4510373083082500195">"Lỗi"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"Ứng dụng này không hỗ trợ tài khoản cho người dùng giới hạn"</string> + <string name="app_not_found" msgid="3429141853498927379">"Không tìm thấy ứng dụng nào để xử lý tác vụ này"</string> </resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index ccc66fb..af1a2ea 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"直接使用麦克风以录制音频。"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"相机"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"直接使用相机以拍摄图片或视频。"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"锁定屏幕"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"影响设备的锁定屏幕。"</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"您的应用信息"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"能够影响设备上其他应用的行为。"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"壁纸"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"文字操作"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"存储空间不足"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"某些系统功能可能无法正常使用"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g>正在运行"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前正在运行"</string> <string name="ok" msgid="5970060430562524910">"确定"</string> <string name="cancel" msgid="6442560571259935130">"取消"</string> <string name="yes" msgid="5362982303337969312">"确定"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"收件人:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"键入所需的 PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"平板电脑连接到“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”时会暂时断开与 Wi-Fi 的连接"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"手机连接到<xliff:g id="DEVICE_NAME">%1$s</xliff:g>时会暂时断开与 Wi-Fi 的连接。"</string> <string name="select_character" msgid="3365550120617701745">"插入字符"</string> <string name="sms_control_title" msgid="7296612781128917719">"正在发送短信"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁手机。"\n\n"请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"删除"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"将音量调高到推荐级别以上?"\n"长时间聆听高音量可能会损伤听力。"</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"持续按住双指即可启用辅助功能。"</string> <string name="accessibility_enabled" msgid="1381972048564547685">"辅助功能已启用。"</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"已取消辅助功能。"</string> <string name="user_switched" msgid="3768006783166984410">"当前用户是<xliff:g id="NAME">%1$s</xliff:g>。"</string> <string name="owner_name" msgid="2716755460376028154">"机主"</string> + <string name="error_message_title" msgid="4510373083082500195">"错误"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"此应用不支持受限用户的帐户"</string> + <string name="app_not_found" msgid="3429141853498927379">"找不到可处理此操作的应用"</string> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 53f578a..ed2551d 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"直接使用麥克風錄音。"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"相機"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"直接使用相機拍照或錄影。"</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"鎖定畫面"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"可影響裝置的鎖定畫面運作方式。"</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"您的應用程式資訊"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"影響裝置上其他應用程式的行為。"</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"桌布"</string> @@ -510,7 +508,7 @@ <string name="permlab_locationUpdates" msgid="7785408253364335740">"控制位置更新通知"</string> <string name="permdesc_locationUpdates" msgid="1120741557891438876">"允許應用程式啟用/停用來自無線電的位置更新通知 (不建議一般應用程式使用)。"</string> <string name="permlab_checkinProperties" msgid="7855259461268734914">"存取登機選項"</string> - <string name="permdesc_checkinProperties" msgid="4024526968630194128">"允許讀取/寫入由簽入服務上載的內容 (不建議一般應用程式使用)。"</string> + <string name="permdesc_checkinProperties" msgid="4024526968630194128">"允許讀取/寫入由簽入服務上傳的內容 (不建議一般應用程式使用)。"</string> <string name="permlab_bindGadget" msgid="776905339015863471">"選擇小工具"</string> <string name="permdesc_bindGadget" msgid="8261326938599049290">"允許應用程式告知系統哪個應用程式可以使用哪些小工具。啟用這項權限後,應用程式即會讓其他應用程式使用個人資料 (不建議一般應用程式使用)。"</string> <string name="permlab_modifyPhoneState" msgid="8423923777659292228">"修改手機狀態"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"文字動作"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在執行"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前正在執行"</string> <string name="ok" msgid="5970060430562524910">"確定"</string> <string name="cancel" msgid="6442560571259935130">"取消"</string> <string name="yes" msgid="5362982303337969312">"確定"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"收件者:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"請輸入必要的 PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"平板電腦與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 連線期間將暫時中斷 Wi-Fi 連線"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"手機與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 連線期間將暫時中斷 WiFi 連線"</string> <string name="select_character" msgid="3365550120617701745">"插入字元"</string> <string name="sms_control_title" msgid="7296612781128917719">"傳送 SMS 簡訊"</string> @@ -1475,11 +1470,13 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除手機的鎖定狀態。"\n\n"請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"移除"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"要將音量調高到建議等級以上嗎?"\n"長時間聆聽偏高音量可能會損害您的聽力。"</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"持續用兩指按住即可啟用協助工具。"</string> <string name="accessibility_enabled" msgid="1381972048564547685">"協助工具已啟用。"</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"協助工具已取消。"</string> <string name="user_switched" msgid="3768006783166984410">"目前的使用者是 <xliff:g id="NAME">%1$s</xliff:g>。"</string> <string name="owner_name" msgid="2716755460376028154">"擁有者"</string> + <string name="error_message_title" msgid="4510373083082500195">"錯誤"</string> + <string name="app_no_restricted_accounts" msgid="5322164210667258876">"這個應用程式不支援受限的使用者帳戶。"</string> + <string name="app_not_found" msgid="3429141853498927379">"找不到可以處理這個動作的應用程式"</string> </resources> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 18f603c..a104729 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -200,10 +200,8 @@ <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Ukufinyelela okuqondile ku-microphone ukuze uqophe umsindo."</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Ikhamela"</string> <string name="permgroupdesc_camera" msgid="2933667372289567714">"Ukufinyelela okuqondile kukhamera ekuthwebuleni isithombe noma ividiyo."</string> - <!-- no translation found for permgrouplab_screenlock (8275500173330718168) --> - <skip /> - <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) --> - <skip /> + <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Khiya isikrini"</string> + <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Ikhono lokwazi ukuthinta ukuziphatha kwesikrini sokukhiya kudivayisi yakho."</string> <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Ulwazi lezinhlelo zakho zokusebenza"</string> <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Amandla okuthinta ukuziphatha kwezinhlelo zokusebenza kudivayisi yakho."</string> <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Isithombe sangemuva"</string> @@ -1050,10 +1048,8 @@ <string name="editTextMenuTitle" msgid="4909135564941815494">"Izenzo zombhalo"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Isikhala sokulondoloza siyaphela"</string> <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string> - <!-- no translation found for app_running_notification_title (4625479411505090209) --> - <skip /> - <!-- no translation found for app_running_notification_text (3368349329989620597) --> - <skip /> + <string name="app_running_notification_title" msgid="4625479411505090209">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyasebenza"</string> + <string name="app_running_notification_text" msgid="3368349329989620597">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> isebenza manje"</string> <string name="ok" msgid="5970060430562524910">"KULUNGILE"</string> <string name="cancel" msgid="6442560571259935130">"Khansela"</string> <string name="yes" msgid="5362982303337969312">"KULUNGILE"</string> @@ -1147,8 +1143,7 @@ <string name="wifi_p2p_to_message" msgid="248968974522044099">"Ku:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Faka i-PIN edingekayo:"</string> <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string> - <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) --> - <skip /> + <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Ithebulethi izonqamuka okwesikhashana ku-Wi-Fi ngenkathi ixhumeke ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Ifoni izonqamuka okwesikhashana ku-Wi-Fi ngenkathi ixhumeke ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="select_character" msgid="3365550120617701745">"Faka uhlamvu"</string> <string name="sms_control_title" msgid="7296612781128917719">"Ithumela imiyalezo ye-SMS"</string> @@ -1475,11 +1470,16 @@ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-<xliff:g id="NUMBER_0">%d</xliff:g> Emva kweminye imizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google"\n\n" Zame futhi emumva kwengu- <xliff:g id="NUMBER_2">%d</xliff:g> imizuzwana."</string> <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Susa"</string> - <!-- no translation found for safe_media_volume_warning (7324161939475478066) --> - <skip /> + <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Khulisa ivolomu ngaphezu kwezinga elinconyiwe?"\n"Ukulalela ngevolomu ephezulu izikhathi ezinde kungalimaza ukuzwa kwakho."</string> <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Gcina ucindezele iminwe yakho emibili ukuze unike amandla ukufinyelela."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Ukufinyelela kunikwe amandla."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ukufinyelela kukhanseliwe."</string> <string name="user_switched" msgid="3768006783166984410">"Umsebenzisi wamanje <xliff:g id="NAME">%1$s</xliff:g>."</string> <string name="owner_name" msgid="2716755460376028154">"Umnikazi"</string> + <!-- no translation found for error_message_title (4510373083082500195) --> + <skip /> + <!-- no translation found for app_no_restricted_accounts (5322164210667258876) --> + <skip /> + <!-- no translation found for app_not_found (3429141853498927379) --> + <skip /> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 5282df3..8c7a374 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2092,11 +2092,11 @@ <!-- Don't use a layer. --> <enum name="none" value="0" /> <!-- Use a software layer. Refer to - {@link android.view.View#setLayerType(int, android.graphics.Paint) for + {@link android.view.View#setLayerType(int, android.graphics.Paint)} for more information. --> <enum name="software" value="1" /> <!-- Use a hardware layer. Refer to - {@link android.view.View#setLayerType(int, android.graphics.Paint) for + {@link android.view.View#setLayerType(int, android.graphics.Paint)} for more information. --> <enum name="hardware" value="2" /> </attr> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 6f59817..0afe4c1 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -889,6 +889,11 @@ <!-- Declare that your application will be able to deal with RTL (right to left) layouts. If set to false (default value), your application will not care about RTL layouts. --> <attr name="supportsRtl" format="boolean" /> + <!-- Declare that this application requires access to restricted accounts of a certain + type. The default value is null and restricted accounts won\'t be visible to this + application. The type should correspond to the account authenticator type, such as + "com.google" --> + <attr name="restrictedAccountType" format="string"/> </declare-styleable> <!-- The <code>permission</code> tag declares a security permission that can be @@ -991,9 +996,8 @@ permission, and it must always be granted when it is installed. If you set this to false, then in some cases the application may be installed with it being granted the permission, and it will - need to request the permission later if it needs it. + need to request the permission later if it needs it. --> <attr name="required" format="boolean" /> - --> </declare-styleable> <!-- The <code>uses-configuration</code> tag specifies @@ -1036,7 +1040,7 @@ don't support it. If you set this to false, then this will not impose a restriction on where the application can be installed. --> - <attr name="required" format="boolean" /> + <attr name="required" /> </declare-styleable> <!-- The <code>uses-sdk</code> tag describes the SDK features that the diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 489a947..42d692f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2041,6 +2041,7 @@ <public type="attr" name="indicatorEnd" /> <public type="attr" name="childIndicatorStart" /> <public type="attr" name="childIndicatorEnd" /> + <public type="attr" name="restrictedAccountType" /> <public type="style" name="Theme.NoTitleBar.Overscan" /> <public type="style" name="Theme.Light.NoTitleBar.Overscan" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 9e10661..4a15967 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4063,5 +4063,10 @@ <string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string> <!-- Default name of the owner user [CHAR LIMIT=20] --> <string name="owner_name" msgid="3879126011135546571">Owner</string> - + <!-- Error message title [CHAR LIMIT=35] --> + <string name="error_message_title">Error</string> + <!-- Message informing user that app is not permitted to access accounts. [CHAR LIMIT=none] --> + <string name="app_no_restricted_accounts">This application does not support accounts for limited users</string> + <!-- Message informing user that the requested activity could not be found [CHAR LIMIT=none] --> + <string name="app_not_found">No application found to handle this action</string> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 81baaf8..ced0851 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -869,7 +869,7 @@ <java-symbol type="string" name="config_chooseAccountActivity" /> <java-symbol type="string" name="config_chooseTypeAndAccountActivity" /> <java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" /> - + <java-symbol type="string" name="error_message_title" /> <java-symbol type="plurals" name="abbrev_in_num_days" /> <java-symbol type="plurals" name="abbrev_in_num_hours" /> @@ -1121,6 +1121,7 @@ <java-symbol type="layout" name="sms_short_code_confirmation_dialog" /> <java-symbol type="layout" name="keyguard_add_widget" /> <java-symbol type="layout" name="action_bar_up_container" /> + <java-symbol type="layout" name="app_not_authorized" /> <java-symbol type="anim" name="slide_in_child_bottom" /> <java-symbol type="anim" name="slide_in_right" /> @@ -1141,6 +1142,7 @@ <java-symbol type="xml" name="time_zones_by_country" /> <java-symbol type="xml" name="sms_short_codes" /> <java-symbol type="xml" name="audio_assets" /> + <java-symbol type="xml" name="global_keys" /> <java-symbol type="raw" name="accessibility_gestures" /> <java-symbol type="raw" name="incognito_mode_start_page" /> diff --git a/core/res/res/xml/global_keys.xml b/core/res/res/xml/global_keys.xml new file mode 100644 index 0000000..8fa6902 --- /dev/null +++ b/core/res/res/xml/global_keys.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- Mapping of keycodes to components which will be handled globally. + Modify this file to add global keys. + A global key will NOT go to the foreground application and instead only ever be sent via targeted + broadcast to the specified component. The action of the intent will be + android.intent.action.GLOBAL_BUTTON and the KeyEvent will be included in the intent as + android.intent.extra.KEY_EVENT. +--> + +<global_keys version="1"> + <!-- Example format: keyCode = keycode to handle globally. component = component which will handle this key. --> + <!-- <key keyCode="KEYCODE_VOLUME_UP" component="com.android.example.keys/.VolumeKeyHandler" /> --> +</global_keys> diff --git a/core/res/res/xml/kg_password_kbd_numeric.xml b/core/res/res/xml/kg_password_kbd_numeric.xml index 93b32af..93b32af 100755..100644 --- a/core/res/res/xml/kg_password_kbd_numeric.xml +++ b/core/res/res/xml/kg_password_kbd_numeric.xml diff --git a/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java b/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java index cc8c4a6..37495e1 100644 --- a/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java +++ b/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java @@ -18,64 +18,51 @@ package android.content.pm; import android.os.Parcel; import android.test.AndroidTestCase; -import android.util.Base64; -import java.util.jar.Attributes; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; public class ManifestDigestTest extends AndroidTestCase { - private static final byte[] DIGEST_1 = { + private static final byte[] MESSAGE_1 = { (byte) 0x00, (byte) 0xAA, (byte) 0x55, (byte) 0xFF }; - private static final String DIGEST_1_STR = Base64.encodeToString(DIGEST_1, Base64.DEFAULT); - - private static final byte[] DIGEST_2 = { - (byte) 0x0A, (byte) 0xA5, (byte) 0xF0, (byte) 0x5A - }; - - private static final String DIGEST_2_STR = Base64.encodeToString(DIGEST_2, Base64.DEFAULT); - - private static final Attributes.Name SHA1_DIGEST = new Attributes.Name("SHA1-Digest"); - - private static final Attributes.Name MD5_DIGEST = new Attributes.Name("MD5-Digest"); - - public void testManifestDigest_FromAttributes_Null() { + public void testManifestDigest_FromInputStream_Null() { assertNull("Attributes were null, so ManifestDigest.fromAttributes should return null", - ManifestDigest.fromAttributes(null)); + ManifestDigest.fromInputStream(null)); } - public void testManifestDigest_FromAttributes_NoAttributes() { - Attributes a = new Attributes(); + public void testManifestDigest_FromInputStream_ThrowsIoException() { + InputStream is = new InputStream() { + @Override + public int read() throws IOException { + throw new IOException(); + } + }; - assertNull("There were no attributes to extract, so ManifestDigest should be null", - ManifestDigest.fromAttributes(a)); + assertNull("InputStream threw exception, so ManifestDigest should be null", + ManifestDigest.fromInputStream(is)); } - public void testManifestDigest_FromAttributes_SHA1PreferredOverMD5() { - Attributes a = new Attributes(); - a.put(SHA1_DIGEST, DIGEST_1_STR); - - a.put(MD5_DIGEST, DIGEST_2_STR); - - ManifestDigest fromAttributes = ManifestDigest.fromAttributes(a); - - assertNotNull("A valid ManifestDigest should be returned", fromAttributes); + public void testManifestDigest_Equals() throws Exception { + InputStream is = new ByteArrayInputStream(MESSAGE_1); - ManifestDigest created = new ManifestDigest(DIGEST_1); + ManifestDigest expected = + new ManifestDigest(MessageDigest.getInstance("SHA-256").digest(MESSAGE_1)); - assertEquals("SHA-1 should be preferred over MD5: " + created.toString() + " vs. " - + fromAttributes.toString(), created, fromAttributes); + ManifestDigest actual = ManifestDigest.fromInputStream(is); + assertEquals(expected, actual); - assertEquals("Hash codes should be the same: " + created.toString() + " vs. " - + fromAttributes.toString(), created.hashCode(), fromAttributes - .hashCode()); + ManifestDigest unexpected = new ManifestDigest(new byte[0]); + assertFalse(unexpected.equals(actual)); } - public void testManifestDigest_Parcel() { - Attributes a = new Attributes(); - a.put(SHA1_DIGEST, DIGEST_1_STR); + public void testManifestDigest_Parcel() throws Exception { + InputStream is = new ByteArrayInputStream(MESSAGE_1); - ManifestDigest digest = ManifestDigest.fromAttributes(a); + ManifestDigest digest = ManifestDigest.fromInputStream(is); Parcel p = Parcel.obtain(); digest.writeToParcel(p, 0); diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java index 274ac6b..d6a7ee2 100644 --- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java +++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java @@ -33,14 +33,41 @@ public class LinkPropertiesTest extends TestCase { private static String GATEWAY2 = "69.78.8.1"; private static String NAME = "qmi0"; + public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) { + // Check implementation of equals(), element by element. + assertTrue(source.isIdenticalInterfaceName(target)); + assertTrue(target.isIdenticalInterfaceName(source)); + + assertTrue(source.isIdenticalAddresses(target)); + assertTrue(target.isIdenticalAddresses(source)); + + assertTrue(source.isIdenticalDnses(target)); + assertTrue(target.isIdenticalDnses(source)); + + assertTrue(source.isIdenticalRoutes(target)); + assertTrue(target.isIdenticalRoutes(source)); + + assertTrue(source.isIdenticalHttpProxy(target)); + assertTrue(target.isIdenticalHttpProxy(source)); + + assertTrue(source.isIdenticalStackedLinks(target)); + assertTrue(target.isIdenticalStackedLinks(source)); + + // Check result of equals(). + assertTrue(source.equals(target)); + assertTrue(target.equals(source)); + + // Check hashCode. + assertEquals(source.hashCode(), target.hashCode()); + } + @SmallTest public void testEqualsNull() { LinkProperties source = new LinkProperties(); LinkProperties target = new LinkProperties(); assertFalse(source == target); - assertTrue(source.equals(target)); - assertTrue(source.hashCode() == target.hashCode()); + assertLinkPropertiesEqual(source, target); } @SmallTest @@ -73,8 +100,7 @@ public class LinkPropertiesTest extends TestCase { target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1))); target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2))); - assertTrue(source.equals(target)); - assertTrue(source.hashCode() == target.hashCode()); + assertLinkPropertiesEqual(source, target); target.clear(); // change Interface Name @@ -163,8 +189,7 @@ public class LinkPropertiesTest extends TestCase { target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY2))); target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress(GATEWAY1))); - assertTrue(source.equals(target)); - assertTrue(source.hashCode() == target.hashCode()); + assertLinkPropertiesEqual(source, target); } catch (Exception e) { fail(); } @@ -191,8 +216,7 @@ public class LinkPropertiesTest extends TestCase { target.addLinkAddress(new LinkAddress( NetworkUtils.numericToInetAddress(ADDRV6), 128)); - assertTrue(source.equals(target)); - assertTrue(source.hashCode() == target.hashCode()); + assertLinkPropertiesEqual(source, target); } catch (Exception e) { fail(); } diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index e02e95a..7f8d5f0 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -152,7 +152,7 @@ font_src_files += \ RobotoCondensed-Italic.ttf \ RobotoCondensed-BoldItalic.ttf \ DroidNaskh-Regular.ttf \ - DroidNaskh-Regular-SystemUI.ttf \ + DroidNaskhUI-Regular.ttf \ DroidSansDevanagari-Regular.ttf \ DroidSansHebrew-Regular.ttf \ DroidSansHebrew-Bold.ttf \ diff --git a/data/fonts/DroidNaskh-Regular-SystemUI.ttf b/data/fonts/DroidNaskhUI-Regular.ttf Binary files differindex c961de2..c961de2 100644 --- a/data/fonts/DroidNaskh-Regular-SystemUI.ttf +++ b/data/fonts/DroidNaskhUI-Regular.ttf diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml index 999ddc4..16d760c 100644 --- a/data/fonts/fallback_fonts.xml +++ b/data/fonts/fallback_fonts.xml @@ -36,7 +36,7 @@ </family> <family> <fileset> - <file variant="compact">DroidNaskh-Regular-SystemUI.ttf</file> + <file variant="compact">DroidNaskhUI-Regular.ttf</file> </fileset> </family> <family> @@ -78,32 +78,60 @@ </family> <family> <fileset> - <file>AnjaliNewLipi-light.ttf</file> + <file variant="elegant">NotoSansMalayalam-Regular.ttf</file> + <file variant="elegant">NotoSansMalayalam-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file>Lohit-Bengali.ttf</file> + <file variant="compact">NotoSansMalayalamUI-Regular.ttf</file> + <file variant="compact">NotoSansMalayalamUI-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file>Lohit-Kannada.ttf</file> + <file variant="elegant">NotoSansBengali-Regular.ttf</file> + <file variant="elegant">NotoSansBengali-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file>NanumGothic.ttf</file> + <file variant="compact">NotoSansBengaliUI-Regular.ttf</file> + <file variant="compact">NotoSansBengaliUI-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file>AndroidEmoji.ttf</file> + <file variant="elegant">NotoSansTelugu-Regular.ttf</file> + <file variant="elegant">NotoSansTelugu-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file variant="compact">NotoSansTeluguUI-Regular.ttf</file> + <file variant="compact">NotoSansTeluguUI-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file variant="elegant">NotoSansKannada-Regular.ttf</file> + <file variant="elegant">NotoSansKannada-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file>Lohit-Telugu.ttf</file> + <file variant="compact">NotoSansKannadaUI-Regular.ttf</file> + <file variant="compact">NotoSansKannadaUI-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>NanumGothic.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>AndroidEmoji.ttf</file> </fileset> </family> <family> diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index 875795a..7c2f955 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -33,7 +33,7 @@ PRODUCT_PACKAGES := \ RobotoCondensed-Italic.ttf \ RobotoCondensed-BoldItalic.ttf \ DroidNaskh-Regular.ttf \ - DroidNaskh-Regular-SystemUI.ttf \ + DroidNaskhUI-Regular.ttf \ DroidSansDevanagari-Regular.ttf \ DroidSansHebrew-Regular.ttf \ DroidSansHebrew-Bold.ttf \ diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk index e403205..8b03bf7 100644 --- a/data/sounds/AllAudio.mk +++ b/data/sounds/AllAudio.mk @@ -1,5 +1,4 @@ -# -# Copyright (C) 2009 The Android Open Source Project +# Copyright 2013 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,13 +11,222 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# -$(call inherit-product, frameworks/base/data/sounds/OriginalAudio.mk) -$(call inherit-product, frameworks/base/data/sounds/AudioPackage2.mk) -$(call inherit-product, frameworks/base/data/sounds/AudioPackage3.mk) -$(call inherit-product, frameworks/base/data/sounds/AudioPackage4.mk) -$(call inherit-product, frameworks/base/data/sounds/AudioPackage5.mk) -$(call inherit-product, frameworks/base/data/sounds/AudioPackage6.mk) -$(call inherit-product, frameworks/base/data/sounds/AudioPackage7.mk) -$(call inherit-product, frameworks/base/data/sounds/AudioPackage7alt.mk) +LOCAL_PATH := frameworks/base/data/sounds + +PRODUCT_COPY_FILES += \ + $(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \ + $(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \ + $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \ + $(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \ + $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \ + $(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \ + $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \ + $(LOCAL_PATH)/alarms/ogg/Barium.ogg:system/media/audio/alarms/Barium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \ + $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \ + $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \ + $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \ + $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \ + $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Promethium.ogg:system/media/audio/alarms/Promethium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \ + $(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \ + $(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \ + $(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \ + $(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \ + $(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \ + $(LOCAL_PATH)/notifications/ogg/Antimony.ogg:system/media/audio/notifications/Antimony.ogg \ + $(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \ + $(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \ + $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \ + $(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \ + $(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:system/media/audio/notifications/Beryllium.ogg \ + $(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \ + $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \ + $(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \ + $(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \ + $(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \ + $(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \ + $(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:system/media/audio/notifications/Cobalt.ogg \ + $(LOCAL_PATH)/notifications/Cricket.ogg:system/media/audio/notifications/Cricket.ogg \ + $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \ + $(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \ + $(LOCAL_PATH)/notifications/Doink.ogg:system/media/audio/notifications/Doink.ogg \ + $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \ + $(LOCAL_PATH)/notifications/Drip.ogg:system/media/audio/notifications/Drip.ogg \ + $(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \ + $(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \ + $(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \ + $(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \ + $(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:system/media/audio/notifications/Fluorine.ogg \ + $(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \ + $(LOCAL_PATH)/notifications/ogg/Gallium.ogg:system/media/audio/notifications/Gallium.ogg \ + $(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \ + $(LOCAL_PATH)/notifications/ogg/Helium.ogg:system/media/audio/notifications/Helium.ogg \ + $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \ + $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \ + $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \ + $(LOCAL_PATH)/notifications/ogg/Krypton.ogg:system/media/audio/notifications/Krypton.ogg \ + $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \ + $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \ + $(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \ + $(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \ + $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \ + $(LOCAL_PATH)/notifications/ogg/Palladium.ogg:system/media/audio/notifications/Palladium.ogg \ + $(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:system/media/audio/notifications/Plastic_Pipe.ogg \ + $(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \ + $(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \ + $(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \ + $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \ + $(LOCAL_PATH)/notifications/ogg/Radon.ogg:system/media/audio/notifications/Radon.ogg \ + $(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:system/media/audio/notifications/Rubidium.ogg \ + $(LOCAL_PATH)/notifications/ogg/Selenium.ogg:system/media/audio/notifications/Selenium.ogg \ + $(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \ + $(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \ + $(LOCAL_PATH)/notifications/SpaceSeed.ogg:system/media/audio/notifications/SpaceSeed.ogg \ + $(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \ + $(LOCAL_PATH)/notifications/ogg/Strontium.ogg:system/media/audio/notifications/Strontium.ogg \ + $(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \ + $(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \ + $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \ + $(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \ + $(LOCAL_PATH)/notifications/ogg/Thallium.ogg:system/media/audio/notifications/Thallium.ogg \ + $(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \ + $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \ + $(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \ + $(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \ + $(LOCAL_PATH)/notifications/ogg/Xenon.ogg:system/media/audio/notifications/Xenon.ogg \ + $(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:system/media/audio/notifications/Zirconium.ogg \ + $(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \ + $(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \ + $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \ + $(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \ + $(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \ + $(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \ + $(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \ + $(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \ + $(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \ + $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \ + $(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \ + $(LOCAL_PATH)/newwavelabs/Backroad.ogg:system/media/audio/ringtones/Backroad.ogg \ + $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \ + $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \ + $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \ + $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \ + $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \ + $(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \ + $(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \ + $(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \ + $(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \ + $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \ + $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \ + $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \ + $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \ + $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \ + $(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \ + $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \ + $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \ + $(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \ + $(LOCAL_PATH)/newwavelabs/Ding.ogg:system/media/audio/ringtones/Ding.ogg \ + $(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \ + $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \ + $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \ + $(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:system/media/audio/ringtones/Enter_the_Nexus.ogg \ + $(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \ + $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \ + $(LOCAL_PATH)/ringtones/FreeFlight.ogg:system/media/audio/ringtones/FreeFlight.ogg \ + $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \ + $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \ + $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \ + $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \ + $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \ + $(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \ + $(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \ + $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \ + $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \ + $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \ + $(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \ + $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \ + $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \ + $(LOCAL_PATH)/newwavelabs/Nairobi.ogg:system/media/audio/ringtones/Nairobi.ogg \ + $(LOCAL_PATH)/newwavelabs/Nassau.ogg:system/media/audio/ringtones/Nassau.ogg \ + $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \ + $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \ + $(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \ + $(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \ + $(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \ + $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \ + $(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \ + $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \ + $(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \ + $(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \ + $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \ + $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \ + $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \ + $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \ + $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \ + $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \ + $(LOCAL_PATH)/newwavelabs/Safari.ogg:system/media/audio/ringtones/Safari.ogg \ + $(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \ + $(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \ + $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \ + $(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \ + $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \ + $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \ + $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \ + $(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \ + $(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \ + $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \ + $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \ + $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \ + $(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \ + $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \ + $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \ + $(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg \ + $(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg \ + $(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \ + $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \ + $(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \ + $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \ + $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \ + $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \ + $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \ + $(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \ + $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \ + $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \ + $(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \ + $(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \ + $(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \ + $(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \ + $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \ + diff --git a/data/sounds/generate-all-audio.sh b/data/sounds/generate-all-audio.sh new file mode 100755 index 0000000..6f42f5a --- /dev/null +++ b/data/sounds/generate-all-audio.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# Copyright 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script regenerates AllAudio.mk based on the content of the other +# makefiles. + +# It needs to be run from its location in the source tree. + +cat > AllAudio.mk << EOF +# Copyright 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := frameworks/base/data/sounds + +PRODUCT_COPY_FILES += \\ +EOF + +cat OriginalAudio.mk AudioPackage*.mk | + grep \\\$\(LOCAL_PATH\).*: | + cut -d : -f 2 | + cut -d \ -f 1 | + sort -u | + while read DEST + do + echo -n \ \ \ \ >> AllAudio.mk + cat *.mk | + grep \\\$\(LOCAL_PATH\).*:$DEST | + tr -d \ \\t | + cut -d : -f 1 | + sort -u | + tail -n 1 | + tr -d \\n >> AllAudio.mk + echo :$DEST\ \\ >> AllAudio.mk + done +echo >> AllAudio.mk diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd index b2d50ce..e17a0fd 100644 --- a/docs/html/about/dashboards/index.jd +++ b/docs/html/about/dashboards/index.jd @@ -1,107 +1,65 @@ page.title=Dashboards -header.hide=1 @jd:body - - -<h2 id="Platform">Platform Versions</h2> - -<p>This page provides data about the relative number of active devices -running a given version of the Android platform. This can help you -understand the landscape of device distribution and decide how to prioritize -the development of your application features for the devices currently in -the hands of users. For information about how to target your application to devices based on -platform version, read about <a -href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API levels</a>.</p> - - -<h3 id="PlatformCurrent">Current Distribution</h3> - -<p>The following pie chart and table is based on the number of Android devices that have accessed -Google Play within a 14-day period ending on the data collection date noted below.</p> - -<div class="col-5" style="margin-left:0"> - - -<table> -<tr> - <th>Version</th> - <th>Codename</th> - <th>API</th> - <th>Distribution</th> -</tr> -<tr><td><a href="/about/versions/android-1.6.html">1.6</a></td><td>Donut</td> <td>4</td><td>0.2%</td></tr> -<tr><td><a href="/about/versions/android-2.1.html">2.1</a></td><td>Eclair</td> <td>7</td><td>1.9%</td></tr> -<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td> <td>8</td><td>7.5%</td></tr> -<tr><td><a href="/about/versions/android-2.3.html">2.3 - 2.3.2</a> - </td><td rowspan="2">Gingerbread</td> <td>9</td><td>0.2%</td></tr> -<tr><td><a href="/about/versions/android-2.3.3.html">2.3.3 - 2.3.7 - </a></td><!-- Gingerbread --> <td>10</td><td>43.9%</td></tr> -<tr><td><a href="/about/versions/android-3.1.html">3.1</a></td> - <td rowspan="2">Honeycomb</td> <td>12</td><td>0.3%</td></tr> -<tr><td><a href="/about/versions/android-3.2.html">3.2</a></td> <!-- Honeycomb --><td>13</td><td>0.9%</td></tr> -<tr><td><a href="/about/versions/android-4.0.3.html">4.0.3 - 4.0.4</a></td> - <td>Ice Cream Sandwich</td><td>15</td><td>28.6%</td></tr> -<tr><td><a href="/about/versions/android-4.1.html">4.1</a></td> - <td rowspan="2">Jelly Bean</td><td>16</td><td>14.9%</td></tr> -<tr><td><a href="/about/versions/android-4.2.html">4.2</a></td><!--Jelly Bean--> <td>17</td><td>1.6%</td></tr> -</table> - +<style> +div.chart, +div.screens-chart { + display:none; +} +tr .total { + background-color:transparent; + border:0; + color:#666; +} +tr th.total { + font-weight:bold; +} +</style> + + + + +<div class="sidebox"> +<h2>Google Play Install Stats</h2> +<p>The Google Play Developer Console also provides <a +href="{@docRoot}distribute/googleplay/about/distribution.html#stats">detailed statistics</a> +about your users' devices. Those stats may help you prioritize the device profiles for which +you optimize your app.</p> </div> -<div class="col-8" style="margin-right:0"> -<img style="margin-left:30px" alt="" -src="//chart.apis.google.com/chart?&cht=p&chs=460x245&chf=bg,s,00000000&chd=t:2.1,7.5,44.1,1.2,28.6,16.5&chl=Eclair%20%26%20older|Froyo|Gingerbread|Honeycomb|Ice%20Cream%20Sandwich|Jelly%20Bean&chco=c4df9b,6fad0c" -/> - -</div><!-- end dashboard-panel --> - -<p style="clear:both"><em>Data collected during a 14-day period ending on March 4, 2013</em></p> -<!-- -<p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p> ---> - -<h3 id="PlatformHistorical">Historical Distribution</h3> - -<p>The following stacked line graph provides a history of the relative number of -active Android devices running different versions of the Android platform. It also provides a -valuable perspective of how many devices your application is compatible with, based on the -platform version.</p> - -<p>Notice that the platform versions are stacked on top of each other with the oldest active -version at the top. This format indicates the total percent of active devices that are compatible -with a given version of Android. For example, if you develop your application for -the version that is at the very top of the chart, then your application is -compatible with 100% of active devices (and all future versions), because all Android APIs are -forward compatible. Or, if you develop your application for a version lower on the chart, -then it is currently compatible with the percentage of devices indicated on the y-axis, where the -line for that version meets the y-axis on the right.</p> - -<p>Each dataset in the timeline is based on the number of Android devices that accessed -Google Play within a 14-day period ending on the date indicated on the x-axis.</p> - -<img alt="" height="250" width="660" -src="//chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,00000000&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C09/01%7C09/15%7C10/01%7C10/15%7C11/01%7C11/15%7C12/01%7C12/15%7C01/01%7C01/15%7C02/01%7C02/15%7C03/01%7C1%3A%7C2012%7C%7C%7C%7C%7C%7C%7C%7C2013%7C%7C%7C%7C2013%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.3,99.4,99.5,99.5,99.5,99.6,100.0,100.0,100.0,100.0,100.0,100.0,100.0|95.6,95.8,96.1,96.3,96.4,96.7,96.9,97.2,97.4,97.4,97.6,97.7,97.9|81.4,82.3,83.2,83.8,84.7,85.6,86.4,87.0,88.2,88.8,89.4,89.9,90.3|23.7,25.5,27.4,28.7,31.1,33.0,35.4,36.8,40.3,42.0,43.6,45.1,46.0|21.5,23.5,25.5,26.8,29.4,31.4,33.8,35.2,38.8,40.7,42.3,43.9,44.8|1.1,1.4,1.8,2.1,3.2,4.8,6.5,7.5,9.9,11.7,13.3,14.8,16.1&chm=b,c3df9b,0,1,0|tFroyo,689326,1,0,15,,t::-5|b,b4db77,1,2,0|tGingerbread,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|b,96dd28,3,4,0|tIce%20Cream%20Sandwich,293f07,4,0,15,,t::-5|b,83c916,4,5,0|tJelly%20Bean,131d02,5,9,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Eclair|Froyo|Gingerbread|Honeycomb|Ice%20Cream%20Sandwich|Jelly%20Bean&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" -/> -<p><em>Last historical dataset collected during a 14-day period ending on March 1, 2013</em></p> - - - - - - - - +<p>This page provides information about the relative number of devices that share a certain +characteristic, such as Android version or screen size. This information may +help you prioritize efforts for <a +href="{@docRoot}training/basics/supporting-devices/index.html">supporting different devices</a>.</p> +<p>Each snapshot of data represents all the devices that visited the Google Play Store in the +prior 14 days.</p> +<p class="note"><strong>Note:</strong> Beginning in April, 2013, these charts are now built +using data collected from each device when the user visits the Google Play Store. Previously, the +data was collected when the device simply checked-in to Google servers. We believe the new +data more accurately reflects those users who are most engaged in the Android and Google Play +ecosystem.</p> +<h2 id="Platform">Platform Versions</h2> +<p>This section provides data about the relative number of devices running a given version of +the Android platform.</p> +<p>For information about how to target your application to devices based on +platform version, read <a +href="{@docRoot}training/basics/supporting-devices/platforms.html">Supporting Different +Platform Versions</a>.</p> +<div id="version-chart"> +</div> +<p style="clear:both"><em>Data collected during a 14-day period ending on April 2, 2013. +<br/>Any versions with less than 0.1% distribution are not shown.</em> +</p> @@ -111,72 +69,22 @@ src="//chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,000 <h2 id="Screens">Screen Sizes and Densities</h2> -<img alt="" style="float:right;" -src="//chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=Xlarge%7CLarge%7CNormal%7CSmall&chd=t%3A4.6,6.1,86.6,2.7" /> - - -<img alt="" style="float:right;clear:right" -src="//chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=ldpi%7Cmdpi%7Chdpi%7Cxhdpi&chd=t%3A2.2,18,51.1,28.7" /> - - -<p>This section provides data about the relative number of active devices that have a particular +<p>This section provides data about the relative number of devices that have a particular screen configuration, defined by a combination of screen size and density. To simplify the way that you design your user interfaces for different screen configurations, Android divides the range of -actual screen sizes and densities into:</p> - -<ul> -<li>A set of four generalized <strong>sizes</strong>: <em>small</em>, <em>normal</em>, -<em>large</em>, and <em>xlarge</em></em></li> -<li>A set of four generalized <strong>densities</strong>: <em>ldpi</em> (low), <em>mdpi</em> -(medium), <em>hdpi</em> (high), and <em>xhdpi</em> (extra high)</li> -</ul> +actual screen sizes and densities into several buckets as expressed by the table below.</p> <p>For information about how you can support multiple screen configurations in your -application, see <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple +application, read <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.</p> -<p class="note"><strong>Note:</strong> This data is based on the number -of Android devices that have accessed Google Play within a 7-day period -ending on the data collection date noted below.</p> - - -<table style="width:350px"> -<tr> -<th></th> -<th scope="col">ldpi</th> -<th scope="col">mdpi</th> -<th scope="col">hdpi</th> -<th scope="col">xhdpi</th> -</tr> -<tr><th scope="row">small</th> -<td>1.7%</td> <!-- small/ldpi --> -<td></td> <!-- small/mdpi --> -<td>1.0%</td> <!-- small/hdpi --> -<td></td> <!-- small/xhdpi --> -</tr> -<tr><th scope="row">normal</th> -<td>0.4%</td> <!-- normal/ldpi --> -<td>11%</td> <!-- normal/mdpi --> -<td>50.1%</td> <!-- normal/hdpi --> -<td>25.1%</td> <!-- normal/xhdpi --> -</tr> -<tr><th scope="row">large</th> -<td>0.1%</td> <!-- large/ldpi --> -<td>2.4%</td> <!-- large/mdpi --> -<td></td> <!-- large/hdpi --> -<td>3.6%</td> <!-- large/xhdpi --> -</tr> -<tr><th scope="row">xlarge</th> -<td></td> <!-- xlarge/ldpi --> -<td>4.6%</td> <!-- xlarge/mdpi --> -<td></td> <!-- xlarge/hdpi --> -<td></td> <!-- xlarge/xhdpi --> -</tr> -</table> -<p style="clear:both"><em>Data collected during a 7-day period ending on October 1, 2012</em></p> +<div id="screens-chart"> +</div> +<p style="clear:both"><em>Data collected during a 14-day period ending on April 2, 2013 +<br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p> @@ -187,14 +95,14 @@ ending on the data collection date noted below.</p> <h2 id="OpenGL">Open GL Version</h2> -<p>This section provides data about the relative number of active devices that support a particular +<p>This section provides data about the relative number of devices that support a particular version of OpenGL ES. Note that support for one particular version of OpenGL ES also implies support for any lower version (for example, support for version 2.0 also implies support for 1.1).</p> <img alt="" style="float:right" -src="//chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A9.2,90.8&chf=bg,s,00000000" /> +src="//chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A0.3,99.7&chf=bg,s,00000000" /> <p>To declare which version of OpenGL ES your application requires, you should use the {@code android:glEsVersion} attribute of the <a @@ -204,10 +112,6 @@ href="{@docRoot}guide/topics/manifest/supports-gl-texture-element.html">{@code <supports-gl-texture>}</a> element to declare the GL compression formats that your application uses.</p> -<p class="note"><strong>Note:</strong> This data is based on the number -of Android devices that have accessed Google Play within a 7-day period -ending on the data collection date noted below.</p> - <table style="width:350px"> <tr> @@ -216,14 +120,347 @@ ending on the data collection date noted below.</p> </tr> <tr> <td>1.1 only</th> -<td>9.2%</td> +<td>0.3%</td> </tr> <tr> <td>2.0 & 1.1</th> -<td>90.8%</td> +<td>99.7%</td> </tr> </table> -<p style="clear:both"><em>Data collected during a 7-day period ending on October 1, 2012</em></p> +<p style="clear:both"><em>Data collected during a 14-day period ending on April 2, 2013</em></p> + + + + + + + + + + + + + +<script> +var VERSION_DATA = +[ + { + "chart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A1.8%2C4.0%2C39.8%2C0.2%2C29.3%2C25.0&chl=Eclair%7CFroyo%7CGingerbread%7CHoneycomb%7CIce%20Cream%20Sandwich%7CJelly%20Bean&chs=500x250&cht=p&chco=c4df9b%2C6fad0c", + "data": [ + { + "api": 4, + "name": "Donut", + "perc": "0.1" + }, + { + "api": 7, + "name": "Eclair", + "perc": "1.7" + }, + { + "api": 8, + "name": "Froyo", + "perc": "4.0" + }, + { + "api": 9, + "name": "Gingerbread", + "perc": "0.1" + }, + { + "api": 10, + "name": "Gingerbread", + "perc": "39.7" + }, + { + "api": 13, + "name": "Honeycomb", + "perc": "0.2" + }, + { + "api": 15, + "name": "Ice Cream Sandwich", + "perc": "29.3" + }, + { + "api": 16, + "name": "Jelly Bean", + "perc": "23.0" + }, + { + "api": 17, + "name": "Jelly Bean", + "perc": "2.0" + } + ] + } +]; + + + + + +var SCREEN_DATA = +[ + { + "data": { + "Large": { + "hdpi": "0.5", + "ldpi": "0.7", + "mdpi": "2.7", + "tvdpi": "1.0", + "xhdpi": "0.8" + }, + "Normal": { + "hdpi": "37.9", + "ldpi": "0.1", + "mdpi": "16.1", + "xhdpi": "25.0", + "xxhdpi": "0.8" + }, + "Small": { + "ldpi": "9.5" + }, + "Xlarge": { + "hdpi": "0.1", + "ldpi": "0.1", + "mdpi": "4.6", + "xhdpi": "0.1" + } + }, + "densitychart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A10.4%2C23.4%2C1.0%2C38.5%2C25.9%2C0.8&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chs=400x250&cht=p&chco=c4df9b%2C6fad0c", + "layoutchart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A4.9%2C5.7%2C79.9%2C9.5&chl=Xlarge%7CLarge%7CNormal%7CSmall&chs=400x250&cht=p&chco=c4df9b%2C6fad0c" + } +]; + + + +var VERSION_NAMES = +[ + {"api":0},{"api":1},{"api":2},{"api":3}, + { + "api":4, + "link":"<a href='/about/versions/android-1.6.html'>1.6</a>", + "codename":"Donut", + }, + { "api":5}, + { "api":6}, + { + "api":7, + "link":"<a href='/about/versions/android-2.1.html'>2.1</a>", + "codename":"Eclair", + }, + { + "api":8, + "link":"<a href='/about/versions/android-2.2.html'>2.2</a>", + "codename":"Froyo" + }, + { + "api":9, + "link":"<a href='/about/versions/android-2.3.html'>2.3 -<br>2.3.2</a>", + "codename":"Gingerbread" + }, + { + "api":10, + "link":"<a href='/about/versions/android-2.3.3.html'>2.3.3 -<br>2.3.7</a>", + "codename":"Gingerbread" + }, + { "api":11}, + { + "api":12, + "link":"<a href='/about/versions/android-3.1.html'>3.1</a>", + "codename":"Honeycomb" + }, + { + "api":13, + "link":"<a href='/about/versions/android-3.2.html'>3.2</a>", + "codename":"Honeycomb" + }, + { "api":14}, + { + "api":15, + "link":"<a href='/about/versions/android-4.0.html'>4.0.3 -<br>4.0.4</a>", + "codename":"Ice Cream Sandwich" + }, + { + "api":16, + "link":"<a href='/about/versions/android-4.1.html'>4.1.x</a>", + "codename":"Jelly Bean" + }, + { + "api":17, + "link":"<a href='/about/versions/android-4.2.html'>4.2.x</a>", + "codename":"Jelly Bean" + } +]; + + + + + + + + + + + + + + + + + + + +$(document).ready(function(){ + // for each set of data (each month) + $.each(VERSION_DATA, function(i, set) { + + // set up wrapper divs + var $div = $('<div class="chart"' + + ((i == 0) ? ' style="display:block"' : '') + + ' >'); + var $divtable = $('<div class="col-5" style="margin-left:0">'); + var $divchart = $('<div class="col-8" style="margin-right:0">'); + + // set up a new table + var $table = $("<table>"); + var $trh = $("<tr><th>Version</th>" + + "<th>Codename</th>" + + "<th>API</th>" + + "<th>Distribution</th></tr>"); + $table.append($trh); + + // loop each data set (each api level represented in stats) + $.each(set.data, function(i, data) { + // check if we need to rowspan the codename + var rowspan = 1; + // must not be first row + if (i > 0) { + // if this row's codename is the same as previous row codename + if (data.name == set.data[i-1].name) { + rowspan = 0; + // otherwise, as long as this is not the last row + } else if (i < (set.data.length - 1)) { + // increment rowspan for each subsequent row w/ same codename + while (data.name == set.data[i+rowspan].name) { + rowspan++; + // unless we've reached the last row + if ((i + rowspan) >= set.data.length) break; + } + } + } + + // create table row and get corresponding version info from VERSION_NAMES + var $tr = $("<tr>"); + $tr.append("<td>" + VERSION_NAMES[data.api].link + "</td>"); + if (rowspan > 0) { + $tr.append("<td rowspan='" + rowspan + "'>" + VERSION_NAMES[data.api].codename + "</td>"); + } + $tr.append("<td>" + data.api + "</td>"); + $tr.append("<td>" + data.perc + "%</td>"); + $table.append($tr); + }); + + // create chart image + var $chart = $('<img style="margin-left:30px" alt="" src="' + set.chart + '" />'); + + // stack up and insert the elements + $divtable.append($table); + $divchart.append($chart); + $div.append($divtable).append($divchart); + $("#version-chart").append($div); + }); + + + + var SCREEN_SIZES = ["Small","Normal","Large","Xlarge"]; + var SCREEN_DENSITIES = ["ldpi","mdpi","tvdpi","hdpi","xhdpi","xxhdpi"]; + + + // for each set of screens data (each month) + $.each(SCREEN_DATA, function(i, set) { + + // set up wrapper divs + var $div = $('<div class="screens-chart"' + + ((i == 0) ? ' style="display:block"' : '') + + ' >'); + + // set up a new table + var $table = $("<table>"); + var $trh = $("<tr><th></th></tr>"); + $.each(SCREEN_DENSITIES, function(i, density) { + $trh.append("<th scope='col'>" + density + "</th>"); + }); + $trh.append("<th scope='col' class='total'>Total</th>"); + $table.append($trh); + + // array to hold totals for each density + var densityTotals = new Array(SCREEN_DENSITIES.length); + $.each(densityTotals, function(i, total) { + densityTotals[i] = 0; // make them all zero to start + }); + + // loop through each screen size + $.each(SCREEN_SIZES, function(i, size) { + // if there are any devices of this size + if (typeof set.data[size] != "undefined") { + // create table row and insert data + var $tr = $("<tr>"); + $tr.append("<th scope='row'>" + size + "</th>"); + // variable to sum all densities for this size + var total = 0; + // loop through each density + $.each(SCREEN_DENSITIES, function(i, density) { + var num = typeof set.data[size][density] != "undefined" ? set.data[size][density] : 0; + $tr.append("<td>" + (num != 0 ? num + "%" : "") + "</td>"); + total += parseFloat(num); + densityTotals[i] += parseFloat(num); + }) + $tr.append("<td class='total'>" + total.toFixed(1) + "%</td>"); + $table.append($tr); + } + }); + + // create row of totals for each density + var $tr = $("<tr><th class='total'>Total</th></tr>"); + $.each(densityTotals, function(i, total) { + $tr.append("<td class='total'>" + total.toFixed(1) + "%</td>"); + }); + $table.append($tr); + + // create charts + var $sizechart = $('<img style="float:left;width:380px" alt="" src="' + + set.layoutchart + '" />'); + var $densitychart = $('<img style="float:left;width:380px" alt="" src="' + + set.densitychart + '" />'); + + // stack up and insert the elements + $div.append($table).append($sizechart).append($densitychart); + $("#screens-chart").append($div); + }); + + +}); + + + +function changeVersionDate() { + var date = $('#date-versions option:selected').val(); + + $(".chart").hide(); + $(".chart."+date+"").show(); +} + + +function changeScreensVersionDate() { + var date = $('#date-screens option:selected').val(); + + $(".screens-chart").hide(); + $(".screens-chart."+date+"").show(); +} + +</script> diff --git a/docs/html/about/versions/jelly-bean.jd b/docs/html/about/versions/jelly-bean.jd index acb2538..71957be 100644 --- a/docs/html/about/versions/jelly-bean.jd +++ b/docs/html/about/versions/jelly-bean.jd @@ -904,7 +904,7 @@ style="font-weight:500;">App Widgets</span> can resize automatically to fit the <h3>Media codec access</h3> -<p>Android 4.1 provides low-level access to platform hardware and software codecs. Apps can query the system to discover what <strong>low-level media codecs</strong> are available on the device and then and use them in the ways they need. For example, you can now create multiple instances of a media codec, queue input buffers, and receive output buffers in return. In addition, the media codec framework supports protected content. Apps can query for an available codec that is able to play protected content with a DRM solution available on the the device.</p> +<p>Android 4.1 provides low-level access to platform hardware and software codecs. Apps can query the system to discover what <strong>low-level media codecs</strong> are available on the device and then and use them in the ways they need. For example, you can now create multiple instances of a media codec, queue input buffers, and receive output buffers in return. In addition, the media codec framework supports protected content. Apps can query for an available codec that is able to play protected content with a DRM solution available on the device.</p> <h3>USB Audio</h3> diff --git a/docs/html/distribute/googleplay/about/visibility.jd b/docs/html/distribute/googleplay/about/visibility.jd index 4957c0f..18f60e9 100644 --- a/docs/html/distribute/googleplay/about/visibility.jd +++ b/docs/html/distribute/googleplay/about/visibility.jd @@ -39,7 +39,7 @@ style="font-weight:500;">Growth in app consumption</span>: Users download more t </div> <div> -<p>Google Play is also a top destination for visitors from the the web. Anyone +<p>Google Play is also a top destination for visitors from the web. Anyone with a browser can explore everything that Google Play has to offer from its <a href="http://play.google.com/store">web site</a>. Android users can even buy and install the apps they want and Google Play pushes them automatically to their @@ -159,7 +159,7 @@ the editorial team will notice.</p> <h4>Featured and Staff Picks</h4> -<p>Each week the the Google Play editorial staff selects a new set of apps to +<p>Each week the Google Play editorial staff selects a new set of apps to promote in its popular <em>Featured</em> and <em>Staff Picks</em> collections. </p> diff --git a/docs/html/google/gcm/gcm.jd b/docs/html/google/gcm/gcm.jd index f8ba7b4..089baf5 100644 --- a/docs/html/google/gcm/gcm.jd +++ b/docs/html/google/gcm/gcm.jd @@ -777,10 +777,11 @@ sent. Optional. The default value is <code>false</code>, and must be a JSON bool <p class="note"><strong>Note:</strong> If your organization has a firewall that restricts the traffic to or -from the Internet, you need to configure it to allow connectivity with GCM. +from the Internet, you need to configure it to allow connectivity with GCM in order for +your Android devices to receive messages. The ports to open are: 5228, 5229, and 5230. GCM typically only uses 5228, but it sometimes uses 5229 and 5230. GCM doesn't provide specific IPs, so you should -allow your server to accept incoming connections from all IP addresses +allow your firewall to accept incoming connections from all IP addresses contained in the IP blocks listed in Google's ASN of 15169.</p> diff --git a/docs/html/google/play/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd index 3365cfc..57227a8 100644 --- a/docs/html/google/play/billing/billing_integrate.jd +++ b/docs/html/google/play/billing/billing_integrate.jd @@ -19,6 +19,7 @@ parent.link=index.html <li><a href="#Subs">Implementing Subscriptions</a><li> </ol> </li> + <li><a href="#billing-security">Securing Your App</a> </ol> <h2>Reference</h2> <ol> @@ -361,6 +362,34 @@ Bundle activeSubs = mService.getPurchases(3, "com.example.myapp", the user. Once a subscription expires without renewal, it will no longer appear in the returned {@code Bundle}.</p> +<h2 id="billing-security">Securing Your Application</h2> + +<p>To help ensure the integrity of the transaction information that is sent to +your application, Google Play signs the JSON string that contains the response +data for a purchase order. Google Play uses the private key that is associated +with your application in the Developer Console to create this signature. The +Developer Console generates an RSA key pair for each application.<p> + +<p class="note"><strong>Note:</strong>To find the public key portion of this key +pair, open your application's details in the Developer Console, then click on +<strong>Services & APIs</strong>, and look at the field titled +<strong>Your License Key for This Application</strong>.</p> + +<p>The Base64-encoded RSA public key generated by Google Play is in binary +encoded, X.509 subjectPublicKeyInfo DER SEQUENCE format. It is the same public +key that is used with Google Play licensing.</p> + +<p>When your application receives this signed response you can +use the public key portion of your RSA key pair to verify the signature. +By performing signature verification you can detect responses that have +been tampered with or that have been spoofed. You can perform this signature +verification step in your application; however, if your application connects +to a secure remote server then we recommend that you perform the signature +verification on that server.</p> + +<p>For more information about best practices for security and design, see <a +href="{@docRoot}google/play/billing/billing_best_practices.html">Security and Design</a>.</p> + diff --git a/docs/html/google/play/billing/billing_reference.jd b/docs/html/google/play/billing/billing_reference.jd index 1410e65..e168d70 100644 --- a/docs/html/google/play/billing/billing_reference.jd +++ b/docs/html/google/play/billing/billing_reference.jd @@ -143,7 +143,9 @@ does not include tax.</td> </tr> <tr> <td>{@code INAPP_DATA_SIGNATURE}</td> - <td>String containing the signature of the purchase data that was signed with the private key of the developer.</td> + <td>String containing the signature of the purchase data that was signed +with the private key of the developer. The data signature uses the +RSASSA-PKCS1-v1_5 scheme.</td> </tr> </table> </p> diff --git a/docs/html/google/play/billing/v2/billing_integrate.jd b/docs/html/google/play/billing/v2/billing_integrate.jd index defe265..defe265 100755..100644 --- a/docs/html/google/play/billing/v2/billing_integrate.jd +++ b/docs/html/google/play/billing/v2/billing_integrate.jd diff --git a/docs/html/google/play/licensing/adding-licensing.jd b/docs/html/google/play/licensing/adding-licensing.jd index 3f2460f..93561f6 100644 --- a/docs/html/google/play/licensing/adding-licensing.jd +++ b/docs/html/google/play/licensing/adding-licensing.jd @@ -853,37 +853,39 @@ sample application calls <code>checkAccess()</code> from a <h3 id="account-key">Embed your public key for licensing</h3> -<p>For each publisher account, the Google Play service automatically -generates a 2048-bit RSA public/private key pair that is used exclusively for -licensing. The key pair is uniquely associated with the publisher account and is -shared across all applications that are published through the account. Although -associated with a publisher account, the key pair is <em>not</em> the same as -the key that you use to sign your applications (or derived from it).</p> +<p>For each application, the Google Play service automatically +generates a 2048-bit RSA public/private key pair that is used for +licensing and in-app billing. The key pair is uniquely associated with the +application. Although associated with the application, the key pair is +<em>not</em> the same as the key that you use to sign your applications (or derived from it).</p> <p>The Google Play Developer Console exposes the public key for licensing to any -developer signed in to the publisher account, but it keeps the private key +developer signed in to the Developer Console, but it keeps the private key hidden from all users in a secure location. When an application requests a license check for an application published in your account, the licensing server -signs the license response using the private key of your account's key pair. +signs the license response using the private key of your application's key pair. When the LVL receives the response, it uses the public key provided by the application to verify the signature of the license response. </p> -<p>To add licensing to an application, you must obtain your publisher account's +<p>To add licensing to an application, you must obtain your application's public key for licensing and copy it into your application. Here's how to find -your account's public key for licensing:</p> +your application's public key for licensing:</p> <ol> <li>Go to the Google Play <a href="http://play.google.com/apps/publish">Developer Console</a> and sign in. Make sure that you sign in to the account from which the application you are licensing is published (or will be published). </li> -<li>In the account home page, locate the "Edit profile" link and click it. </li> -<li>In the Edit Profile page, locate the "Licensing" pane, shown below. Your -public key for licensing is given in the "Public key" text box. </li> +<li>In the application details page, locate the <strong>Services & APIs</strong> +link and click it. </li> +<li>In the <strong>Services & APIs</strong> page, locate the +<strong>Licensing & In-App Billing</strong> section. Your public key for +licensing is given in the +<strong>Your License Key For This Application</strong> field. </li> </ol> <p>To add the public key to your application, simply copy/paste the key string -from the text box into your application as the value of the String variable +from the field into your application as the value of the String variable <code>BASE64_PUBLIC_KEY</code>. When you are copying, make sure that you have selected the entire key string, without omitting any characters. </p> @@ -965,16 +967,6 @@ application that they have legitimately purchased on another device.</li> </ul> </div> - - - - - - - - - - <h2 id="app-obfuscation">Obfuscating Your Code</h2> <p>To ensure the security of your application, particularly for a paid diff --git a/docs/html/google/play/licensing/index.jd b/docs/html/google/play/licensing/index.jd index a13be10..6632fc0 100644 --- a/docs/html/google/play/licensing/index.jd +++ b/docs/html/google/play/licensing/index.jd @@ -16,7 +16,7 @@ restrict use of the application to a specific device, in addition to any other c <p>The licensing service is a secure means of controlling access to your applications. When an application checks the licensing status, the Google Play server signs the licensing status -response using a key pair that is uniquely associated with the publisher account. Your application +response using a key pair that is uniquely associated with the application. Your application stores the public key in its compiled <code>.apk</code> file and uses it to verify the licensing status response.</p> diff --git a/docs/html/google/play/licensing/licensing-reference.jd b/docs/html/google/play/licensing/licensing-reference.jd index 4240097..7bfa61a 100644 --- a/docs/html/google/play/licensing/licensing-reference.jd +++ b/docs/html/google/play/licensing/licensing-reference.jd @@ -186,7 +186,7 @@ licensing server, possibly because of network availability problems. </td> </tr> <tr> <td>{@code ERROR_SERVER_FAILURE}</td> -<td>Server error — the server could not load the publisher account's key +<td>Server error — the server could not load the application's key pair for licensing.</td> <td>No</td> <td></td> diff --git a/docs/html/google/play/licensing/overview.jd b/docs/html/google/play/licensing/overview.jd index 2434a4c..4e1a9c9 100644 --- a/docs/html/google/play/licensing/overview.jd +++ b/docs/html/google/play/licensing/overview.jd @@ -38,13 +38,13 @@ the licensing server and receives the result. The Google Play application sends the result to your application, which can allow or disallow further use of the application as needed.</p> -<p class="note"><strong>Note:</strong> If a paid application has been uploaded to Google Play but -saved only as a draft application (the app is unpublished), the licensing server considers all users -to be licensed users of the application (because it's not even possible to purchase the app). -This exception is necessary in order for you to perform testing of your licensing +<p class="note"><strong>Note:</strong> If a paid application has been uploaded +to Google Play, but saved only as a draft application (the app is +unpublished), the licensing server considers all users to be licensed users of +the application (because it's not even possible to purchase the app). This +exception is necessary in order for you to perform testing of your licensing implementation.</p> - <div class="figure" style="width:469px"> <img src="{@docRoot}images/licensing_arch.png" alt=""/> <p class="img-caption"><strong>Figure 1.</strong> Your application initiates a @@ -102,10 +102,11 @@ response data using an RSA key pair that is shared exclusively between the Googl server and you.</p> <p>The licensing service generates a single licensing key pair for each -publisher account and exposes the public key in your account's profile page. You must copy the -public key from the web site and embed it in your application source code. The server retains the -private key internally and uses it to sign license responses for the applications you -publish with that account.</p> +application and exposes the public key in your application's +<strong>Services & APIs</strong> page in the Developer Console. You must copy +the public key from the Developer Console and embed it in your application +source code. The server retains the private key internally and uses it to sign +license responses for the applications you publish with that account.</p> <p>When your application receives a signed response, it uses the embedded public key to verify the data. The use of public key cryptography in the licensing @@ -221,7 +222,7 @@ gives you wider distribution potential for your applications. </p> <p>Licensing lets you move to a license-based model that is enforceable on all devices that have access to Google Play. Access is not bound to the characteristics of the host device, but to your -publisher account on Google Play (through the app's public key) and the +application on Google Play (through the app's public key) and the licensing policy that you define. Your application can be installed and managed on any device on any storage, including SD card.</p> diff --git a/docs/html/guide/components/fragments.jd b/docs/html/guide/components/fragments.jd index 7747b31..32c9f99 100644 --- a/docs/html/guide/components/fragments.jd +++ b/docs/html/guide/components/fragments.jd @@ -172,7 +172,7 @@ the user might not come back).</dd> <p>Most applications should implement at least these three methods for every fragment, but there are several other callback methods you should also use to handle various stages of the -fragment lifecycle. All the lifecycle callback methods are discussed more later, in the section +fragment lifecycle. All the lifecycle callback methods are discussed in more detail in the section about <a href="#Lifecycle">Handling the Fragment Lifecycle</a>.</p> diff --git a/docs/html/guide/components/fundamentals.jd b/docs/html/guide/components/fundamentals.jd index 2c33a26..ce50022 100644 --- a/docs/html/guide/components/fundamentals.jd +++ b/docs/html/guide/components/fundamentals.jd @@ -345,7 +345,7 @@ receivers can be either declared in the manifest or created dynamically in code {@link android.content.BroadcastReceiver} objects) and registered with the system by calling {@link android.content.Context#registerReceiver registerReceiver()}.</p> -<p>For more about how to structure the manifest file for your application, see the <a +<p>For more about how to structure the manifest file for your application, see <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml File</a> documentation. </p> diff --git a/docs/html/guide/components/tasks-and-back-stack.jd b/docs/html/guide/components/tasks-and-back-stack.jd index ecaba8d..a21bf34 100644 --- a/docs/html/guide/components/tasks-and-back-stack.jd +++ b/docs/html/guide/components/tasks-and-back-stack.jd @@ -231,7 +231,7 @@ activities except for the root activity when the user leaves the task.</p> <activity>}</a> manifest element and with flags in the intent that you pass to {@link android.app.Activity#startActivity startActivity()}.</p> -<p>In this regard, the the principal <a +<p>In this regard, the principal <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> attributes you can use are:</p> @@ -319,7 +319,7 @@ each instance can belong to different tasks, and one task can have multiple inst routes the intent to that instance through a call to its {@link android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a new instance of the activity. The activity can be instantiated multiple times, each instance can -belong to different tasks, and one task can have multiple instances (but only if the the +belong to different tasks, and one task can have multiple instances (but only if the activity at the top of the back stack is <em>not</em> an existing instance of the activity). <p>For example, suppose a task's back stack consists of root activity A with activities B, C, and D on top (the stack is A-B-C-D; D is on top). An intent arrives for an activity of type D. diff --git a/docs/html/guide/practices/index.jd b/docs/html/guide/practices/index.jd index 04a43c5..48a849a 100644 --- a/docs/html/guide/practices/index.jd +++ b/docs/html/guide/practices/index.jd @@ -19,7 +19,7 @@ of fueling the impression-install-ranking cycle: improve the product!</p> <a href="http://android-developers.blogspot.com/2012/01/say-goodbye-to-menu-button.html"> <h4>Say Goodbye to the Menu Button</h4> - <p>As Ice Cream Sandwich rolls out to more devices, it?s important that you begin to migrate + <p>As Ice Cream Sandwich rolls out to more devices, it's important that you begin to migrate your designs to the action bar in order to promote a consistent Android user experience.</p> </a> @@ -49,4 +49,4 @@ make mistakes in the way they interact with the Android system and with other ap </div> -</div>
\ No newline at end of file +</div> diff --git a/docs/html/guide/practices/ui_guidelines/icon_design_action_bar.jd b/docs/html/guide/practices/ui_guidelines/icon_design_action_bar.jd index c12b789..9f835a7 100644 --- a/docs/html/guide/practices/ui_guidelines/icon_design_action_bar.jd +++ b/docs/html/guide/practices/ui_guidelines/icon_design_action_bar.jd @@ -96,7 +96,7 @@ finished Action Bar icon dimensions for each generalized screen density.</p> </th> </tr> <tr> - <th style="background-color:#f3f3f3;font-weight:normal"> + <th> Action Bar Icon Size </th> <td> diff --git a/docs/html/guide/practices/ui_guidelines/icon_design_dialog.jd b/docs/html/guide/practices/ui_guidelines/icon_design_dialog.jd index e02cdfc..a2c1459 100644 --- a/docs/html/guide/practices/ui_guidelines/icon_design_dialog.jd +++ b/docs/html/guide/practices/ui_guidelines/icon_design_dialog.jd @@ -51,36 +51,46 @@ for suggestions on how to work with multiple sets of icons.</p> <p class="table-caption"><strong>Table 1.</strong> Summary of finished dialog icon dimensions for each of the three generalized screen densities.</p> - <table> - <tbody> - <tr> - <th style="background-color:#f3f3f3;font-weight:normal"> - <nobr>Low density screen <em>(ldpi)</em></nobr> - </th> - <th style="background-color:#f3f3f3;font-weight:normal"> - <nobr>Medium density screen <em>(mdpi)</em></nobr> - </th> - <th style="background-color:#f3f3f3;font-weight:normal"> - <nobr>High density screen <em>(hdpi)</em><nobr> - </th> - </tr> - - <tr> - <td> - 24 x 24 px - </td> - <td> - 32 x 32 px - </td> - <td> - 48 x 48 px - </td> - </tr> - - </tbody> - </table> - - +<table> + <tbody> + <tr> + <th></th> + <th> + <code>ldpi</code> (120 dpi)<br> + <small style="font-weight: normal">(Low density screen)</small> + </th> + <th> + <code>mdpi</code> (160 dpi)<br> + <small style="font-weight: normal">(Medium density screen)</small> + </th> + <th> + <code>hdpi</code> (240 dpi)<br> + <small style="font-weight: normal">(High density screen)</small> + </th> + <th> + <code>xhdpi</code> (320 dpi)<br> + <small style="font-weight: normal">(Extra-high density screen)</small> + </th> + </tr> + <tr> + <th style="background-color:#f3f3f3;font-weight:normal"> + Dialog Icon Size + </th> + <td> + 24 x 24 px + </td> + <td> + 32 x 32 px + </td> + <td> + 48 x 48 px + </td> + <td> + 64 x 64 px + </td> + </tr> + </tbody> +</table> <p><strong>Final art must be exported as a transparent PNG file. Do not include a background color</strong>.</p> diff --git a/docs/html/guide/practices/ui_guidelines/icon_design_launcher.jd b/docs/html/guide/practices/ui_guidelines/icon_design_launcher.jd index 200c135..4ec56b1 100644 --- a/docs/html/guide/practices/ui_guidelines/icon_design_launcher.jd +++ b/docs/html/guide/practices/ui_guidelines/icon_design_launcher.jd @@ -213,7 +213,7 @@ finished launcher icon dimensions for each generalized screen density.</p> </th> </tr> <tr> - <th style="background-color:#f3f3f3;font-weight:normal"> + <th> Launcher Icon Size </th> <td> diff --git a/docs/html/guide/practices/ui_guidelines/icon_design_list.jd b/docs/html/guide/practices/ui_guidelines/icon_design_list.jd index 2fbce87..38ceb85 100644 --- a/docs/html/guide/practices/ui_guidelines/icon_design_list.jd +++ b/docs/html/guide/practices/ui_guidelines/icon_design_list.jd @@ -53,36 +53,46 @@ for suggestions on how to work with multiple sets of icons.</p> <p class="table-caption"><strong>Table 1.</strong> Summary of finished list view icon dimensions for each of the three generalized screen densities.</p> - <table> - <tbody> - <tr> - <th style="background-color:#f3f3f3;font-weight:normal"> - <nobr>Low density screen <em>(ldpi)</em></nobr> - </th> - <th style="background-color:#f3f3f3;font-weight:normal"> - <nobr>Medium density screen <em>(mdpi)</em></nobr> - </th> - <th style="background-color:#f3f3f3;font-weight:normal"> - <nobr>High density screen <em>(hdpi)</em><nobr> - </th> - </tr> - - <tr> - <td> - 24 x 24 px - </td> - <td> - 32 x 32 px - </td> - <td> - 48 x 48 px - </td> - </tr> - - </tbody> - </table> - - +<table> + <tbody> + <tr> + <th></th> + <th> + <code>ldpi</code> (120 dpi)<br> + <small style="font-weight: normal">(Low density screen)</small> + </th> + <th> + <code>mdpi</code> (160 dpi)<br> + <small style="font-weight: normal">(Medium density screen)</small> + </th> + <th> + <code>hdpi</code> (240 dpi)<br> + <small style="font-weight: normal">(High density screen)</small> + </th> + <th> + <code>xhdpi</code> (320 dpi)<br> + <small style="font-weight: normal">(Extra-high density screen)</small> + </th> + </tr> + <tr> + <th style="background-color:#f3f3f3;font-weight:normal"> + List View Icon Size + </th> + <td> + 24 x 24 px + </td> + <td> + 32 x 32 px + </td> + <td> + 48 x 48 px + </td> + <td> + 64 x 64 px + </td> + </tr> + </tbody> +</table> <p><strong>Final art must be exported as a transparent PNG file. Do not include a background color</strong>.</p> diff --git a/docs/html/guide/practices/ui_guidelines/icon_design_status_bar.jd b/docs/html/guide/practices/ui_guidelines/icon_design_status_bar.jd index 8c15777..4cd4db3 100644 --- a/docs/html/guide/practices/ui_guidelines/icon_design_status_bar.jd +++ b/docs/html/guide/practices/ui_guidelines/icon_design_status_bar.jd @@ -155,7 +155,7 @@ finished icon dimensions for each generalized screen density.</p> </th> </tr> <tr> - <th style="background-color:#f3f3f3;font-weight:normal"> + <th> Status Bar Icon Size<br><small>(Android 3.0 and Later)</small> </th> <td> diff --git a/docs/html/guide/topics/admin/device-admin.jd b/docs/html/guide/topics/admin/device-admin.jd index 4a325db..f917576 100644 --- a/docs/html/guide/topics/admin/device-admin.jd +++ b/docs/html/guide/topics/admin/device-admin.jd @@ -339,7 +339,7 @@ a label.</li> </code> is a permission that a {@link android.app.admin.DeviceAdminReceiver} subclass must have, to ensure that only the system can interact with the receiver (no application can be granted this permission). This prevents other applications from abusing your device admin app.</li> -<li><code>android.app.action.DEVICE_ADMIN_ENABLED</code> is the the primary +<li><code>android.app.action.DEVICE_ADMIN_ENABLED</code> is the primary action that a {@link android.app.admin.DeviceAdminReceiver} subclass must handle to be allowed to manage a device. This is set to the receiver when the user enables the device admin app. Your code typically handles this in diff --git a/docs/html/guide/topics/appwidgets/index.jd b/docs/html/guide/topics/appwidgets/index.jd index 6e6fa28..93d6c6f 100644 --- a/docs/html/guide/topics/appwidgets/index.jd +++ b/docs/html/guide/topics/appwidgets/index.jd @@ -1119,7 +1119,7 @@ initialize an array of <code>WidgetItem</code> objects. When your app widget is active, the system accesses these objects using their index position in the array and the text they contain is displayed </p> -<p>Here is an excerpt from the the <a +<p>Here is an excerpt from the <a href="{@docRoot}resources/samples/StackWidget/index.html">StackView Widget</a> sample's {@link android.widget.RemoteViewsService.RemoteViewsFactory diff --git a/docs/html/guide/topics/connectivity/wifip2p.jd b/docs/html/guide/topics/connectivity/wifip2p.jd index bbf30fd..efb3ac7 100644 --- a/docs/html/guide/topics/connectivity/wifip2p.jd +++ b/docs/html/guide/topics/connectivity/wifip2p.jd @@ -433,7 +433,7 @@ if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { <p>The {@link android.net.wifi.p2p.WifiP2pManager#requestPeers requestPeers()} method is also asynchronous and can notify your activity when a list of peers is available with {@link - android.net.wifi.p2p.WifiP2pManager.PeerListListener#onPeersAvailable onPeersAvailable()}, which is defined in the + android.net.wifi.p2p.WifiP2pManager.PeerListListener#onPeersAvailable onPeersAvailable()}, which is defined in the {@link android.net.wifi.p2p.WifiP2pManager.PeerListListener} interface. The {@link android.net.wifi.p2p.WifiP2pManager.PeerListListener#onPeersAvailable onPeersAvailable()} method provides you with an {@link android.net.wifi.p2p.WifiP2pDeviceList}, which you can iterate diff --git a/docs/html/guide/topics/graphics/opengl.jd b/docs/html/guide/topics/graphics/opengl.jd index 6114a4a..5630e63 100644 --- a/docs/html/guide/topics/graphics/opengl.jd +++ b/docs/html/guide/topics/graphics/opengl.jd @@ -444,7 +444,7 @@ investigate other texture compression formats available on your target devices.< <p>Beyond the ETC1 format, Android devices have varied support for texture compression based on their GPU chipsets and OpenGL implementations. You should investigate texture compression support on -the the devices you are are targeting to determine what compression types your application should +the devices you are are targeting to determine what compression types your application should support. In order to determine what texture formats are supported on a given device, you must <a href="#gl-extension-query">query the device</a> and review the <em>OpenGL extension names</em>, which identify what texture compression formats (and other OpenGL features) are supported by the diff --git a/docs/html/guide/topics/graphics/prop-animation.jd b/docs/html/guide/topics/graphics/prop-animation.jd index b733624..49d7bb8 100644 --- a/docs/html/guide/topics/graphics/prop-animation.jd +++ b/docs/html/guide/topics/graphics/prop-animation.jd @@ -479,7 +479,7 @@ ObjectAnimator.ofFloat(targetObject, "propName", 1f) </li> <li>Depending on what property or object you are animating, you might need to call the {@link - android.view.View#invalidate invalidate()} method on a View force the screen to redraw itself with the + android.view.View#invalidate invalidate()} method on a View to force the screen to redraw itself with the updated animated values. You do this in the {@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate onAnimationUpdate()} callback. For example, animating the color property of a Drawable object only cause updates to the @@ -825,7 +825,7 @@ rotationAnim.setDuration(5000ms); <h2 id="views">Animating Views</h2> - <p>The property animation system allow streamlined animation of View objects and offerse + <p>The property animation system allow streamlined animation of View objects and offers a few advantages over the view animation system. The view animation system transformed View objects by changing the way that they were drawn. This was handled in the container of each View, because the View itself had no properties to manipulate. diff --git a/docs/html/guide/topics/location/index.jd b/docs/html/guide/topics/location/index.jd index 3217196..c4e8829 100644 --- a/docs/html/guide/topics/location/index.jd +++ b/docs/html/guide/topics/location/index.jd @@ -59,7 +59,9 @@ href="{@docRoot}guide/topics/location/strategies.html">Location Strategies</a> g <h2 id="maps">Google Maps Android API</h2> -<p>With the Google Maps Android API, you can add maps to your app that are based on Google +<p>With the +<a href="http://developers.google.com/maps/documentation/android/">Google Maps Android API</a>, +you can add maps to your app that are based on Google Maps data. The API automatically handles access to Google Maps servers, data downloading, map display, and touch gestures on the map. You can also use API calls to add markers, polygons and overlays, and to change the user's view of a particular map area.</p> @@ -85,6 +87,6 @@ with the Google Play Store running Android 2.2 or higher, through <p>To integrate Google Maps into your app, you need to install the Google Play services libraries for your Android SDK. For more details, read about <a -href="{@docRoot}google/play-services/index.html">Google Play services</a>.</p> +href="{@docRoot}google/play-services/maps.html">Google Play services</a>.</p> diff --git a/docs/html/guide/topics/manifest/activity-alias-element.jd b/docs/html/guide/topics/manifest/activity-alias-element.jd index ba2c154..d3df08b 100644 --- a/docs/html/guide/topics/manifest/activity-alias-element.jd +++ b/docs/html/guide/topics/manifest/activity-alias-element.jd @@ -89,7 +89,7 @@ See the <code><a href="{@docRoot}guide/topics/manifest/activity-element.html">&l <dt><a name="label"></a>{@code android:label}</dt> <dd>A user-readable label for the alias when presented to users through the alias. -See the the <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> element's +See the <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> element's <code><a href="{@docRoot}guide/topics/manifest/activity-element.html#label">label</a></code> attribute for more information. </p></dd> @@ -132,4 +132,4 @@ the alias in the manifest. <dt>see also:</dt> <dd><code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code></dd> -</dl>
\ No newline at end of file +</dl> diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd index 2aedaec..c9f505f 100644 --- a/docs/html/guide/topics/manifest/activity-element.jd +++ b/docs/html/guide/topics/manifest/activity-element.jd @@ -217,7 +217,7 @@ separated by '{@code |}' — for example, "{@code locale|navigation|orientat </tr><tr> <td>"{@code uiMode}"</td> <td>The user interface mode has changed — this can be caused when the user places the -device into a desk/car dock or when the the night mode changes. See {@link +device into a desk/car dock or when the night mode changes. See {@link android.app.UiModeManager}. <em>Added in API level 8</em>.</td> </tr><tr> diff --git a/docs/html/guide/topics/providers/calendar-provider.jd b/docs/html/guide/topics/providers/calendar-provider.jd index 5adc68c..3cd4511 100644 --- a/docs/html/guide/topics/providers/calendar-provider.jd +++ b/docs/html/guide/topics/providers/calendar-provider.jd @@ -816,7 +816,7 @@ zone. <tr> <td>{@link android.provider.CalendarContract.Instances#END_MINUTE}</td> - <td>The end minute of the instance measured from midnight in the the + <td>The end minute of the instance measured from midnight in the Calendar's time zone.</td> </tr> diff --git a/docs/html/guide/topics/resources/accessing-resources.jd b/docs/html/guide/topics/resources/accessing-resources.jd index 0673b6f..8f99653 100644 --- a/docs/html/guide/topics/resources/accessing-resources.jd +++ b/docs/html/guide/topics/resources/accessing-resources.jd @@ -50,7 +50,7 @@ the {@code aapt} tool automatically generates.</p> <p>When your application is compiled, {@code aapt} generates the {@code R} class, which contains resource IDs for all the resources in your {@code res/} directory. For each type of resource, there is an {@code R} subclass (for example, -{@code R.drawable} for all drawable resources) and for each resource of that type, there is a static +{@code R.drawable} for all drawable resources), and for each resource of that type, there is a static integer (for example, {@code R.drawable.icon}). This integer is the resource ID that you can use to retrieve your resource.</p> @@ -68,7 +68,7 @@ resource is a simple value (such as a string).</li> <p>There are two ways you can access a resource:</p> <ul> - <li><strong>In code:</strong> Using an static integer from a sub-class of your {@code R} + <li><strong>In code:</strong> Using a static integer from a sub-class of your {@code R} class, such as: <pre class="classic no-pretty-print">R.string.hello</pre> <p>{@code string} is the resource type and {@code hello} is the resource name. There are many @@ -264,11 +264,13 @@ reference a system resource, you would need to include the package name. For exa android:text="@string/hello" /> </pre> -<p class="note"><strong>Note:</strong> You should use string resources at all times, so that your -application can be localized for other languages. For information about creating alternative +<p class="note"><strong>Note:</strong> You should use string resources at +all times, so that your application can be localized for other languages. +For information about creating alternative resources (such as localized strings), see <a href="providing-resources.html#AlternativeResources">Providing Alternative -Resources</a>.</p> +Resources</a>. For a complete guide to localizing your application for other languages, +see <a href="localization.html">Localization</a>.</p> <p>You can even use resources in XML to create aliases. For example, you can create a drawable resource that is an alias for another drawable resource:</p> diff --git a/docs/html/guide/topics/resources/animation-resource.jd b/docs/html/guide/topics/resources/animation-resource.jd index ef64f07..e5cac88 100644 --- a/docs/html/guide/topics/resources/animation-resource.jd +++ b/docs/html/guide/topics/resources/animation-resource.jd @@ -593,7 +593,7 @@ attribute, the value of which is a reference to an interpolator resource.</p> <p>All interpolators available in Android are subclasses of the {@link android.view.animation.Interpolator} class. For each interpolator class, Android includes a public resource you can reference in order to apply the interpolator to an animation -using the the {@code android:interpolator} attribute. +using the {@code android:interpolator} attribute. The following table specifies the resource to use for each interpolator:</p> <table> diff --git a/docs/html/guide/topics/resources/layout-resource.jd b/docs/html/guide/topics/resources/layout-resource.jd index 380ab15..366ddc8 100644 --- a/docs/html/guide/topics/resources/layout-resource.jd +++ b/docs/html/guide/topics/resources/layout-resource.jd @@ -135,7 +135,7 @@ a reference of all available attributes, </dd> <dt id="requestfocus-element"><code><requestFocus></code></dt> <dd>Any element representing a {@link android.view.View} object can include this empty element, - which gives it's parent initial focus on the screen. You can have only one of these + which gives its parent initial focus on the screen. You can have only one of these elements per file.</dd> <dt id="include-element"><code><include></code></dt> diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd index b311b7f..5097cc4 100644 --- a/docs/html/guide/topics/resources/providing-resources.jd +++ b/docs/html/guide/topics/resources/providing-resources.jd @@ -376,7 +376,7 @@ res/ screen area. Specifically, the device's smallestWidth is the shortest of the screen's available height and width (you may also think of it as the "smallest possible width" for the screen). You can use this qualifier to ensure that, regardless of the screen's current orientation, your -application's has at least {@code <N>} dps of width available for it UI.</p> +application has at least {@code <N>} dps of width available for its UI.</p> <p>For example, if your layout requires that its smallest dimension of screen area be at least 600 dp at all times, then you can use this qualifer to create the layout resources, {@code res/layout-sw600dp/}. The system will use these resources only when the smallest dimension of diff --git a/docs/html/guide/topics/search/search-dialog.jd b/docs/html/guide/topics/search/search-dialog.jd index b9a26d6..e24681a 100644 --- a/docs/html/guide/topics/search/search-dialog.jd +++ b/docs/html/guide/topics/search/search-dialog.jd @@ -722,6 +722,7 @@ public boolean onCreateOptionsMenu(Menu menu) { // Get the SearchView and set the searchable configuration SearchManager searchManager = (SearchManager) {@link android.app.Activity#getSystemService getSystemService}(Context.SEARCH_SERVICE); SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); + // Assumes current activity is the searchable activity searchView.setSearchableInfo(searchManager.getSearchableInfo({@link android.app.Activity#getComponentName()})); searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd index 678a512..db09e7d 100644 --- a/docs/html/guide/topics/ui/actionbar.jd +++ b/docs/html/guide/topics/ui/actionbar.jd @@ -674,7 +674,7 @@ action view still appears in the action bar when the user selects the item. You view collapsible by adding {@code "collapseActionView"} to the {@code android:showAsAction} attribute, as shown in the XML above.</p> -<p>Because the system will expand the action view when the user selects the item, so you +<p>Because the system will expand the action view when the user selects the item, you <em>do not</em> need to respond to the item in the {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected} callback. The system still calls {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} when the user selects it, diff --git a/docs/html/guide/topics/ui/drag-drop.jd b/docs/html/guide/topics/ui/drag-drop.jd index cacdf5c..884a1b2 100644 --- a/docs/html/guide/topics/ui/drag-drop.jd +++ b/docs/html/guide/topics/ui/drag-drop.jd @@ -750,7 +750,7 @@ imageView.setOnLongClickListener(new View.OnLongClickListener() { A{@link android.view.DragEvent#ACTION_DRAG_EXITED} event, it receives a new {@link android.view.DragEvent#ACTION_DRAG_LOCATION} event every time the touch point moves. The {@link android.view.DragEvent#getX()} and {@link android.view.DragEvent#getY()} methods - return the the X and Y coordinates of the touch point. + return the X and Y coordinates of the touch point. </li> <li> {@link android.view.DragEvent#ACTION_DRAG_EXITED}: This event is sent to a listener that @@ -995,4 +995,4 @@ protected class myDragEventListener implements View.OnDragEventListener { }; }; }; -</pre>
\ No newline at end of file +</pre> diff --git a/docs/html/guide/topics/ui/menus.jd b/docs/html/guide/topics/ui/menus.jd index 01d373e..dfcea52 100644 --- a/docs/html/guide/topics/ui/menus.jd +++ b/docs/html/guide/topics/ui/menus.jd @@ -834,7 +834,7 @@ android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}</l </ul> <p>You can create a group by nesting {@code <item>} elements inside a {@code <group>} -element in your menu resource or by specifying a group ID with the the {@link +element in your menu resource or by specifying a group ID with the {@link android.view.Menu#add(int,int,int,int) add()} method.</p> <p>Here's an example menu resource that includes a group:</p> diff --git a/docs/html/guide/topics/ui/notifiers/toasts.jd b/docs/html/guide/topics/ui/notifiers/toasts.jd index 92c146a..e5d4a0a 100644 --- a/docs/html/guide/topics/ui/notifiers/toasts.jd +++ b/docs/html/guide/topics/ui/notifiers/toasts.jd @@ -105,7 +105,7 @@ with the following XML (saved as <em>toast_layout.xml</em>):</p> </LinearLayout> </pre> -<p>Notice that the ID of the LinearLayout element is "toast_layout". You must use this +<p>Notice that the ID of the LinearLayout element is "toast_layout_root". You must use this ID to inflate the layout from the XML, as shown here:</p> <pre> diff --git a/docs/html/images/home/io-extended-2013.png b/docs/html/images/home/io-extended-2013.png Binary files differnew file mode 100644 index 0000000..93989d4 --- /dev/null +++ b/docs/html/images/home/io-extended-2013.png diff --git a/docs/html/images/ui/notifications/custom_message.png b/docs/html/images/ui/notifications/custom_message.png Binary files differindex 00b7632..00b7632 100755..100644 --- a/docs/html/images/ui/notifications/custom_message.png +++ b/docs/html/images/ui/notifications/custom_message.png diff --git a/docs/html/images/ui/notifications/notifications_window.png b/docs/html/images/ui/notifications/notifications_window.png Binary files differindex 0354ee9..0354ee9 100755..100644 --- a/docs/html/images/ui/notifications/notifications_window.png +++ b/docs/html/images/ui/notifications/notifications_window.png diff --git a/docs/html/images/ui/notifications/status_bar.png b/docs/html/images/ui/notifications/status_bar.png Binary files differindex f0240a5..f0240a5 100755..100644 --- a/docs/html/images/ui/notifications/status_bar.png +++ b/docs/html/images/ui/notifications/status_bar.png diff --git a/docs/html/index.jd b/docs/html/index.jd index ec0469c..29d6a8f 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -14,16 +14,16 @@ page.metaDescription=The official site for Android developers. Provides the Andr <ul> <li class="item carousel-home"> <div class="content-left col-10"> - <img src="{@docRoot}images/home/io-logo-2013.png" style="margin:40px 0 0"> + <img src="{@docRoot}images/home/io-extended-2013.png" style="margin:90px 0 0"> </div> <div class="content-right col-5"> - <h1>Google I/O 2013</h1> - <p>Android will be at Google I/O on May 15-17, 2013, with sessions covering a variety of topics - such as design, performance, and how to extend your app with the latest Android features.</p> - <p>For more information about event details and planned sessions, - stay tuned to <a - href="http://google.com/+GoogleDevelopers">+Google Developers</a>.</p> - <p><a href="https://developers.google.com/events/io/" class="button">Learn more</a></p> + <h1>Google I/O Extended</h1> + <p>Android will be at Google I/O on May 15-17, 2013, with sessions covering topics + such as design, performance, and how to enhance your app with the latest Android features.</p> + <p>Even if you can't make it there, you can experience the excitement and innovation of + Google I/O remotely with Google I/O Extended.</p> + <p><a href="https://developers.google.com/events/io/io-extended/?utm_source=site&utm_medium=emb&utm_campaign=extended-android-site" + >Organize or attend an event near you »</a></p> </div> </li> <li class="item carousel-home"> diff --git a/docs/html/tools/debugging/ddms.jd b/docs/html/tools/debugging/ddms.jd index 3d6324b..f641aad 100644 --- a/docs/html/tools/debugging/ddms.jd +++ b/docs/html/tools/debugging/ddms.jd @@ -54,7 +54,7 @@ parent.link=index.html <p>When DDMS starts, it connects to <a href="{@docRoot}tools/help/adb.html">adb</a>. When a device is connected, a VM monitoring service is created between <code>adb</code> and DDMS, which notifies DDMS when a VM on the device is started or terminated. Once a VM - is running, DDMS retrieves the the VM's process ID (pid), via <code>adb</code>, and opens a connection to the + is running, DDMS retrieves the VM's process ID (pid), via <code>adb</code>, and opens a connection to the VM's debugger, through the adb daemon (adbd) on the device. DDMS can now talk to the VM using a custom wire protocol.</p> diff --git a/docs/html/tools/device.jd b/docs/html/tools/device.jd index 9bdaf47..c7827b2 100644 --- a/docs/html/tools/device.jd +++ b/docs/html/tools/device.jd @@ -30,7 +30,7 @@ device directly from Eclipse or from the command line with ADB. If you don't yet have a device, check with the service providers in your area to determine which Android-powered devices are available.</p> -<p>If you want a SIM-unlocked phone, then you might consider the Google Nexus S. To find a place +<p>If you want a SIM-unlocked phone, then you might consider a Nexus phone. To find a place to purchase the Nexus S and other Android-powered devices, visit <a href="http://www.google.com/phone/detail/nexus-s">google.com/phone</a>.</p> diff --git a/docs/html/tools/devices/emulator.jd b/docs/html/tools/devices/emulator.jd index cee6473..fda233d 100644 --- a/docs/html/tools/devices/emulator.jd +++ b/docs/html/tools/devices/emulator.jd @@ -898,7 +898,7 @@ to a specified guest port on the router, while the router directs traffic to/from that port to the emulated device's host port. </p> <p>To set up the network redirection, you create a mapping of host and guest -ports/addresses on the the emulator instance. There are two ways to set up +ports/addresses on the emulator instance. There are two ways to set up network redirection: using emulator console commands and using the ADB tool, as described below. </p> @@ -1254,7 +1254,7 @@ syntax for this command is as follows: </p> <td> </td> </tr> <tr> - <td><code>power health <percent></code></td> + <td><code>capacity <percent></code></td> <td>Set remaining battery capacity state (0-100).</td> <td> </td> </tr> diff --git a/docs/html/tools/projects/index.jd b/docs/html/tools/projects/index.jd index 6a49ac9..439d3be 100644 --- a/docs/html/tools/projects/index.jd +++ b/docs/html/tools/projects/index.jd @@ -68,12 +68,12 @@ page.title=Managing Projects <code>src<em>/your/package/namespace/ActivityName</em>.java</code>. All other source code files (such as <code>.java</code> or <code>.aidl</code> files) go here as well.</dd> - <dt><code>bin</code></dt> + <dt><code>bin/</code></dt> <dd>Output directory of the build. This is where you can find the final <code>.apk</code> file and other compiled resources.</dd> - <dt><code>jni</code></dt> + <dt><code>jni/</code></dt> <dd>Contains native code sources developed using the Android NDK. For more information, see the <a href="{@docRoot}tools/sdk/ndk/index.html">Android NDK documentation</a>.</dd> @@ -88,7 +88,7 @@ page.title=Managing Projects <dd>This is empty. You can use it to store raw asset files. Files that you save here are compiled into an <code>.apk</code> file as-is, and the original filename is preserved. You can navigate this directory in the same way as a typical file system using URIs and read files as a stream of - bytes using the the {@link android.content.res.AssetManager}. For example, this is a good + bytes using the {@link android.content.res.AssetManager}. For example, this is a good location for textures and game data.</dd> <dt><code>res/</code></dt> @@ -114,7 +114,7 @@ page.title=Managing Projects <dt><code>drawable/</code></dt> <dd>For bitmap files (PNG, JPEG, or GIF), 9-Patch image files, and XML files that describe - Drawable shapes or a Drawable objects that contain multiple states (normal, pressed, or + Drawable shapes or Drawable objects that contain multiple states (normal, pressed, or focused). See the <a href= "{@docRoot}guide/topics/resources/drawable-resource.html">Drawable</a> resource type.</dd> @@ -251,7 +251,7 @@ used.</dd> code and resources as a standard Android project, stored in the same way. For example, source code in the library project can access its own resources through its <code>R</code> class.</p> - <p>However, a library project differs from an standard Android application project in that you + <p>However, a library project differs from a standard Android application project in that you cannot compile it directly to its own <code>.apk</code> and run it on an Android device. Similarly, you cannot export the library project to a self-contained JAR file, as you would do for a true library. Instead, you must compile the library indirectly, by referencing the diff --git a/docs/html/tools/testing/activity_test.jd b/docs/html/tools/testing/activity_test.jd index 8288249..096aea5 100644 --- a/docs/html/tools/testing/activity_test.jd +++ b/docs/html/tools/testing/activity_test.jd @@ -537,7 +537,7 @@ import android.widget.Spinner; import android.widget.SpinnerAdapter; </pre> <p> - You now have the the complete <code>setUp()</code> method. + You now have the complete <code>setUp()</code> method. </p> <h3 id="AddPreConditionsTest">Adding an initial conditions test</h3> <p> @@ -1266,7 +1266,7 @@ $ adb install Spinner/bin/SpinnerActivity-debug.apk </li> <li> Follow the tutorial, starting with the section <a href="#CreateTestCaseClass">Creating the Test Case Class</a>. When you are prompted to - run the sample application, go the the Launcher screen in your device or emulator and select SpinnerActivity. + run the sample application, go to the Launcher screen in your device or emulator and select SpinnerActivity. When you are prompted to run the test application, return here to continue with the following instructions. </li> <li> diff --git a/docs/html/tools/testing/activity_testing.jd b/docs/html/tools/testing/activity_testing.jd index 7190b98..88ac9b2 100644 --- a/docs/html/tools/testing/activity_testing.jd +++ b/docs/html/tools/testing/activity_testing.jd @@ -77,7 +77,7 @@ parent.link=index.html </div> </div> <p> - Activity testing is particularly dependent on the the Android instrumentation framework. + Activity testing is particularly dependent on the Android instrumentation framework. Unlike other components, activities have a complex lifecycle based on callback methods; these can't be invoked directly except by instrumentation. Also, the only way to send events to the user interface from a program is through instrumentation. @@ -322,7 +322,7 @@ parent.link=index.html the published application. </p> <p> - To add the the permission, add the element + To add the permission, add the element <code><uses-permission android:name="android.permission.DISABLE_KEYGUARD"/></code> as a child of the <code><manifest></code> element. To disable the KeyGuard, add the following code to the <code>onCreate()</code> method of activities you intend to test: diff --git a/docs/html/tools/testing/testing_android.jd b/docs/html/tools/testing/testing_android.jd index acf5ec2..10843e8 100644 --- a/docs/html/tools/testing/testing_android.jd +++ b/docs/html/tools/testing/testing_android.jd @@ -111,14 +111,14 @@ parent.link=index.html </li> <li> The SDK tools for building and tests are available in Eclipse with ADT, and also in - command-line form for use with other IDES. These tools get information from the project of + command-line form for use with other IDEs. These tools get information from the project of the application under test and use this information to automatically create the build files, manifest file, and directory structure for the test package. </li> <li> The SDK also provides <a href="{@docRoot}tools/help/monkeyrunner_concepts.html">monkeyrunner</a>, an API - testing devices with Python programs, and <a + for testing devices with Python programs, and <a href="{@docRoot}tools/help/monkey.html">UI/Application Exerciser Monkey</a>, a command-line tool for stress-testing UIs by sending pseudo-random events to a device. </li> diff --git a/docs/html/tools/testing/testing_otheride.jd b/docs/html/tools/testing/testing_otheride.jd index 0678f52..9484158 100644 --- a/docs/html/tools/testing/testing_otheride.jd +++ b/docs/html/tools/testing/testing_otheride.jd @@ -75,9 +75,9 @@ parent.link=index.html <p> You use the <code>android</code> tool to create test projects. You also use <code>android</code> to convert existing test code into an Android test project, - or to add the <code>run-tests</code> Ant target to an existing Android test project. + or to add the <code>test</code> Ant target to an existing Android test project. These operations are described in more detail in the section <a href="#UpdateTestProject"> - Updating a test project</a>. The <code>run-tests</code> target is described in + Updating a test project</a>. The <code>test</code> target is described in <a href="#RunTestsAnt">Quick build and run with Ant</a>. </p> <h3 id="CreateTestProject">Creating a test project</h3> @@ -300,7 +300,7 @@ $ android create test-project -m ../HelloAndroid -n HelloAndroidTest -p HelloAnd <h3 id="RunTestsAnt">Quick build and run with Ant</h3> <p> You can use Ant to run all the tests in your test project, using the target - <code>run-tests</code>, which is created automatically when you create a test project with + <code>test</code>, which is created automatically when you create a test project with the <code>android</code> tool. </p> <p> diff --git a/docs/html/tools/workflow/index.jd b/docs/html/tools/workflow/index.jd index 5ae06e6..784b212 100644 --- a/docs/html/tools/workflow/index.jd +++ b/docs/html/tools/workflow/index.jd @@ -34,7 +34,7 @@ development steps encompass four development phases, which include:</p> </li> <li><strong>Development</strong> <p>During this phase you set up and develop your Android project, which contains all of the - source code and resource files for your application. For more informations, see + source code and resource files for your application. For more information, see <a href="{@docRoot}tools/projects/index.html">Create an Android project</a>.</p> </li> <li><strong>Debugging and Testing</strong> diff --git a/docs/html/training/accessibility/service.jd b/docs/html/training/accessibility/service.jd index 373ddbb..953c558 100644 --- a/docs/html/training/accessibility/service.jd +++ b/docs/html/training/accessibility/service.jd @@ -204,7 +204,7 @@ public void onAccessibilityEvent(AccessibilityEvent event) { <p>This step is optional, but highly useful. One of the new features in Android 4.0 (API Level 14) is the ability for an {@link android.accessibilityservice.AccessibilityService} to query the view -hierarchy, collecting information about the the UI component that generated an event, and +hierarchy, collecting information about the UI component that generated an event, and its parent and children. In order to do this, make sure that you set the following line in your XML configuration:</p> <pre> diff --git a/docs/html/training/animation/anim_card_flip.mp4 b/docs/html/training/animation/anim_card_flip.mp4 Binary files differindex e885f98..e885f98 100755..100644 --- a/docs/html/training/animation/anim_card_flip.mp4 +++ b/docs/html/training/animation/anim_card_flip.mp4 diff --git a/docs/html/training/animation/anim_card_flip.ogv b/docs/html/training/animation/anim_card_flip.ogv Binary files differindex 33cd86c..33cd86c 100755..100644 --- a/docs/html/training/animation/anim_card_flip.ogv +++ b/docs/html/training/animation/anim_card_flip.ogv diff --git a/docs/html/training/animation/anim_card_flip.webm b/docs/html/training/animation/anim_card_flip.webm Binary files differindex a670d78..a670d78 100755..100644 --- a/docs/html/training/animation/anim_card_flip.webm +++ b/docs/html/training/animation/anim_card_flip.webm diff --git a/docs/html/training/animation/anim_screenslide.mp4 b/docs/html/training/animation/anim_screenslide.mp4 Binary files differindex 3e65026..3e65026 100755..100644 --- a/docs/html/training/animation/anim_screenslide.mp4 +++ b/docs/html/training/animation/anim_screenslide.mp4 diff --git a/docs/html/training/animation/anim_screenslide.ogv b/docs/html/training/animation/anim_screenslide.ogv Binary files differindex c45ebd4..c45ebd4 100755..100644 --- a/docs/html/training/animation/anim_screenslide.ogv +++ b/docs/html/training/animation/anim_screenslide.ogv diff --git a/docs/html/training/animation/anim_screenslide.webm b/docs/html/training/animation/anim_screenslide.webm Binary files differindex c72adbc..c72adbc 100755..100644 --- a/docs/html/training/animation/anim_screenslide.webm +++ b/docs/html/training/animation/anim_screenslide.webm diff --git a/docs/html/training/animation/cardflip.jd b/docs/html/training/animation/cardflip.jd index 1477f9f..48fbbd8 100644 --- a/docs/html/training/animation/cardflip.jd +++ b/docs/html/training/animation/cardflip.jd @@ -70,7 +70,7 @@ trainingnavtop=true <code>animator/card_flip_right_out.xml</code> </li> <li> - <code>animator/card_flip_right_in.xml</code> + <code>animator/card_flip_left_in.xml</code> </li> <li> <code>animator/card_flip_left_out.xml</code> @@ -372,4 +372,4 @@ private void flipCard() { // Commit the transaction. .commit(); } -</pre>
\ No newline at end of file +</pre> diff --git a/docs/html/training/basics/data-storage/databases.jd b/docs/html/training/basics/data-storage/databases.jd index 9976bb1..61fb758 100644 --- a/docs/html/training/basics/data-storage/databases.jd +++ b/docs/html/training/basics/data-storage/databases.jd @@ -284,7 +284,7 @@ immune to SQL injection.</p> // Define 'where' part of query. String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; // Specify arguments in placeholder order. -String[] selelectionArgs = { String.valueOf(rowId) }; +String[] selectionArgs = { String.valueOf(rowId) }; // Issue SQL statement. db.delete(table_name, selection, selectionArgs); </pre> @@ -309,7 +309,7 @@ values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title); // Which row to update, based on the ID String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; -String[] selelectionArgs = { String.valueOf(rowId) }; +String[] selectionArgs = { String.valueOf(rowId) }; int count = db.update( FeedReaderDbHelper.FeedEntry.TABLE_NAME, diff --git a/docs/html/training/basics/fragments/fragment-ui.jd b/docs/html/training/basics/fragments/fragment-ui.jd index d648938..db3119b 100644 --- a/docs/html/training/basics/fragments/fragment-ui.jd +++ b/docs/html/training/basics/fragments/fragment-ui.jd @@ -41,7 +41,7 @@ tablet which has a wider screen size to display more information to the user.</p <img src="{@docRoot}images/training/basics/fragments-screen-mock.png" alt="" /> <p class="img-caption"><strong>Figure 1.</strong> Two fragments, displayed in different -configurations for the same activity on different screen sizes. On a large screen, both fragment +configurations for the same activity on different screen sizes. On a large screen, both fragments fit side by side, but on a handset device, only one fragment fits at a time so the fragments must replace each other as the user navigates.</p> diff --git a/docs/html/training/basics/intents/result.jd b/docs/html/training/basics/intents/result.jd index 0086913..24ecc46 100644 --- a/docs/html/training/basics/intents/result.jd +++ b/docs/html/training/basics/intents/result.jd @@ -62,7 +62,7 @@ app can properly identify the result and determine how to handle it.</p> static final int PICK_CONTACT_REQUEST = 1; // The request code ... private void pickContact() { - Intent pickContactIntent = new Intent(Intent.ACTION_PICK, new Uri("content://contacts")); + Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts")); pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST); } diff --git a/docs/html/training/basics/network-ops/managing.jd b/docs/html/training/basics/network-ops/managing.jd index 0f3d495..990b8cb 100644 --- a/docs/html/training/basics/network-ops/managing.jd +++ b/docs/html/training/basics/network-ops/managing.jd @@ -269,7 +269,7 @@ returns to the main activity:</p> // When the user changes the preferences selection, // onSharedPreferenceChanged() restarts the main activity as a new - // task. Sets the the refreshDisplay flag to "true" to indicate that + // task. Sets the refreshDisplay flag to "true" to indicate that // the main activity should update its display. // The main activity queries the PreferenceManager to get the latest settings. diff --git a/docs/html/training/gestures/scroll.jd b/docs/html/training/gestures/scroll.jd index bd1537a..3e3aa14 100644 --- a/docs/html/training/gestures/scroll.jd +++ b/docs/html/training/gestures/scroll.jd @@ -56,7 +56,7 @@ scrolling animation in response to a touch event. They are similar, but {@link android.widget.OverScroller} includes methods for indicating to users that they've reached the content edges after a pan or fling gesture. The {@code InteractiveChart} sample -uses the the {@link android.widget.EdgeEffect} class +uses the {@link android.widget.EdgeEffect} class (actually the {@link android.support.v4.widget.EdgeEffectCompat} class) to display a "glow" effect when users reach the content edges.</p> diff --git a/docs/html/training/in-app-billing/list-iab-products.jd b/docs/html/training/in-app-billing/list-iab-products.jd index 36ff34a..c423fc1 100644 --- a/docs/html/training/in-app-billing/list-iab-products.jd +++ b/docs/html/training/in-app-billing/list-iab-products.jd @@ -54,7 +54,7 @@ next.link=purchase-iab-products.html <li>The {@code List} argument consists of one or more product IDs (also called SKUs) for the products that you want to query.</li> <li>Finally, the {@code QueryInventoryFinishedListener} argument specifies a listener is notified when the query operation has completed and handles the query response.</li> </ul> -If you use the the convenience classes provided in the sample, the classes will handle background thread management for In-app Billing requests, so you can safely make queries from the main thread of your application. +If you use the convenience classes provided in the sample, the classes will handle background thread management for In-app Billing requests, so you can safely make queries from the main thread of your application. </p> <p>The following code shows how you can retrieve the details for two products with IDs {@code SKU_APPLE} and {@code SKU_BANANA} that you previously defined in the Developer Console.</p> diff --git a/docs/html/training/in-app-billing/purchase-iab-products.jd b/docs/html/training/in-app-billing/purchase-iab-products.jd index 7fa77d3..4e6e035 100644 --- a/docs/html/training/in-app-billing/purchase-iab-products.jd +++ b/docs/html/training/in-app-billing/purchase-iab-products.jd @@ -104,7 +104,7 @@ IabHelper.QueryInventoryFinishedListener mGotInventoryListener </pre> <h2 id="Consume">Consume a Purchase</h2> -<p>You can use the In-app Billing Version 3 API to track the ownership of purchased items in Google Play. Once an item is purchased, it is considered to be "owned" and cannot be purchased again from Google Play while in that state. You must send a consumption request for the item before Google Play makes it available for purchase again. All managed in-app products are consumable. How you use the consumption mechanism in your app is up to you. Typically, you would implement consumption for products with temporary benefits that users may want to purchase multiple times (for example, in-game currency or replensihable game tokens). You would typically not want to implement consumption for products that are purchased once and provide a permanent effect (for example, a premium upgrade).</p> +<p>You can use the In-app Billing Version 3 API to track the ownership of purchased items in Google Play. Once an item is purchased, it is considered to be "owned" and cannot be purchased again from Google Play while in that state. You must send a consumption request for the item before Google Play makes it available for purchase again. All managed in-app products are consumable. How you use the consumption mechanism in your app is up to you. Typically, you would implement consumption for products with temporary benefits that users may want to purchase multiple times (for example, in-game currency or replenishable game tokens). You would typically not want to implement consumption for products that are purchased once and provide a permanent effect (for example, a premium upgrade).</p> <p>It's your responsibility to control and track how the in-app product is provisioned to the user. For example, if the user purchased in-game currency, you should update the player's inventory with the amount of currency purchased.</p> <p class="note"><strong>Security Recommendation:</strong> You must send a consumption request before provisioning the benefit of the consumable in-app purchase to the user. Make sure that you have received a successful consumption response from Google Play before you provision the item.</p> <p>To record a purchase consumption, call {@code consumeAsync(Purchase, OnConsumeFinishedListener)} on your {@code IabHelper} instance. The first argument that the method takes is the {@code Purchase} object representing the item to consume. The second argument is a {@code OnConsumeFinishedListener} that is notified when the consumption operation has completed and handles the consumption response from Google Play. It is safe to make this call fom your main thread.</p> diff --git a/docs/html/training/monitoring-device-state/battery-monitoring.jd b/docs/html/training/monitoring-device-state/battery-monitoring.jd index c963a18..a202566 100644 --- a/docs/html/training/monitoring-device-state/battery-monitoring.jd +++ b/docs/html/training/monitoring-device-state/battery-monitoring.jd @@ -65,9 +65,9 @@ boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL; // How are we charging? -int chargePlug = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); -boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB; -boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;</pre> +int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); +boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB; +boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;</pre> <p>Typically you should maximize the rate of your background updates in the case where the device is connected to an AC charger, reduce the rate if the charge is over USB, and lower it @@ -105,8 +105,8 @@ the current charging state and method as described in the previous step.</p> status == BatteryManager.BATTERY_STATUS_FULL; int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); - boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB; - boolean acCharge = chargePlug == BATTERY_PLUGGED_AC; + boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB; + boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC; } }</pre> @@ -119,8 +119,8 @@ the rate of your background updates if the battery charge is below a certain lev <p>You can find the current battery charge by extracting the current battery level and scale from the battery status intent as shown here:</p> -<pre>int level = battery.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); -int scale = battery.getIntExtra(BatteryManager.EXTRA_SCALE, -1); +<pre>int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); +int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1); float batteryPct = level / (float)scale;</pre> diff --git a/docs/html/training/monitoring-device-state/docking-monitoring.jd b/docs/html/training/monitoring-device-state/docking-monitoring.jd index 3787a55..5c8bfd6 100644 --- a/docs/html/training/monitoring-device-state/docking-monitoring.jd +++ b/docs/html/training/monitoring-device-state/docking-monitoring.jd @@ -79,7 +79,7 @@ boolean isDesk = dockState == EXTRA_DOCK_STATE_DESK || <h2 id="MonitorDockState">Monitor for Changes in the Dock State or Type</h2> -<p>Whenever the the device is docked or undocked, the {@link +<p>Whenever the device is docked or undocked, the {@link android.content.Intent#ACTION_DOCK_EVENT} action is broadcast. To monitor changes in the device's dock-state, simply register a broadcast receiver in your application manifest as shown in the snippet below:</p> diff --git a/docs/html/training/multiple-threads/define-runnable.jd b/docs/html/training/multiple-threads/define-runnable.jd index 17640a9..40853d3 100644 --- a/docs/html/training/multiple-threads/define-runnable.jd +++ b/docs/html/training/multiple-threads/define-runnable.jd @@ -98,7 +98,7 @@ class PhotoDecodeRunnable implements Runnable { android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); ... /* - * Stores the current Thread in the the PhotoTask instance, + * Stores the current Thread in the PhotoTask instance, * so that the instance * can interrupt the Thread. */ diff --git a/graphics/java/android/graphics/DashPathEffect.java b/graphics/java/android/graphics/DashPathEffect.java index 4f16dc4..2bdecce 100644 --- a/graphics/java/android/graphics/DashPathEffect.java +++ b/graphics/java/android/graphics/DashPathEffect.java @@ -26,7 +26,7 @@ public class DashPathEffect extends PathEffect { * controls the length of the dashes. The paint's strokeWidth controls the * thickness of the dashes. * Note: this patheffect only affects drawing with the paint's style is set - * to STROKE or STROKE_AND_FILL. It is ignored if the drawing is done with + * to STROKE or FILL_AND_STROKE. It is ignored if the drawing is done with * style == FILL. * @param intervals array of ON and OFF distances * @param phase offset into the intervals array diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index d5cee3a..1485d8d 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1295,17 +1295,17 @@ public class Paint { return 0f; } if (!mHasCompatScaling) { - return (float) Math.ceil(native_measureText(text, index, count)); + return (float) Math.ceil(native_measureText(text, index, count, mBidiFlags)); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - float w = native_measureText(text, index, count); + float w = native_measureText(text, index, count, mBidiFlags); setTextSize(oldSize); return (float) Math.ceil(w*mInvCompatScaling); } - private native float native_measureText(char[] text, int index, int count); + private native float native_measureText(char[] text, int index, int count, int bidiFlags); /** * Return the width of the text. @@ -1327,17 +1327,17 @@ public class Paint { return 0f; } if (!mHasCompatScaling) { - return (float) Math.ceil(native_measureText(text, start, end)); + return (float) Math.ceil(native_measureText(text, start, end, mBidiFlags)); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - float w = native_measureText(text, start, end); + float w = native_measureText(text, start, end, mBidiFlags); setTextSize(oldSize); return (float) Math.ceil(w*mInvCompatScaling); } - private native float native_measureText(String text, int start, int end); + private native float native_measureText(String text, int start, int end, int bidiFlags); /** * Return the width of the text. @@ -1355,16 +1355,16 @@ public class Paint { } if (!mHasCompatScaling) { - return (float) Math.ceil(native_measureText(text)); + return (float) Math.ceil(native_measureText(text, mBidiFlags)); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - float w = native_measureText(text); + float w = native_measureText(text, mBidiFlags); setTextSize(oldSize); return (float) Math.ceil(w*mInvCompatScaling); } - private native float native_measureText(String text); + private native float native_measureText(String text, int bidiFlags); /** * Return the width of the text. @@ -1431,12 +1431,12 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - return native_breakText(text, index, count, maxWidth, measuredWidth); + return native_breakText(text, index, count, maxWidth, mBidiFlags, measuredWidth); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - int res = native_breakText(text, index, count, maxWidth*mCompatScaling, + int res = native_breakText(text, index, count, maxWidth*mCompatScaling, mBidiFlags, measuredWidth); setTextSize(oldSize); if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling; @@ -1444,7 +1444,7 @@ public class Paint { } private native int native_breakText(char[] text, int index, int count, - float maxWidth, float[] measuredWidth); + float maxWidth, int bidiFlags, float[] measuredWidth); /** * Measure the text, stopping early if the measured width exceeds maxWidth. @@ -1521,12 +1521,12 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - return native_breakText(text, measureForwards, maxWidth, measuredWidth); + return native_breakText(text, measureForwards, maxWidth, mBidiFlags, measuredWidth); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - int res = native_breakText(text, measureForwards, maxWidth*mCompatScaling, + int res = native_breakText(text, measureForwards, maxWidth*mCompatScaling, mBidiFlags, measuredWidth); setTextSize(oldSize); if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling; @@ -1534,7 +1534,7 @@ public class Paint { } private native int native_breakText(String text, boolean measureForwards, - float maxWidth, float[] measuredWidth); + float maxWidth, int bidiFlags, float[] measuredWidth); /** * Return the advance widths for the characters in the string. @@ -1560,12 +1560,12 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - return native_getTextWidths(mNativePaint, text, index, count, widths); + return native_getTextWidths(mNativePaint, text, index, count, mBidiFlags, widths); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - int res = native_getTextWidths(mNativePaint, text, index, count, widths); + int res = native_getTextWidths(mNativePaint, text, index, count, mBidiFlags, widths); setTextSize(oldSize); for (int i=0; i<res; i++) { widths[i] *= mInvCompatScaling; @@ -1642,12 +1642,12 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - return native_getTextWidths(mNativePaint, text, start, end, widths); + return native_getTextWidths(mNativePaint, text, start, end, mBidiFlags, widths); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - int res = native_getTextWidths(mNativePaint, text, start, end, widths); + int res = native_getTextWidths(mNativePaint, text, start, end, mBidiFlags, widths); setTextSize(oldSize); for (int i=0; i<res; i++) { widths[i] *= mInvCompatScaling; @@ -2073,7 +2073,7 @@ public class Paint { if (bounds == null) { throw new NullPointerException("need bounds Rect"); } - nativeGetStringBounds(mNativePaint, text, start, end, bounds); + nativeGetStringBounds(mNativePaint, text, start, end, mBidiFlags, bounds); } /** @@ -2093,7 +2093,7 @@ public class Paint { if (bounds == null) { throw new NullPointerException("need bounds Rect"); } - nativeGetCharArrayBounds(mNativePaint, text, index, count, bounds); + nativeGetCharArrayBounds(mNativePaint, text, index, count, mBidiFlags, bounds); } @Override @@ -2140,9 +2140,9 @@ public class Paint { String locale); private static native int native_getTextWidths(int native_object, - char[] text, int index, int count, float[] widths); + char[] text, int index, int count, int bidiFlags, float[] widths); private static native int native_getTextWidths(int native_object, - String text, int start, int end, float[] widths); + String text, int start, int end, int bidiFlags, float[] widths); private static native int native_getTextGlyphs(int native_object, String text, int start, int end, int contextStart, int contextEnd, @@ -2165,8 +2165,8 @@ public class Paint { private static native void native_getTextPath(int native_object, int bidiFlags, String text, int start, int end, float x, float y, int path); private static native void nativeGetStringBounds(int nativePaint, - String text, int start, int end, Rect bounds); + String text, int start, int end, int bidiFlags, Rect bounds); private static native void nativeGetCharArrayBounds(int nativePaint, - char[] text, int index, int count, Rect bounds); + char[] text, int index, int count, int bidiFlags, Rect bounds); private static native void finalizer(int nativePaint); } diff --git a/graphics/java/android/renderscript/ScriptIntrinsicBlur.java b/graphics/java/android/renderscript/ScriptIntrinsicBlur.java index 7ffd1e7..2848f64 100644 --- a/graphics/java/android/renderscript/ScriptIntrinsicBlur.java +++ b/graphics/java/android/renderscript/ScriptIntrinsicBlur.java @@ -46,7 +46,7 @@ public final class ScriptIntrinsicBlur extends ScriptIntrinsic { * @return ScriptIntrinsicBlur */ public static ScriptIntrinsicBlur create(RenderScript rs, Element e) { - if ((e != Element.U8_4(rs)) && e != (Element.U8(rs))) { + if ((!e.isCompatible(Element.U8_4(rs))) && (!e.isCompatible(Element.U8(rs)))) { throw new RSIllegalArgumentException("Unsuported element type."); } int id = rs.nScriptIntrinsicCreate(5, e.getID(rs)); diff --git a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java index b219978..f7e844e 100644 --- a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java +++ b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java @@ -47,7 +47,7 @@ public final class ScriptIntrinsicColorMatrix extends ScriptIntrinsic { * @return ScriptIntrinsicColorMatrix */ public static ScriptIntrinsicColorMatrix create(RenderScript rs, Element e) { - if (e != Element.U8_4(rs)) { + if (!e.isCompatible(Element.U8_4(rs))) { throw new RSIllegalArgumentException("Unsuported element type."); } int id = rs.nScriptIntrinsicCreate(2, e.getID(rs)); diff --git a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java index b40ea84..d54df96 100644 --- a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java +++ b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java @@ -48,7 +48,7 @@ public final class ScriptIntrinsicConvolve3x3 extends ScriptIntrinsic { */ public static ScriptIntrinsicConvolve3x3 create(RenderScript rs, Element e) { float f[] = { 0, 0, 0, 0, 1, 0, 0, 0, 0}; - if (e != Element.U8_4(rs)) { + if (!e.isCompatible(Element.U8_4(rs))) { throw new RSIllegalArgumentException("Unsuported element type."); } int id = rs.nScriptIntrinsicCreate(1, e.getID(rs)); diff --git a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java index 79a7630..83faf35 100644 --- a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java +++ b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java @@ -28,10 +28,28 @@ import java.util.Date; import javax.security.auth.x500.X500Principal; /** - * This provides the required parameters needed for initializing the KeyPair - * generator that works with - * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore - * facility</a>. + * This provides the required parameters needed for initializing the + * {@code KeyPairGenerator} that works with <a href="{@docRoot} + * guide/topics/security/keystore.html">Android KeyStore facility</a>. The + * Android KeyStore facility is accessed through a + * {@link java.security.KeyPairGenerator} API using the + * {@code AndroidKeyPairGenerator} provider. The {@code context} passed in may + * be used to pop up some UI to ask the user to unlock or initialize the Android + * keystore facility. + * <p> + * After generation, the {@code keyStoreAlias} is used with the + * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)} + * interface to retrieve the {@link PrivateKey} and its associated + * {@link Certificate} chain. + * <p> + * The KeyPair generator will create a self-signed certificate with the subject + * as its X.509v3 Subject Distinguished Name and as its X.509v3 Issuer + * Distinguished Name along with the other parameters specified with the + * {@link Builder}. + * <p> + * The self-signed certificate may be replaced at a later time by a certificate + * signed by a real Certificate Authority. + * * @hide */ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { @@ -74,6 +92,7 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { * period * @throws IllegalArgumentException when any argument is {@code null} or * {@code endDate} is before {@code startDate}. + * @hide should be built with AndroidKeyPairGeneratorSpecBuilder */ public AndroidKeyPairGeneratorSpec(Context context, String keyStoreAlias, X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate) { @@ -142,4 +161,121 @@ public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec { Date getEndDate() { return mEndDate; } + + /** + * Builder class for {@link AndroidKeyPairGeneratorSpec} objects. + * <p> + * This will build a parameter spec for use with the <a href="{@docRoot} + * guide/topics/security/keystore.html">Android KeyStore facility</a>. + * <p> + * The required fields must be filled in with the builder. + * <p> + * Example: + * + * <pre class="prettyprint"> + * Calendar start = new Calendar(); + * Calendar end = new Calendar(); + * end.add(1, Calendar.YEAR); + * + * AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(mContext) + * .setAlias("myKey") + * .setSubject(new X500Principal("CN=myKey")) + * .setSerial(BigInteger.valueOf(1337)) + * .setStartDate(start.getTime()) + * .setEndDate(end.getTime()) + * .build(); + * </pre> + */ + public static class Builder { + private final Context mContext; + + private String mKeystoreAlias; + + private X500Principal mSubjectDN; + + private BigInteger mSerialNumber; + + private Date mStartDate; + + private Date mEndDate; + + public Builder(Context context) { + if (context == null) { + throw new NullPointerException("context == null"); + } + mContext = context; + } + + /** + * Sets the alias to be used to retrieve the key later from a + * {@link java.security.KeyStore} instance using the + * {@code AndroidKeyStore} provider. + */ + public Builder setAlias(String alias) { + if (alias == null) { + throw new NullPointerException("alias == null"); + } + mKeystoreAlias = alias; + return this; + } + + /** + * Sets the subject used for the self-signed certificate of the + * generated key pair. + */ + public Builder setSubject(X500Principal subject) { + if (subject == null) { + throw new NullPointerException("subject == null"); + } + mSubjectDN = subject; + return this; + } + + /** + * Sets the serial number used for the self-signed certificate of the + * generated key pair. + */ + public Builder setSerialNumber(BigInteger serialNumber) { + if (serialNumber == null) { + throw new NullPointerException("serialNumber == null"); + } + mSerialNumber = serialNumber; + return this; + } + + /** + * Sets the start of the validity period for the self-signed certificate + * of the generated key pair. + */ + public Builder setStartDate(Date startDate) { + if (startDate == null) { + throw new NullPointerException("startDate == null"); + } + mStartDate = startDate; + return this; + } + + /** + * Sets the end of the validity period for the self-signed certificate + * of the generated key pair. + */ + public Builder setEndDate(Date endDate) { + if (endDate == null) { + throw new NullPointerException("endDate == null"); + } + mEndDate = endDate; + return this; + } + + /** + * Builds the instance of the {@code AndroidKeyPairGeneratorSpec}. + * + * @throws IllegalArgumentException if a required field is missing + * @return built instance of {@code AndroidKeyPairGeneratorSpec} + */ + public AndroidKeyPairGeneratorSpec build() { + return new AndroidKeyPairGeneratorSpec(mContext, mKeystoreAlias, mSubjectDN, + mSerialNumber, mStartDate, mEndDate); + } + } } diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index d8109ce..166849d 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -49,6 +49,8 @@ public class Credentials { public static final String INSTALL_ACTION = "android.credentials.INSTALL"; + public static final String INSTALL_AS_USER_ACTION = "android.credentials.INSTALL_AS_USER"; + public static final String UNLOCK_ACTION = "com.android.credentials.UNLOCK"; /** Key prefix for CA certificates. */ @@ -83,6 +85,12 @@ public class Credentials { public static final String EXTENSION_PFX = ".pfx"; /** + * Intent extra: install the certificate bundle as this UID instead of + * system. + */ + public static final String EXTRA_INSTALL_AS_UID = "install_as_uid"; + + /** * Intent extra: name for the user's private key. */ public static final String EXTRA_USER_PRIVATE_KEY_NAME = "user_private_key_name"; diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index d7119fff..c99dff0 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -356,6 +356,30 @@ public final class KeyChain { } } + /** + * Returns {@code true} if the current device's {@code KeyChain} supports a + * specific {@code PrivateKey} type indicated by {@code algorithm} (e.g., + * "RSA"). + */ + public static boolean isKeyAlgorithmSupported(String algorithm) { + return "RSA".equals(algorithm); + } + + /** + * Returns {@code true} if the current device's {@code KeyChain} binds any + * {@code PrivateKey} of the given {@code algorithm} to the device once + * imported or generated. This can be used to tell if there is special + * hardware support that can be used to bind keys to the device in a way + * that makes it non-exportable. + */ + public static boolean isBoundKeyAlgorithm(String algorithm) { + if (!isKeyAlgorithmSupported(algorithm)) { + return false; + } + + return KeyStore.getInstance().isHardwareBacked(); + } + private static X509Certificate toCertificate(byte[] bytes) { if (bytes == null) { throw new IllegalArgumentException("bytes == null"); diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 12c0ed8..852f0bb 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -296,6 +296,24 @@ public class KeyStore { } } + public boolean isHardwareBacked() { + try { + return mBinder.is_hardware_backed() == NO_ERROR; + } catch (RemoteException e) { + Log.w(TAG, "Cannot connect to keystore", e); + return false; + } + } + + public boolean clearUid(int uid) { + try { + return mBinder.clear_uid(uid) == NO_ERROR; + } catch (RemoteException e) { + Log.w(TAG, "Cannot connect to keystore", e); + return false; + } + } + public int getLastError() { return mError; } diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java index e6a3750..3d275cd 100644 --- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java +++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java @@ -53,6 +53,26 @@ public class AndroidKeyPairGeneratorSpecTest extends AndroidTestCase { assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate()); } + public void testBuilder_Success() throws Exception { + AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(getContext()) + .setAlias(TEST_ALIAS_1) + .setSubject(TEST_DN_1) + .setSerialNumber(SERIAL_1) + .setStartDate(NOW) + .setEndDate(NOW_PLUS_10_YEARS) + .build(); + + assertEquals("Context should be the one specified", getContext(), spec.getContext()); + + assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias()); + + assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN()); + + assertEquals("startDate should be the one specified", NOW, spec.getStartDate()); + + assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate()); + } + public void testConstructor_NullContext_Failure() throws Exception { try { new AndroidKeyPairGeneratorSpec(null, TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW, diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 020c1e9..fe51bf9 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -34,6 +34,9 @@ namespace android { namespace uirenderer { +// Depth of the save stack at the beginning of batch playback at flush time +#define FLUSH_SAVE_STACK_DEPTH 2 + ///////////////////////////////////////////////////////////////////////////////// // Operation Batches ///////////////////////////////////////////////////////////////////////////////// @@ -75,7 +78,7 @@ public: for (unsigned int i = 0; i < mOps.size(); i++) { DrawOp* op = mOps[i]; - renderer.restoreDisplayState(op->state, kStateDeferFlag_Draw); + renderer.restoreDisplayState(op->state); #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS renderer.eventMark(op->name()); @@ -106,7 +109,7 @@ public: virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) { DEFER_LOGD("replaying state op batch %p", this); - renderer.restoreDisplayState(mOp->state, 0); + renderer.restoreDisplayState(mOp->state); // use invalid save count because it won't be used at flush time - RestoreToCountOp is the // only one to use it, and we don't use that class at flush time, instead calling @@ -117,12 +120,12 @@ public: } private: - StateOp* mOp; + const StateOp* mOp; }; class RestoreToCountBatch : public DrawOpBatch { public: - RestoreToCountBatch(int restoreCount) : mRestoreCount(restoreCount) {} + RestoreToCountBatch(StateOp* op, int restoreCount) : mOp(op), mRestoreCount(restoreCount) {} bool intersects(Rect& rect) { // if something checks for intersection, it's trying to go backwards across a state op, @@ -133,11 +136,15 @@ public: virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) { DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount); + + renderer.restoreDisplayState(mOp->state); renderer.restoreToCount(mRestoreCount); return DrawGlInfo::kStatusDone; } private: + // we use the state storage for the RestoreToCountOp, but don't replay the op itself + const StateOp* mOp; /* * The count used here represents the flush() time saveCount. This is as opposed to the * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and @@ -251,7 +258,8 @@ void DeferredDisplayList::addSave(OpenGLRenderer& renderer, SaveOp* op, int newS * Either will act as a barrier to draw operation reordering, as we want to play back layer * save/restore and complex canvas modifications (including save/restore) in order. */ -void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount) { +void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* op, + int newSaveCount) { DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount); if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) { @@ -265,7 +273,7 @@ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, int newSav while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop(); - storeRestoreToCountBarrier(mSaveStack.size() + 1); + storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + FLUSH_SAVE_STACK_DEPTH); } void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { @@ -338,11 +346,15 @@ void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* resetBatchingState(); } -void DeferredDisplayList::storeRestoreToCountBarrier(int newSaveCount) { +void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op, + int newSaveCount) { DEFER_LOGD("%p adding restore to count %d barrier, pos %d", this, newSaveCount, mBatches.size()); - mBatches.add(new RestoreToCountBatch(newSaveCount)); + // store displayState for the restore operation, as it may be associated with a saveLayer that + // doesn't have kClip_SaveFlag set + renderer.storeDisplayState(op->state, getStateOpDeferFlags()); + mBatches.add(new RestoreToCountBatch(op, newSaveCount)); resetBatchingState(); } @@ -377,6 +389,8 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) { DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers(); renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + // NOTE: depth of the save stack at this point, before playback, should be reflected in + // FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly status |= replayBatchList(mBatches, renderer, dirty); renderer.restoreToCount(1); diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 2afc8c1..3e450da 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -65,7 +65,7 @@ public: void addClip(OpenGLRenderer& renderer, ClipOp* op); void addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount); void addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount); - void addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount); + void addRestoreToCount(OpenGLRenderer& renderer, StateOp* op, int newSaveCount); /** * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if @@ -81,7 +81,7 @@ private: void resetBatchingState(); void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op); - void storeRestoreToCountBarrier(int newSaveCount); + void storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op, int newSaveCount); bool recordingComplexClip() const { return mComplexClipStackStart >= 0; } diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index d985ad0..36c95f9 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -352,7 +352,9 @@ void DisplayList::outputViewProperties(const int level) { } } if (mAlpha < 1) { - if (mCaching || !mHasOverlappingRendering) { + if (mCaching) { + ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mAlpha); + } else if (!mHasOverlappingRendering) { ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha); } else { int flags = SkCanvas::kHasAlphaLayer_SaveFlag; @@ -400,7 +402,9 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, } } if (mAlpha < 1) { - if (mCaching || !mHasOverlappingRendering) { + if (mCaching) { + renderer.setOverrideLayerAlpha(mAlpha); + } else if (!mHasOverlappingRendering) { renderer.scaleAlpha(mAlpha); } else { // TODO: should be able to store the size of a DL at record time and not @@ -513,6 +517,7 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT); renderer.restoreToCount(restoreTo); + renderer.setOverrideLayerAlpha(1.0f); } }; // namespace uirenderer diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 4f2db69..a5dee9f 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -117,7 +117,7 @@ public: applyState(replayStruct.mRenderer, saveCount); } - virtual void applyState(OpenGLRenderer& renderer, int saveCount) = 0; + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const = 0; }; class DrawOp : public DisplayListOp { @@ -165,7 +165,11 @@ public: return DeferredDisplayList::kOpBatch_None; } - float strokeWidthOutset() { return mPaint->getStrokeWidth() * 0.5f; } + float strokeWidthOutset() { + float width = mPaint->getStrokeWidth(); + if (width == 0) return 0.5f; // account for hairline + return width * 0.5f; + } protected: SkPaint* getPaint(OpenGLRenderer& renderer) { @@ -223,7 +227,7 @@ public: deferStruct.mDeferredList.addSave(deferStruct.mRenderer, this, newSaveCount); } - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.save(mFlags); } @@ -251,11 +255,12 @@ public: : mCount(count) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) { - deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer, saveCount + mCount); + deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer, + this, saveCount + mCount); deferStruct.mRenderer.restoreToCount(saveCount + mCount); } - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.restoreToCount(saveCount + mCount); } @@ -293,7 +298,7 @@ public: mAlpha, mMode, mFlags); } - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mMode, mFlags); } @@ -330,7 +335,7 @@ public: TranslateOp(float dx, float dy) : mDx(dx), mDy(dy) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.translate(mDx, mDy); } @@ -350,7 +355,7 @@ public: RotateOp(float degrees) : mDegrees(degrees) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.rotate(mDegrees); } @@ -369,7 +374,7 @@ public: ScaleOp(float sx, float sy) : mSx(sx), mSy(sy) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.scale(mSx, mSy); } @@ -389,7 +394,7 @@ public: SkewOp(float sx, float sy) : mSx(sx), mSy(sy) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.skew(mSx, mSy); } @@ -409,7 +414,7 @@ public: SetMatrixOp(SkMatrix* matrix) : mMatrix(matrix) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.setMatrix(mMatrix); } @@ -428,7 +433,7 @@ public: ConcatMatrixOp(SkMatrix* matrix) : mMatrix(matrix) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.concatMatrix(mMatrix); } @@ -471,7 +476,7 @@ public: ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op) : ClipOp(op), mArea(left, top, right, bottom) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp); } @@ -500,7 +505,7 @@ public: ClipPathOp(SkPath* path, SkRegion::Op op) : ClipOp(op), mPath(path) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.clipPath(mPath, mOp); } @@ -521,7 +526,7 @@ public: ClipRegionOp(SkRegion* region, SkRegion::Op op) : ClipOp(op), mRegion(region) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.clipRegion(mRegion, mOp); } @@ -540,7 +545,7 @@ private: class ResetShaderOp : public StateOp { public: - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.resetShader(); } @@ -555,7 +560,7 @@ class SetupShaderOp : public StateOp { public: SetupShaderOp(SkiaShader* shader) : mShader(shader) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.setupShader(mShader); } @@ -571,7 +576,7 @@ private: class ResetColorFilterOp : public StateOp { public: - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.resetColorFilter(); } @@ -587,7 +592,7 @@ public: SetupColorFilterOp(SkiaColorFilter* colorFilter) : mColorFilter(colorFilter) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.setupColorFilter(mColorFilter); } @@ -603,7 +608,7 @@ private: class ResetShadowOp : public StateOp { public: - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.resetShadow(); } @@ -619,7 +624,7 @@ public: SetupShadowOp(float radius, float dx, float dy, int color) : mRadius(radius), mDx(dx), mDy(dy), mColor(color) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.setupShadow(mRadius, mDx, mDy, mColor); } @@ -638,7 +643,7 @@ private: class ResetPaintFilterOp : public StateOp { public: - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.resetPaintFilter(); } @@ -654,7 +659,7 @@ public: SetupPaintFilterOp(int clearBits, int setBits) : mClearBits(clearBits), mSetBits(setBits) {} - virtual void applyState(OpenGLRenderer& renderer, int saveCount) { + virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { renderer.setupPaintFilter(mClearBits, mSetBits); } diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 07daa3b..0b8f7e6 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -248,9 +248,7 @@ status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList, } status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y) { - mLayers.add(layer); - mCaches.resourceCache.incrementRefcount(layer); - + layer = refLayer(layer); addDrawOp(new (alloc()) DrawLayerOp(layer, x, y)); return DrawGlInfo::kStatusDone; } diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 50e552f..19f7eb6 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -271,6 +271,12 @@ private: return copy; } + inline Layer* refLayer(Layer* layer) { + mLayers.add(layer); + mCaches.resourceCache.incrementRefcount(layer); + return layer; + } + inline SkBitmap* refBitmap(SkBitmap* bitmap) { // Note that this assumes the bitmap is immutable. There are cases this won't handle // correctly, such as creating the bitmap from scratch, drawing with it, changing its diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp index e80b325..e80b325 100755..100644 --- a/libs/hwui/Dither.cpp +++ b/libs/hwui/Dither.cpp diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h index 34cf9bf..34cf9bf 100755..100644 --- a/libs/hwui/Dither.h +++ b/libs/hwui/Dither.h diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 2998535..63bb73f 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -75,6 +75,13 @@ bool Layer::resize(const uint32_t width, const uint32_t height) { return true; } + const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize; + if (desiredWidth > maxTextureSize || desiredHeight > maxTextureSize) { + ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)", + desiredWidth, desiredHeight, maxTextureSize, maxTextureSize); + return false; + } + uint32_t oldWidth = getWidth(); uint32_t oldHeight = getHeight(); @@ -158,10 +165,13 @@ void Layer::defer() { dirtyRect.right, dirtyRect.bottom, !isBlend()); displayList->defer(deferredState, 0); + + deferredUpdateScheduled = false; + displayList = NULL; } void Layer::flush() { - if (deferredList && !deferredList->isEmpty()) { + if (deferredList) { renderer->setViewport(layer.getWidth(), layer.getHeight()); renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); @@ -176,5 +186,21 @@ void Layer::flush() { } } +void Layer::render() { + renderer->setViewport(layer.getWidth(), layer.getHeight()); + renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, + !isBlend()); + + renderer->drawDisplayList(displayList, dirtyRect, DisplayList::kReplayFlag_ClipChildren); + + renderer->finish(); + renderer = NULL; + + dirtyRect.setEmpty(); + + deferredUpdateScheduled = false; + displayList = NULL; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 0e00191..27e0cf1 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -275,6 +275,7 @@ struct Layer { void defer(); void flush(); + void render(); /** * Bounds of the layer. diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index bb02286..8451048 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -222,6 +222,21 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque return NULL; } + // We first obtain a layer before comparing against the max texture size + // because layers are not allocated at the exact desired size. They are + // always created slighly larger to improve recycling + const uint32_t maxTextureSize = caches.maxTextureSize; + if (layer->getWidth() > maxTextureSize || layer->getHeight() > maxTextureSize) { + ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)", + width, height, maxTextureSize, maxTextureSize); + + // Creating a new layer always increment its refcount by 1, this allows + // us to destroy the layer object if one was created for us + Caches::getInstance().resourceCache.decrementRefcount(layer); + + return NULL; + } + layer->setFbo(fbo); layer->layer.set(0.0f, 0.0f, width, height); layer->texCoords.set(0.0f, height / float(layer->getHeight()), @@ -243,14 +258,11 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque layer->setEmpty(false); layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE); + // This should only happen if we run out of memory if (glGetError() != GL_NO_ERROR) { - ALOGD("Could not allocate texture for layer (fbo=%d %dx%d)", - fbo, width, height); - + ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height); glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - - Caches::getInstance().resourceCache.decrementRefcount(layer); - + caches.resourceCache.decrementRefcount(layer); return NULL; } } @@ -272,7 +284,6 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { layer->texCoords.set(0.0f, height / float(layer->getHeight()), width / float(layer->getWidth()), 0.0f); } else { - Caches::getInstance().resourceCache.decrementRefcount(layer); return false; } } diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 2903bcd..1138998 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -114,6 +114,7 @@ OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) { mDrawModifiers.mShader = NULL; mDrawModifiers.mColorFilter = NULL; + mDrawModifiers.mOverrideLayerAlpha = 1.0f; mDrawModifiers.mHasShadow = false; mDrawModifiers.mHasDrawFilter = false; @@ -540,13 +541,7 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { } if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) { - OpenGLRenderer* renderer = layer->renderer; - renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight()); - renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, - !layer->isBlend()); - renderer->drawDisplayList(layer->displayList, dirty, - DisplayList::kReplayFlag_ClipChildren); - renderer->finish(); + layer->render(); } else { layer->defer(); } @@ -556,13 +551,6 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { startTiling(mSnapshot); } - if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) { - dirty.setEmpty(); - layer->renderer = NULL; - } - - layer->deferredUpdateScheduled = false; - layer->displayList = NULL; layer->debugDrawUpdate = mCaches.debugLayersUpdates; return true; @@ -609,11 +597,12 @@ void OpenGLRenderer::flushLayers() { // Note: it is very important to update the layers in reverse order for (int i = count - 1; i >= 0; i--) { sprintf(layerName, "Layer #%d", i); - startMark(layerName); { - Layer* layer = mLayerUpdates.itemAt(i); - layer->flush(); - mCaches.resourceCache.decrementRefcount(layer); - } + startMark(layerName); + + Layer* layer = mLayerUpdates.itemAt(i); + layer->flush(); + mCaches.resourceCache.decrementRefcount(layer); + endMark(); } @@ -626,6 +615,15 @@ void OpenGLRenderer::flushLayers() { void OpenGLRenderer::pushLayerUpdate(Layer* layer) { if (layer) { + // Make sure we don't introduce duplicates. + // SortedVector would do this automatically but we need to respect + // the insertion order. The linear search is not an issue since + // this list is usually very short (typically one item, at most a few) + for (int i = mLayerUpdates.size() - 1; i >= 0; i--) { + if (mLayerUpdates.itemAt(i) == layer) { + return; + } + } mLayerUpdates.push_back(layer); mCaches.resourceCache.incrementRefcount(layer); } @@ -696,7 +694,10 @@ bool OpenGLRenderer::restoreSnapshot() { } if (restoreLayer) { + endMark(); // Savelayer + startMark("ComposeLayer"); composeLayer(current, previous); + endMark(); } return restoreClip; @@ -874,6 +875,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto mSnapshot->flags |= Snapshot::kFlagIsLayer; mSnapshot->layer = layer; + startMark("SaveLayer"); if (fboLayer) { return createFboLayer(layer, bounds, clip, previousFbo); } else { @@ -1073,7 +1075,7 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) layer->setFilter(GL_LINEAR, true); } - float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha; + float alpha = getLayerAlpha(layer); bool blend = layer->isBlend() || alpha < 1.0f; drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(), layer->getTexture(), alpha, layer->getMode(), blend, @@ -1111,7 +1113,7 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { rects = safeRegion.getArray(&count); } - const float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha; + const float alpha = getLayerAlpha(layer); const float texX = 1.0f / float(layer->getWidth()); const float texY = 1.0f / float(layer->getHeight()); const float height = rect.getHeight(); @@ -1326,8 +1328,6 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef } else { state.mBounds.set(currentClip); } - state.mDrawModifiers = mDrawModifiers; - state.mAlpha = mSnapshot->alpha; } if (stateDeferFlags & kStateDeferFlag_Clip) { @@ -1336,18 +1336,18 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef state.mClip.setEmpty(); } - // transform always deferred + // Transform, drawModifiers, and alpha always deferred, since they are used by state operations + // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything) state.mMatrix.load(currentMatrix); + state.mDrawModifiers = mDrawModifiers; + state.mAlpha = mSnapshot->alpha; return false; } -void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, int stateDeferFlags) { +void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state) { currentTransform().load(state.mMatrix); - - if (stateDeferFlags & kStateDeferFlag_Draw) { - mDrawModifiers = state.mDrawModifiers; - mSnapshot->alpha = state.mAlpha; - } + mDrawModifiers = state.mDrawModifiers; + mSnapshot->alpha = state.mAlpha; if (!state.mClip.isEmpty()) { mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom); @@ -2238,7 +2238,7 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const float left, float top, float right, float bottom, SkPaint* paint) { int alpha; SkXfermode::Mode mode; - getAlphaAndModeDirect(paint, &alpha, &mode); + getAlphaAndMode(paint, &alpha, &mode); return drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors, left, top, right, bottom, alpha, mode); @@ -2991,7 +2991,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { if (layer->region.isRect()) { composeLayerRect(layer, layer->regionRect); } else if (layer->mesh) { - const float a = layer->getAlpha() / 255.0f * mSnapshot->alpha; + const float a = getLayerAlpha(layer); setupDraw(); setupDrawWithTexture(); setupDrawColor(a, a, a, a); @@ -3447,10 +3447,24 @@ void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, flo TextureVertex::setUV(v++, u2, v2); } -void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) { +void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const { getAlphaAndModeDirect(paint, alpha, mode); + if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) { + // if drawing a layer, ignore the paint's alpha + *alpha = mDrawModifiers.mOverrideLayerAlpha; + } *alpha *= mSnapshot->alpha; } +float OpenGLRenderer::getLayerAlpha(Layer* layer) const { + float alpha; + if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) { + alpha = mDrawModifiers.mOverrideLayerAlpha; + } else { + alpha = layer->getAlpha() / 255.0f; + } + return alpha * mSnapshot->alpha; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index b17bc3f..dd7a5a2 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -51,6 +51,7 @@ namespace uirenderer { struct DrawModifiers { SkiaShader* mShader; SkiaColorFilter* mColorFilter; + float mOverrideLayerAlpha; // Drop shadow bool mHasShadow; @@ -275,15 +276,17 @@ public: virtual void resetPaintFilter(); virtual void setupPaintFilter(int clearBits, int setBits); + // If this value is set to < 1.0, it overrides alpha set on layer (see drawBitmap, drawLayer) + void setOverrideLayerAlpha(float alpha) { mDrawModifiers.mOverrideLayerAlpha = alpha; } + SkPaint* filterPaint(SkPaint* paint); bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags); - void restoreDisplayState(const DeferredDisplayState& state, int stateDeferFlags); + void restoreDisplayState(const DeferredDisplayState& state); const DrawModifiers& getDrawModifiers() { return mDrawModifiers; } void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; } - // TODO: what does this mean? no perspective? no rotate? ANDROID_API bool isCurrentTransformSimple() { return mSnapshot->transform->isSimple(); } @@ -325,7 +328,8 @@ public: /** * Gets the alpha and xfermode out of a paint object. If the paint is null * alpha will be 255 and the xfermode will be SRC_OVER. This method does - * not multiply the paint's alpha by the current snapshot's alpha. + * not multiply the paint's alpha by the current snapshot's alpha, and does + * not replace the alpha with the overrideLayerAlpha * * @param paint The paint to extract values from * @param alpha Where to store the resulting alpha @@ -450,13 +454,21 @@ protected: /** * Gets the alpha and xfermode out of a paint object. If the paint is null - * alpha will be 255 and the xfermode will be SRC_OVER. + * alpha will be 255 and the xfermode will be SRC_OVER. Accounts for both + * snapshot alpha, and overrideLayerAlpha * * @param paint The paint to extract values from * @param alpha Where to store the resulting alpha * @param mode Where to store the resulting xfermode */ - inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode); + inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const; + + /** + * Gets the alpha from a layer, accounting for snapshot alpha and overrideLayerAlpha + * + * @param layer The layer from which the alpha is extracted + */ + inline float getLayerAlpha(Layer* layer) const; /** * Safely retrieves the mode from the specified xfermode. If the specified diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index 395bbf6..0879b1b 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -61,6 +61,7 @@ void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint bool forceExpand) { if (forceExpand || paint->getStyle() != SkPaint::kFill_Style) { float outset = paint->getStrokeWidth() * 0.5f; + if (outset == 0) outset = 0.5f; // account for hairline bounds.outset(outset, outset); } } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 6f284f8..b80a166 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2127,7 +2127,7 @@ public class AudioManager { mediaButtonIntent.setComponent(eventReceiver); PendingIntent pi = PendingIntent.getBroadcast(mContext, 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/); - unregisterMediaButtonIntent(pi, eventReceiver); + unregisterMediaButtonIntent(pi); } /** @@ -2139,16 +2139,16 @@ public class AudioManager { if (eventReceiver == null) { return; } - unregisterMediaButtonIntent(eventReceiver, null); + unregisterMediaButtonIntent(eventReceiver); } /** * @hide */ - public void unregisterMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) { + public void unregisterMediaButtonIntent(PendingIntent pi) { IAudioService service = getService(); try { - service.unregisterMediaButtonIntent(pi, eventReceiver); + service.unregisterMediaButtonIntent(pi); } catch (RemoteException e) { Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e); } @@ -2274,6 +2274,26 @@ public class AudioManager { } /** + * @hide + * Request the user of a RemoteControlClient to seek to the given playback position. + * @param generationId the RemoteControlClient generation counter for which this request is + * issued. Requests for an older generation than current one will be ignored. + * @param timeMs the time in ms to seek to, must be positive. + */ + public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) { + if (timeMs < 0) { + return; + } + IAudioService service = getService(); + try { + service.setRemoteControlClientPlaybackPosition(generationId, timeMs); + } catch (RemoteException e) { + Log.e(TAG, "Dead object in setRccPlaybackPosition("+ generationId + ", " + + timeMs + ")", e); + } + } + + /** * @hide * Reload audio settings. This method is called by Settings backup * agent when audio settings are restored and causes the AudioService diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 38cdb8a..fd71d79 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -166,12 +166,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private static final int MSG_PROMOTE_RCC = 29; private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30; private static final int MSG_UNLOAD_SOUND_EFFECTS = 31; - - - // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be - // persisted - private static final int PERSIST_CURRENT = 0x1; - private static final int PERSIST_LAST_AUDIBLE = 0x2; + private static final int MSG_RCC_NEW_PLAYBACK_STATE = 32; + private static final int MSG_RCC_SEEK_REQUEST = 33; private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; // Timeout for connection to bluetooth headset service @@ -581,14 +577,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { for (int streamType = 0; streamType < numStreamTypes; streamType++) { if (streamType != mStreamVolumeAlias[streamType]) { mStreamStates[streamType]. - setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], - false /*lastAudible*/); - mStreamStates[streamType]. - setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], - true /*lastAudible*/); + setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]]); } // apply stream volume - if (mStreamStates[streamType].muteCount() == 0) { + if (!mStreamStates[streamType].isMuted()) { mStreamStates[streamType].applyAllVolumes(); } } @@ -632,10 +624,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias; if (updateVolumes) { - mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias], - false /*lastAudible*/); - mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias], - true /*lastAudible*/); + mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias]); sendMsg(mAudioHandler, MSG_SET_ALL_VOLUMES, SENDMSG_QUEUE, @@ -835,14 +824,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { final int device = getDeviceForStream(streamTypeAlias); - // get last audible index if stream is muted, current index otherwise - int aliasIndex = streamState.getIndex(device, - (streamState.muteCount() != 0) /* lastAudible */); + int aliasIndex = streamState.getIndex(device); boolean adjustVolume = true; - int step; - int index; - int oldIndex; // reset any pending volume command synchronized (mSafeMediaVolumeState) { @@ -871,64 +855,40 @@ public class AudioService extends IAudioService.Stub implements OnFinished { step = rescaleIndex(10, streamType, streamTypeAlias); } - if ((direction == AudioManager.ADJUST_RAISE) && - !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { - index = mStreamStates[streamType].getIndex(device, - (streamState.muteCount() != 0) /* lastAudible */); - oldIndex = index; - mVolumePanel.postDisplaySafeVolumeWarning(flags); - } else { - // If either the client forces allowing ringer modes for this adjustment, - // or the stream type is one that is affected by ringer modes - if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || - (streamTypeAlias == getMasterStreamType())) { - int ringerMode = getRingerMode(); - // do not vibrate if already in vibrate mode - if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { - flags &= ~AudioManager.FLAG_VIBRATE; - } - // Check if the ringer mode changes with this volume adjustment. If - // it does, it will handle adjusting the volume, so we won't below - adjustVolume = checkForRingerModeChange(aliasIndex, direction, step); - if ((streamTypeAlias == getMasterStreamType()) && - (mRingerMode == AudioManager.RINGER_MODE_SILENT)) { - streamState.setLastAudibleIndex(0, device); - } - } - - // If stream is muted, adjust last audible index only - oldIndex = mStreamStates[streamType].getIndex(device, - (mStreamStates[streamType].muteCount() != 0) /* lastAudible */); - - if (streamState.muteCount() != 0) { - if (adjustVolume) { - // Post a persist volume msg - // no need to persist volume on all streams sharing the same alias - streamState.adjustLastAudibleIndex(direction * step, device); - sendMsg(mAudioHandler, - MSG_PERSIST_VOLUME, - SENDMSG_QUEUE, - PERSIST_LAST_AUDIBLE, - device, - streamState, - PERSIST_DELAY); - } - index = mStreamStates[streamType].getIndex(device, true /* lastAudible */); - } else { - if (adjustVolume && streamState.adjustIndex(direction * step, device)) { - // Post message to set system volume (it in turn will post a message - // to persist). Do not change volume if stream is muted. - sendMsg(mAudioHandler, - MSG_SET_DEVICE_VOLUME, - SENDMSG_QUEUE, - device, - 0, - streamState, - 0); - } - index = mStreamStates[streamType].getIndex(device, false /* lastAudible */); + // If either the client forces allowing ringer modes for this adjustment, + // or the stream type is one that is affected by ringer modes + if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || + (streamTypeAlias == getMasterStreamType())) { + int ringerMode = getRingerMode(); + // do not vibrate if already in vibrate mode + if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { + flags &= ~AudioManager.FLAG_VIBRATE; + } + // Check if the ringer mode changes with this volume adjustment. If + // it does, it will handle adjusting the volume, so we won't below + adjustVolume = checkForRingerModeChange(aliasIndex, direction, step); + } + + int oldIndex = mStreamStates[streamType].getIndex(device); + + if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) { + if ((direction == AudioManager.ADJUST_RAISE) && + !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { + Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex); + mVolumePanel.postDisplaySafeVolumeWarning(flags); + } else if (streamState.adjustIndex(direction * step, device)) { + // Post message to set system volume (it in turn will post a message + // to persist). Do not change volume if stream is muted. + sendMsg(mAudioHandler, + MSG_SET_DEVICE_VOLUME, + SENDMSG_QUEUE, + device, + 0, + streamState, + 0); } } + int index = mStreamStates[streamType].getIndex(device); sendVolumeUpdate(streamType, oldIndex, index, flags); } @@ -968,6 +928,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { }; private void onSetStreamVolume(int streamType, int index, int flags, int device) { + setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false); // setting volume on master stream type also controls silent mode if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || (mStreamVolumeAlias[streamType] == getMasterStreamType())) { @@ -975,18 +936,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished { if (index == 0) { newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE : AudioManager.RINGER_MODE_SILENT; - setStreamVolumeInt(mStreamVolumeAlias[streamType], - index, - device, - false, - true); } else { newRingerMode = AudioManager.RINGER_MODE_NORMAL; } setRingerMode(newRingerMode); } - - setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true); } /** @see AudioManager#setStreamVolume(int, int, int) */ @@ -1005,9 +959,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // reset any pending volume command mPendingVolumeCommand = null; - // get last audible index if stream is muted, current index otherwise - oldIndex = streamState.getIndex(device, - (streamState.muteCount() != 0) /* lastAudible */); + oldIndex = streamState.getIndex(device); index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]); @@ -1033,9 +985,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { streamType, index, flags, device); } else { onSetStreamVolume(streamType, index, flags, device); - // get last audible index if stream is muted, current index otherwise - index = mStreamStates[streamType].getIndex(device, - (mStreamStates[streamType].muteCount() != 0) /* lastAudible */); + index = mStreamStates[streamType].getIndex(device); } } sendVolumeUpdate(streamType, oldIndex, index, flags); @@ -1197,41 +1147,23 @@ public class AudioService extends IAudioService.Stub implements OnFinished { * @param device the device whose volume must be changed * @param force If true, set the volume even if the desired volume is same * as the current volume. - * @param lastAudible If true, stores new index as last audible one */ private void setStreamVolumeInt(int streamType, int index, int device, - boolean force, - boolean lastAudible) { + boolean force) { VolumeStreamState streamState = mStreamStates[streamType]; - // If stream is muted, set last audible index only - if (streamState.muteCount() != 0) { - // Do not allow last audible index to be 0 - if (index != 0) { - streamState.setLastAudibleIndex(index, device); - // Post a persist volume msg - sendMsg(mAudioHandler, - MSG_PERSIST_VOLUME, - SENDMSG_QUEUE, - PERSIST_LAST_AUDIBLE, - device, - streamState, - PERSIST_DELAY); - } - } else { - if (streamState.setIndex(index, device, lastAudible) || force) { - // Post message to set system volume (it in turn will post a message - // to persist). - sendMsg(mAudioHandler, - MSG_SET_DEVICE_VOLUME, - SENDMSG_QUEUE, - device, - 0, - streamState, - 0); - } + if (streamState.setIndex(index, device) || force) { + // Post message to set system volume (it in turn will post a message + // to persist). + sendMsg(mAudioHandler, + MSG_SET_DEVICE_VOLUME, + SENDMSG_QUEUE, + device, + 0, + streamState, + 0); } } @@ -1243,7 +1175,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { for (int stream = 0; stream < mStreamStates.length; stream++) { if (!isStreamAffectedByMute(stream) || stream == streamType) continue; - // Bring back last audible volume mStreamStates[stream].mute(cb, state); } } @@ -1261,7 +1192,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { /** get stream mute state. */ public boolean isStreamMute(int streamType) { - return (mStreamStates[streamType].muteCount() != 0); + return mStreamStates[streamType].isMuted(); } /** @see AudioManager#setMasterMute(boolean, int) */ @@ -1288,8 +1219,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public int getStreamVolume(int streamType) { ensureValidStreamType(streamType); int device = getDeviceForStream(streamType); - int index = mStreamStates[streamType].getIndex(device, false /* lastAudible */); + int index = mStreamStates[streamType].getIndex(device); + // by convention getStreamVolume() returns 0 when a stream is muted. + if (mStreamStates[streamType].isMuted()) { + index = 0; + } if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && (device & mFixedVolumeDevices) != 0) { index = mStreamStates[streamType].getMaxIndex(); @@ -1346,7 +1281,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public int getLastAudibleStreamVolume(int streamType) { ensureValidStreamType(streamType); int device = getDeviceForStream(streamType); - return (mStreamStates[streamType].getIndex(device, true /* lastAudible */) + 5) / 10; + return (mStreamStates[streamType].getIndex(device) + 5) / 10; } /** Get last audible master volume before it was muted. */ @@ -1411,7 +1346,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { if (mVoiceCapable && mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) { synchronized (mStreamStates[streamType]) { - Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet(); + Set set = mStreamStates[streamType].mIndex.entrySet(); Iterator i = set.iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry)i.next(); @@ -1533,7 +1468,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode if (newModeOwnerPid != 0) { - disconnectBluetoothSco(newModeOwnerPid); + final long ident = Binder.clearCallingIdentity(); + disconnectBluetoothSco(newModeOwnerPid); + Binder.restoreCallingIdentity(ident); } } @@ -1658,8 +1595,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { streamType = AudioManager.STREAM_MUSIC; } int device = getDeviceForStream(streamType); - int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false); - setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, false); + int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device); + setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true); updateStreamVolumeAlias(true /*updateVolumes*/); } @@ -1893,7 +1830,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { streamState.readSettings(); // unmute stream that was muted but is not affect by mute anymore - if (streamState.muteCount() != 0 && ((!isStreamAffectedByMute(streamType) && + if (streamState.isMuted() && ((!isStreamAffectedByMute(streamType) && !isStreamMutedByRingerMode(streamType)) || mUseFixedVolume)) { int size = streamState.mDeathHandlers.size(); for (int i = 0; i < size; i++) { @@ -2393,8 +2330,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { 0, null, MUSIC_ACTIVE_POLL_PERIOD_MS); - int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device, - false /*lastAudible*/); + int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device); if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) && (index > mSafeMediaVolumeIndex)) { // Approximate cumulative active music time @@ -2739,18 +2675,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private final int mStreamType; private String mVolumeIndexSettingName; - private String mLastAudibleVolumeIndexSettingName; private int mIndexMax; private final ConcurrentHashMap<Integer, Integer> mIndex = new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4); - private final ConcurrentHashMap<Integer, Integer> mLastAudibleIndex = - new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4); private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death private VolumeStreamState(String settingName, int streamType) { mVolumeIndexSettingName = settingName; - mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; mStreamType = streamType; mIndexMax = MAX_STREAM_VOLUME[streamType]; @@ -2763,10 +2695,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { readSettings(); } - public String getSettingNameForDevice(boolean lastAudible, int device) { - String name = lastAudible ? - mLastAudibleVolumeIndexSettingName : - mVolumeIndexSettingName; + public String getSettingNameForDevice(int device) { + String name = mVolumeIndexSettingName; String suffix = AudioSystem.getDeviceName(device); if (suffix.isEmpty()) { return name; @@ -2778,14 +2708,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // force maximum volume on all streams if fixed volume property is set if (mUseFixedVolume) { mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax); - mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax); return; } // do not read system stream volume from settings: this stream is always aliased // to another stream type and its volume is never persisted. Values in settings can // only be stale values - // on first call to readSettings() at init time, muteCount() is always 0 so we will - // always create entries for default device if ((mStreamType == AudioSystem.STREAM_SYSTEM) || (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) { int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; @@ -2794,10 +2721,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { index = mIndexMax; } } - if (muteCount() == 0) { - mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index); - } - mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index); + mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index); return; } @@ -2811,7 +2735,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { remainingDevices &= ~device; // retrieve current volume for device - String name = getSettingNameForDevice(false /* lastAudible */, device); + String name = getSettingNameForDevice(device); // if no volume stored for current stream and device, use default volume if default // device, continue otherwise int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ? @@ -2825,72 +2749,33 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // ignore settings for fixed volume devices: volume should always be at max or 0 if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) && ((device & mFixedVolumeDevices) != 0)) { - if ((muteCount()) == 0 && (index != 0)) { - mIndex.put(device, mIndexMax); - } else { - mIndex.put(device, 0); - } - mLastAudibleIndex.put(device, mIndexMax); - continue; - } - - // retrieve last audible volume for device - name = getSettingNameForDevice(true /* lastAudible */, device); - // use stored last audible index if present, otherwise use current index if not 0 - // or default index - defaultIndex = (index > 0) ? - index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; - int lastAudibleIndex = Settings.System.getIntForUser( - mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT); - - // a last audible index of 0 should never be stored for ring and notification - // streams on phones (voice capable devices). - if ((lastAudibleIndex == 0) && mVoiceCapable && - (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) { - lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; - // Correct the data base - sendMsg(mAudioHandler, - MSG_PERSIST_VOLUME, - SENDMSG_QUEUE, - PERSIST_LAST_AUDIBLE, - device, - this, - PERSIST_DELAY); - } - mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex)); - // the initial index should never be 0 for ring and notification streams on phones - // (voice capable devices) if not in silent or vibrate mode. - if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) && - mVoiceCapable && - (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) { - index = lastAudibleIndex; - // Correct the data base - sendMsg(mAudioHandler, - MSG_PERSIST_VOLUME, - SENDMSG_QUEUE, - PERSIST_CURRENT, - device, - this, - PERSIST_DELAY); - } - if (muteCount() == 0) { + mIndex.put(device, (index != 0) ? mIndexMax : 0); + } else { mIndex.put(device, getValidIndex(10 * index)); } } } public void applyDeviceVolume(int device) { - AudioSystem.setStreamVolumeIndex(mStreamType, - (getIndex(device, false /* lastAudible */) + 5)/10, - device); + int index; + if (isMuted()) { + index = 0; + } else { + index = (getIndex(device) + 5)/10; + } + AudioSystem.setStreamVolumeIndex(mStreamType, index, device); } public synchronized void applyAllVolumes() { // apply default volume first: by convention this will reset all // devices volumes in audio policy manager to the supplied value - AudioSystem.setStreamVolumeIndex(mStreamType, - (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10, - AudioSystem.DEVICE_OUT_DEFAULT); + int index; + if (isMuted()) { + index = 0; + } else { + index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10; + } + AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT); // then apply device specific volumes Set set = mIndex.entrySet(); Iterator i = set.iterator(); @@ -2898,22 +2783,23 @@ public class AudioService extends IAudioService.Stub implements OnFinished { Map.Entry entry = (Map.Entry)i.next(); int device = ((Integer)entry.getKey()).intValue(); if (device != AudioSystem.DEVICE_OUT_DEFAULT) { - AudioSystem.setStreamVolumeIndex(mStreamType, - ((Integer)entry.getValue() + 5)/10, - device); + if (isMuted()) { + index = 0; + } else { + index = ((Integer)entry.getValue() + 5)/10; + } + AudioSystem.setStreamVolumeIndex(mStreamType, index, device); } } } public boolean adjustIndex(int deltaIndex, int device) { - return setIndex(getIndex(device, - false /* lastAudible */) + deltaIndex, - device, - true /* lastAudible */); + return setIndex(getIndex(device) + deltaIndex, + device); } - public synchronized boolean setIndex(int index, int device, boolean lastAudible) { - int oldIndex = getIndex(device, false /* lastAudible */); + public synchronized boolean setIndex(int index, int device) { + int oldIndex = getIndex(device); index = getValidIndex(index); synchronized (mCameraSoundForced) { if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) { @@ -2923,9 +2809,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mIndex.put(device, index); if (oldIndex != index) { - if (lastAudible) { - mLastAudibleIndex.put(device, index); - } // Apply change to all streams using this one as alias // if changing volume of current device, also change volume of current // device on aliased stream @@ -2936,12 +2819,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mStreamVolumeAlias[streamType] == mStreamType) { int scaledIndex = rescaleIndex(index, mStreamType, streamType); mStreamStates[streamType].setIndex(scaledIndex, - device, - lastAudible); + device); if (currentDevice) { mStreamStates[streamType].setIndex(scaledIndex, - getDeviceForStream(streamType), - lastAudible); + getDeviceForStream(streamType)); } } } @@ -2951,63 +2832,21 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } - public synchronized int getIndex(int device, boolean lastAudible) { - ConcurrentHashMap <Integer, Integer> indexes; - if (lastAudible) { - indexes = mLastAudibleIndex; - } else { - indexes = mIndex; - } - Integer index = indexes.get(device); + public synchronized int getIndex(int device) { + Integer index = mIndex.get(device); if (index == null) { // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT - index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT); + index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT); } return index.intValue(); } - public synchronized void setLastAudibleIndex(int index, int device) { - // Apply change to all streams using this one as alias - // if changing volume of current device, also change volume of current - // device on aliased stream - boolean currentDevice = (device == getDeviceForStream(mStreamType)); - int numStreamTypes = AudioSystem.getNumStreamTypes(); - for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { - if (streamType != mStreamType && - mStreamVolumeAlias[streamType] == mStreamType) { - int scaledIndex = rescaleIndex(index, mStreamType, streamType); - mStreamStates[streamType].setLastAudibleIndex(scaledIndex, device); - if (currentDevice) { - mStreamStates[streamType].setLastAudibleIndex(scaledIndex, - getDeviceForStream(streamType)); - } - } - } - mLastAudibleIndex.put(device, getValidIndex(index)); - } - - public synchronized void adjustLastAudibleIndex(int deltaIndex, int device) { - setLastAudibleIndex(getIndex(device, - true /* lastAudible */) + deltaIndex, - device); - } - public int getMaxIndex() { return mIndexMax; } - // only called by setAllIndexes() which is already synchronized - public ConcurrentHashMap <Integer, Integer> getAllIndexes(boolean lastAudible) { - if (lastAudible) { - return mLastAudibleIndex; - } else { - return mIndex; - } - } - - public synchronized void setAllIndexes(VolumeStreamState srcStream, boolean lastAudible) { - ConcurrentHashMap <Integer, Integer> indexes = srcStream.getAllIndexes(lastAudible); - Set set = indexes.entrySet(); + public synchronized void setAllIndexes(VolumeStreamState srcStream) { + Set set = srcStream.mIndex.entrySet(); Iterator i = set.iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry)i.next(); @@ -3015,11 +2854,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { int index = ((Integer)entry.getValue()).intValue(); index = rescaleIndex(index, srcStream.getStreamType(), mStreamType); - if (lastAudible) { - setLastAudibleIndex(index, device); - } else { - setIndex(index, device, false /* lastAudible */); - } + setIndex(index, device); } } @@ -3030,12 +2865,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { Map.Entry entry = (Map.Entry)i.next(); entry.setValue(mIndexMax); } - set = mLastAudibleIndex.entrySet(); - i = set.iterator(); - while (i.hasNext()) { - Map.Entry entry = (Map.Entry)i.next(); - entry.setValue(mIndexMax); - } } public synchronized void mute(IBinder cb, boolean state) { @@ -3071,6 +2900,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // must be called while synchronized on parent VolumeStreamState public void mute(boolean state) { + boolean updateVolume = false; if (state) { if (mMuteCount == 0) { // Register for client death notification @@ -3079,22 +2909,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { if (mICallback != null) { mICallback.linkToDeath(this, 0); } - mDeathHandlers.add(this); + VolumeStreamState.this.mDeathHandlers.add(this); // If the stream is not yet muted by any client, set level to 0 - if (muteCount() == 0) { - Set set = mIndex.entrySet(); - Iterator i = set.iterator(); - while (i.hasNext()) { - Map.Entry entry = (Map.Entry)i.next(); - int device = ((Integer)entry.getKey()).intValue(); - setIndex(0, device, false /* lastAudible */); - } - sendMsg(mAudioHandler, - MSG_SET_ALL_VOLUMES, - SENDMSG_QUEUE, - 0, - 0, - VolumeStreamState.this, 0); + if (!VolumeStreamState.this.isMuted()) { + updateVolume = true; } } catch (RemoteException e) { // Client has died! @@ -3112,37 +2930,25 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mMuteCount--; if (mMuteCount == 0) { // Unregister from client death notification - mDeathHandlers.remove(this); + VolumeStreamState.this.mDeathHandlers.remove(this); // mICallback can be 0 if muted by AudioService if (mICallback != null) { mICallback.unlinkToDeath(this, 0); } - if (muteCount() == 0) { - // If the stream is not muted any more, restore its volume if - // ringer mode allows it - if (!isStreamAffectedByRingerMode(mStreamType) || - mRingerMode == AudioManager.RINGER_MODE_NORMAL) { - Set set = mIndex.entrySet(); - Iterator i = set.iterator(); - while (i.hasNext()) { - Map.Entry entry = (Map.Entry)i.next(); - int device = ((Integer)entry.getKey()).intValue(); - setIndex(getIndex(device, - true /* lastAudible */), - device, - false /* lastAudible */); - } - sendMsg(mAudioHandler, - MSG_SET_ALL_VOLUMES, - SENDMSG_QUEUE, - 0, - 0, - VolumeStreamState.this, 0); - } + if (!VolumeStreamState.this.isMuted()) { + updateVolume = true; } } } } + if (updateVolume) { + sendMsg(mAudioHandler, + MSG_SET_ALL_VOLUMES, + SENDMSG_QUEUE, + 0, + 0, + VolumeStreamState.this, 0); + } } public void binderDied() { @@ -3164,6 +2970,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { return count; } + private synchronized boolean isMuted() { + return muteCount() != 0; + } + // only called by mute() which is already synchronized private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) { VolumeDeathHandler handler; @@ -3196,14 +3006,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue()) + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", "); } - pw.print("\n Last audible: "); - set = mLastAudibleIndex.entrySet(); - i = set.iterator(); - while (i.hasNext()) { - Map.Entry entry = (Map.Entry)i.next(); - pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue()) - + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", "); - } } } @@ -3251,8 +3053,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, SENDMSG_QUEUE, - PERSIST_CURRENT|PERSIST_LAST_AUDIBLE, device, + 0, streamState, PERSIST_DELAY); @@ -3273,24 +3075,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } - private void persistVolume(VolumeStreamState streamState, - int persistType, - int device) { + private void persistVolume(VolumeStreamState streamState, int device) { if (mUseFixedVolume) { return; } - if ((persistType & PERSIST_CURRENT) != 0) { - System.putIntForUser(mContentResolver, - streamState.getSettingNameForDevice(false /* lastAudible */, device), - (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10, - UserHandle.USER_CURRENT); - } - if ((persistType & PERSIST_LAST_AUDIBLE) != 0) { - System.putIntForUser(mContentResolver, - streamState.getSettingNameForDevice(true /* lastAudible */, device), - (streamState.getIndex(device, true /* lastAudible */) + 5) / 10, - UserHandle.USER_CURRENT); - } + System.putIntForUser(mContentResolver, + streamState.getSettingNameForDevice(device), + (streamState.getIndex(device) + 5)/ 10, + UserHandle.USER_CURRENT); } private void persistRingerMode(int ringerMode) { @@ -3542,7 +3334,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { break; case MSG_PERSIST_VOLUME: - persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2); + persistVolume((VolumeStreamState) msg.obj, msg.arg1); break; case MSG_PERSIST_MASTER_VOLUME: @@ -3739,6 +3531,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */, (IRemoteVolumeObserver)msg.obj /* rvo */); break; + case MSG_RCC_NEW_PLAYBACK_STATE: + onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */, + (RccPlaybackState)msg.obj /* newState */); + break; case MSG_SET_RSX_CONNECTION_STATE: onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/); @@ -4912,6 +4708,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } }; + /** + * Synchronization on mCurrentRcLock always inside a block synchronized on mRCStack + */ private final Object mCurrentRcLock = new Object(); /** * The one remote control client which will receive a request for display information. @@ -4999,6 +4798,59 @@ public class AudioService extends IAudioService.Stub implements OnFinished { */ private boolean mHasRemotePlayback; + private static class RccPlaybackState { + public int mState; + public long mPositionMs; + public float mSpeed; + + public RccPlaybackState(int state, long positionMs, float speed) { + mState = state; + mPositionMs = positionMs; + mSpeed = speed; + } + + public void reset() { + mState = RemoteControlClient.PLAYSTATE_STOPPED; + mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID; + mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X; + } + + @Override + public String toString() { + return stateToString() + ", " + + ((mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) ? + "PLAYBACK_POSITION_INVALID ," : String.valueOf(mPositionMs)) + "ms ," + + mSpeed + "X"; + } + + private String stateToString() { + switch (mState) { + case RemoteControlClient.PLAYSTATE_NONE: + return "PLAYSTATE_NONE"; + case RemoteControlClient.PLAYSTATE_STOPPED: + return "PLAYSTATE_STOPPED"; + case RemoteControlClient.PLAYSTATE_PAUSED: + return "PLAYSTATE_PAUSED"; + case RemoteControlClient.PLAYSTATE_PLAYING: + return "PLAYSTATE_PLAYING"; + case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: + return "PLAYSTATE_FAST_FORWARDING"; + case RemoteControlClient.PLAYSTATE_REWINDING: + return "PLAYSTATE_REWINDING"; + case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: + return "PLAYSTATE_SKIPPING_FORWARDS"; + case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: + return "PLAYSTATE_SKIPPING_BACKWARDS"; + case RemoteControlClient.PLAYSTATE_BUFFERING: + return "PLAYSTATE_BUFFERING"; + case RemoteControlClient.PLAYSTATE_ERROR: + return "PLAYSTATE_ERROR"; + default: + return "[invalid playstate]"; + } + } + } + private static class RemoteControlStackEntry { public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED; /** @@ -5027,7 +4879,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public int mPlaybackVolumeMax; public int mPlaybackVolumeHandling; public int mPlaybackStream; - public int mPlaybackState; + public RccPlaybackState mPlaybackState; public IRemoteVolumeObserver mRemoteVolumeObs; public void resetPlaybackInfo() { @@ -5036,17 +4888,21 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; mPlaybackStream = AudioManager.STREAM_MUSIC; - mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED; + mPlaybackState.reset(); mRemoteVolumeObs = null; } - /** precondition: mediaIntent != null, eventReceiver != null */ + /** precondition: mediaIntent != null */ public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) { mMediaIntent = mediaIntent; mReceiverComponent = eventReceiver; mCallingUid = -1; mRcClient = null; mRccId = ++sLastRccId; + mPlaybackState = new RccPlaybackState( + RemoteControlClient.PLAYSTATE_STOPPED, + RemoteControlClient.PLAYBACK_POSITION_INVALID, + RemoteControlClient.PLAYBACK_SPEED_1X); resetPlaybackInfo(); } @@ -5126,6 +4982,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { " -- volMax: " + rcse.mPlaybackVolumeMax + " -- volObs: " + rcse.mRemoteVolumeObs); } + synchronized(mCurrentRcLock) { + pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen); + } } synchronized (mMainRemote) { pw.println("\nRemote Volume State:"); @@ -5209,6 +5068,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT); if ((null != receiverName) && !receiverName.isEmpty()) { ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName); + if (eventReceiver == null) { + // an invalid name was persisted + return; + } // construct a PendingIntent targeted to the restored component name // for the media button and register it Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); @@ -5224,7 +5087,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { * Helper function: * Set the new remote control receiver at the top of the RC focus stack. * Called synchronized on mAudioFocusLock, then mRCStack - * precondition: mediaIntent != null, target != null + * precondition: mediaIntent != null */ private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target) { // already at top of stack? @@ -5253,8 +5116,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mRCStack.push(rcse); // rcse is never null // post message to persist the default media button receiver - mAudioHandler.sendMessage( mAudioHandler.obtainMessage( - MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) ); + if (target != null) { + mAudioHandler.sendMessage( mAudioHandler.obtainMessage( + MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) ); + } } /** @@ -5554,7 +5419,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { /** * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c) - * precondition: mediaIntent != null, target != null + * precondition: mediaIntent != null */ public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) { Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent); @@ -5572,7 +5437,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent) * precondition: mediaIntent != null, eventReceiver != null */ - public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) + public void unregisterMediaButtonIntent(PendingIntent mediaIntent) { Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent); @@ -5954,6 +5819,29 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } + public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) { + sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_QUEUE, generationId /* arg1 */, + 0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */); + } + + public void onSetRemoteControlClientPlaybackPosition(int generationId, long timeMs) { + if(DEBUG_RC) Log.d(TAG, "onSetRemoteControlClientPlaybackPosition(genId=" + generationId + + ", timeMs=" + timeMs + ")"); + synchronized(mRCStack) { + synchronized(mCurrentRcLock) { + if ((mCurrentRcClient != null) && (mCurrentRcClientGen == generationId)) { + // tell the current client to seek to the requested location + try { + mCurrentRcClient.seekTo(generationId, timeMs); + } catch (RemoteException e) { + Log.e(TAG, "Current valid remote client is dead: "+e); + mCurrentRcClient = null; + } + } + } + } + } + public void setPlaybackInfoForRcc(int rccId, int what, int value) { sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE, rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */); @@ -6005,21 +5893,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { case RemoteControlClient.PLAYBACKINFO_USES_STREAM: rcse.mPlaybackStream = value; break; - case RemoteControlClient.PLAYBACKINFO_PLAYSTATE: - rcse.mPlaybackState = value; - synchronized (mMainRemote) { - if (rccId == mMainRemote.mRccId) { - mMainRemoteIsActive = isPlaystateActive(value); - postReevaluateRemote(); - } - } - // an RCC moving to a "playing" state should become the media button - // event receiver so it can be controlled, without requiring the - // app to re-register its receiver - if (isPlaystateActive(value)) { - postPromoteRcc(rccId); - } - break; default: Log.e(TAG, "unhandled key " + key + " for RCC " + rccId); break; @@ -6029,7 +5902,45 @@ public class AudioService extends IAudioService.Stub implements OnFinished { }//for } catch (ArrayIndexOutOfBoundsException e) { // not expected to happen, indicates improper concurrent modification - Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); + Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e); + } + } + } + + public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) { + sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE, + rccId /* arg1 */, state /* arg2 */, + new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */); + } + + public void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) { + if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state + + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")"); + synchronized(mRCStack) { + // iterating from top of stack as playback information changes are more likely + // on entries at the top of the remote control stack + try { + for (int index = mRCStack.size()-1; index >= 0; index--) { + final RemoteControlStackEntry rcse = mRCStack.elementAt(index); + if (rcse.mRccId == rccId) { + rcse.mPlaybackState = newState; + synchronized (mMainRemote) { + if (rccId == mMainRemote.mRccId) { + mMainRemoteIsActive = isPlaystateActive(state); + postReevaluateRemote(); + } + } + // an RCC moving to a "playing" state should become the media button + // event receiver so it can be controlled, without requiring the + // app to re-register its receiver + if (isPlaystateActive(state)) { + postPromoteRcc(rccId); + } + } + }//for + } catch (ArrayIndexOutOfBoundsException e) { + // not expected to happen, indicates improper concurrent modification + Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e); } } } @@ -6073,7 +5984,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { for (int index = mRCStack.size()-1; index >= 0; index--) { final RemoteControlStackEntry rcse = mRCStack.elementAt(index); if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) - && isPlaystateActive(rcse.mPlaybackState) + && isPlaystateActive(rcse.mPlaybackState.mState) && (rcse.mPlaybackStream == streamType)) { if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType + ", vol =" + rcse.mPlaybackVolume); @@ -6304,10 +6215,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED); } else { - s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], - false /*lastAudible*/); - s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], - true /*lastAudible*/); + s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM]); mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED); } @@ -6453,7 +6361,6 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private void enforceSafeMediaVolume() { VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC]; - boolean lastAudible = (streamState.muteCount() != 0); int devices = mSafeMediaVolumeDevices; int i = 0; @@ -6462,27 +6369,16 @@ public class AudioService extends IAudioService.Stub implements OnFinished { if ((device & devices) == 0) { continue; } - int index = streamState.getIndex(device, lastAudible); + int index = streamState.getIndex(device); if (index > mSafeMediaVolumeIndex) { - if (lastAudible) { - streamState.setLastAudibleIndex(mSafeMediaVolumeIndex, device); - sendMsg(mAudioHandler, - MSG_PERSIST_VOLUME, - SENDMSG_QUEUE, - PERSIST_LAST_AUDIBLE, - device, - streamState, - PERSIST_DELAY); - } else { - streamState.setIndex(mSafeMediaVolumeIndex, device, true); - sendMsg(mAudioHandler, - MSG_SET_DEVICE_VOLUME, - SENDMSG_QUEUE, - device, - 0, - streamState, - 0); - } + streamState.setIndex(mSafeMediaVolumeIndex, device); + sendMsg(mAudioHandler, + MSG_SET_DEVICE_VOLUME, + SENDMSG_QUEUE, + device, + 0, + streamState, + 0); } devices &= ~device; } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index efa8089..25aae8f 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -121,16 +121,11 @@ interface IAudioService { void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent); void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c); - oneway void unregisterMediaButtonIntent(in PendingIntent pi, in ComponentName c); + oneway void unregisterMediaButtonIntent(in PendingIntent pi); oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c); oneway void unregisterMediaButtonEventReceiverForCalls(); - int registerRemoteControlClient(in PendingIntent mediaIntent, - in IRemoteControlClient rcClient, in String callingPackageName); - oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent, - in IRemoteControlClient rcClient); - /** * Register an IRemoteControlDisplay. * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient @@ -157,8 +152,29 @@ interface IAudioService { * display doesn't need to receive artwork. */ oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h); + /** + * Request the user of a RemoteControlClient to seek to the given playback position. + * @param generationId the RemoteControlClient generation counter for which this request is + * issued. Requests for an older generation than current one will be ignored. + * @param timeMs the time in ms to seek to, must be positive. + */ + void setRemoteControlClientPlaybackPosition(int generationId, long timeMs); + + /** + * Do not use directly, use instead + * {@link android.media.AudioManager#registerRemoteControlClient(RemoteControlClient)} + */ + int registerRemoteControlClient(in PendingIntent mediaIntent, + in IRemoteControlClient rcClient, in String callingPackageName); + /** + * Do not use directly, use instead + * {@link android.media.AudioManager#unregisterRemoteControlClient(RemoteControlClient)} + */ + oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent, + in IRemoteControlClient rcClient); oneway void setPlaybackInfoForRcc(int rccId, int what, int value); + void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed); int getRemoteStreamMaxVolume(); int getRemoteStreamVolume(); oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo); diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl index 5600263..e4cee06 100644 --- a/media/java/android/media/IRemoteControlClient.aidl +++ b/media/java/android/media/IRemoteControlClient.aidl @@ -47,4 +47,5 @@ oneway interface IRemoteControlClient void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h); void unplugRemoteControlDisplay(IRemoteControlDisplay rcd); void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h); + void seekTo(int clientGeneration, long timeMs); }
\ No newline at end of file diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl index 204de3c..c70889c 100644 --- a/media/java/android/media/IRemoteControlDisplay.aidl +++ b/media/java/android/media/IRemoteControlDisplay.aidl @@ -40,9 +40,19 @@ oneway interface IRemoteControlDisplay void setCurrentClientId(int clientGeneration, in PendingIntent clientMediaIntent, boolean clearing); - void setPlaybackState(int generationId, int state, long stateChangeTimeMs); + void setPlaybackState(int generationId, int state, long stateChangeTimeMs, long currentPosMs, + float speed); - void setTransportControlFlags(int generationId, int transportControlFlags); + /** + * Sets the transport control flags and playback position capabilities of a client. + * @param generationId the current generation ID as known by this client + * @param transportControlFlags bitmask of the transport controls this client supports, see + * {@link RemoteControlClient#setTransportControlFlags(int)} + * @param posCapabilities a bit mask for playback position capabilities, see + * {@link RemoteControlClient#MEDIA_POSITION_READABLE} and + * {@link RemoteControlClient#MEDIA_POSITION_WRITABLE} + */ + void setTransportControlInfo(int generationId, int transportControlFlags, int posCapabilities); void setMetadata(int generationId, in Bundle metadata); diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 4561d3f..3cdf261 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -28,8 +28,8 @@ import android.os.Bundle; import android.util.Log; /** - * MediaDrm class can be used in conjunction with {@link android.media.MediaCrypto} - * to obtain licenses for decoding encrypted media data. + * MediaDrm can be used in conjunction with {@link android.media.MediaCrypto} + * to obtain keys for decrypting protected media data. * * Crypto schemes are assigned 16 byte UUIDs, * the method {@link #isCryptoSchemeSupported} can be used to query if a given @@ -131,11 +131,15 @@ public final class MediaDrm { void onEvent(MediaDrm md, byte[] sessionId, int event, int extra, byte[] data); } + public static final int MEDIA_DRM_EVENT_PROVISION_REQUIRED = 1; + public static final int MEDIA_DRM_EVENT_KEY_REQUIRED = 2; + public static final int MEDIA_DRM_EVENT_KEY_EXPIRED = 3; + public static final int MEDIA_DRM_EVENT_VENDOR_DEFINED = 4; + /* Do not change these values without updating their counterparts * in include/media/mediadrm.h! */ private static final int DRM_EVENT = 200; - private class EventHandler extends Handler { private MediaDrm mMediaDrm; @@ -197,68 +201,88 @@ public final class MediaDrm { public native byte[] openSession() throws MediaDrmException; /** - * Close a session on the MediaDrm object. + * Close a session on the MediaDrm object that was previously opened + * with {@link #openSession}. */ public native void closeSession(byte[] sessionId) throws MediaDrmException; - public static final int MEDIA_DRM_LICENSE_TYPE_STREAMING = 1; - public static final int MEDIA_DRM_LICENSE_TYPE_OFFLINE = 2; + public static final int MEDIA_DRM_KEY_TYPE_STREAMING = 1; + public static final int MEDIA_DRM_KEY_TYPE_OFFLINE = 2; - public final class LicenseRequest { - public LicenseRequest() {} + public final class KeyRequest { + public KeyRequest() {} public byte[] data; public String defaultUrl; }; /** - * A license request/response exchange occurs between the app and a License - * Server to obtain the keys required to decrypt the content. getLicenseRequest() - * is used to obtain an opaque license request byte array that is delivered to the - * license server. The opaque license request byte array is returned in - * LicenseReqeust.data. The recommended URL to deliver the license request to is - * returned in LicenseRequest.defaultUrl + * A key request/response exchange occurs between the app and a license + * server to obtain the keys to decrypt encrypted content. getKeyRequest() + * is used to obtain an opaque key request byte array that is delivered to the + * license server. The opaque key request byte array is returned in + * KeyRequest.data. The recommended URL to deliver the key request to is + * returned in KeyRequest.defaultUrl. + * + * After the app has received the key request response from the server, + * it should deliver to the response to the DRM engine plugin using the method + * {@link #provideKeyResponse}. * * @param sessonId the session ID for the drm session * @param init container-specific data, its meaning is interpreted based on the * mime type provided in the mimeType parameter. It could contain, for example, * the content ID, key ID or other data obtained from the content metadata that is - * required in generating the license request. + * required in generating the key request. * @param mimeType identifies the mime type of the content - * @param licenseType specifes if the license is for streaming or offline content - * @param optionalParameters are included in the license server request message to + * @param keyType specifes if the request is for streaming or offline content + * @param optionalParameters are included in the key request message to * allow a client application to provide additional message parameters to the server. */ - public native LicenseRequest getLicenseRequest( byte[] sessionId, byte[] init, - String mimeType, int licenseType, - HashMap<String, String> optionalParameters ) + public native KeyRequest getKeyRequest(byte[] sessionId, byte[] init, + String mimeType, int keyType, + HashMap<String, String> optionalParameters) throws MediaDrmException; /** - * After a license response is received by the app, it is provided to the DRM plugin - * using provideLicenseResponse. + * A key response is received from the license server by the app, then it is + * provided to the DRM engine plugin using provideKeyResponse. The byte array + * returned is a keySetId that can be used to later restore the keys to a new + * session with the method {@link restoreKeys}, enabling offline key use. * * @param sessionId the session ID for the DRM session * @param response the byte array response from the server */ - public native void provideLicenseResponse( byte[] sessionId, byte[] response ) + public native byte[] provideKeyResponse(byte[] sessionId, byte[] response) throws MediaDrmException; /** - * Remove the keys associated with a license for a session + * Restore persisted offline keys into a new session. keySetId identifies the + * keys to load, obtained from a prior call to {@link provideKeyResponse}. + * * @param sessionId the session ID for the DRM session + * @param keySetId identifies the saved key set to restore */ - public native void removeLicense( byte[] sessionId ) throws MediaDrmException; + public native void restoreKeys(byte[] sessionId, byte[] keySetId) + throws MediaDrmException; /** - * Request an informative description of the license for the session. The status is + * Remove the persisted keys associated with an offline license. Keys are persisted + * when {@link provideKeyResponse} is called with keys obtained from the method + * {@link getKeyRequest} using keyType = MEDIA_DRM_KEY_TYPE_OFFLINE. + * + * @param keySetId identifies the saved key set to remove + */ + public native void removeKeys(byte[] keySetId) throws MediaDrmException; + + /** + * Request an informative description of the key status for the session. The status is * in the form of {name, value} pairs. Since DRM license policies vary by vendor, * the specific status field names are determined by each DRM vendor. Refer to your * DRM provider documentation for definitions of the field names for a particular - * DrmEngine. + * DRM engine plugin. * * @param sessionId the session ID for the DRM session */ - public native HashMap<String, String> queryLicenseStatus( byte[] sessionId ) + public native HashMap<String, String> queryKeyStatus(byte[] sessionId) throws MediaDrmException; public final class ProvisionRequest { @@ -269,22 +293,23 @@ public final class MediaDrm { /** * A provision request/response exchange occurs between the app and a provisioning - * server to retrieve a device certificate. getProvisionRequest is used to obtain - * an opaque license request byte array that is delivered to the provisioning server. - * The opaque provision request byte array is returned in ProvisionRequest.data - * The recommended URL to deliver the license request to is returned in - * ProvisionRequest.defaultUrl. + * server to retrieve a device certificate. If provisionining is required, the + * MEDIA_DRM_EVENT_PROVISION_REQUIRED event will be sent to the event handler. + * getProvisionRequest is used to obtain the opaque provision request byte array that + * should be delivered to the provisioning server. The provision request byte array + * is returned in ProvisionRequest.data. The recommended URL to deliver the provision + * request to is returned in ProvisionRequest.defaultUrl. */ public native ProvisionRequest getProvisionRequest() throws MediaDrmException; /** * After a provision response is received by the app, it is provided to the DRM - * plugin using this method. + * engine plugin using this method. * * @param response the opaque provisioning response byte array to provide to the - * DrmEngine. + * DRM engine plugin. */ - public native void provideProvisionResponse( byte[] response ) + public native void provideProvisionResponse(byte[] response) throws MediaDrmException; /** @@ -314,38 +339,140 @@ public final class MediaDrm { * * @param ssRelease the server response indicating which secure stops to release */ - public native void releaseSecureStops( byte[] ssRelease ) + public native void releaseSecureStops(byte[] ssRelease) throws MediaDrmException; /** - * Read a Drm plugin property value, given the property name string. There are several - * forms of property access functions, depending on the data type returned. + * Read a DRM engine plugin property value, given the property name string. There are + * several forms of property access functions, depending on the data type returned. * * Standard fields names are: - * vendor String - identifies the maker of the plugin - * version String - identifies the version of the plugin - * description String - describes the plugin + * vendor String - identifies the maker of the DRM engine plugin + * version String - identifies the version of the DRM engine plugin + * description String - describes the DRM engine plugin * deviceUniqueId byte[] - The device unique identifier is established during device - * provisioning and provides a means of uniquely identifying - * each device + * provisioning and provides a means of uniquely identifying + * each device + * algorithms String - a comma-separate list of cipher and mac algorithms supported + * by CryptoSession. The list may be empty if the DRM engine + * plugin does not support CryptoSession operations. */ - public native String getPropertyString( String propertyName ) + public native String getPropertyString(String propertyName) throws MediaDrmException; - public native byte[] getPropertyByteArray( String propertyName ) + public native byte[] getPropertyByteArray(String propertyName) throws MediaDrmException; /** - * Write a Drm plugin property value. There are several forms of property setting - * functions, depending on the data type being set. + * Write a DRM engine plugin property value. There are several forms of + * property setting functions, depending on the data type being set. */ - public native void setPropertyString( String propertyName, String value ) + public native void setPropertyString(String propertyName, String value) throws MediaDrmException; - public native void setPropertyByteArray( String propertyName, byte[] value ) + public native void setPropertyByteArray(String propertyName, byte[] value) throws MediaDrmException; + /** + * In addition to supporting decryption of DASH Common Encrypted Media, the + * MediaDrm APIs provide the ability to securely deliver session keys from + * an operator's session key server to a client device, based on the factory-installed + * root of trust, and provide the ability to do encrypt, decrypt, sign and verify + * with the session key on arbitrary user data. + * + * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods + * based on the established session keys. These keys are exchanged using the + * getKeyRequest/provideKeyResponse methods. + * + * Applications of this capability could include securing various types of + * purchased or private content, such as applications, books and other media, + * photos or media delivery protocols. + * + * Operators can create session key servers that are functionally similar to a + * license key server, except that instead of receiving license key requests and + * providing encrypted content keys which are used specifically to decrypt A/V media + * content, the session key server receives session key requests and provides + * encrypted session keys which can be used for general purpose crypto operations. + */ + + private static final native void setCipherAlgorithmNative(MediaDrm drm, byte[] sessionId, + String algorithm); + + private static final native void setMacAlgorithmNative(MediaDrm drm, byte[] sessionId, + String algorithm); + + private static final native byte[] encryptNative(MediaDrm drm, byte[] sessionId, + byte[] keyId, byte[] input, byte[] iv); + + private static final native byte[] decryptNative(MediaDrm drm, byte[] sessionId, + byte[] keyId, byte[] input, byte[] iv); + + private static final native byte[] signNative(MediaDrm drm, byte[] sessionId, + byte[] keyId, byte[] message); + + private static final native boolean verifyNative(MediaDrm drm, byte[] sessionId, + byte[] keyId, byte[] message, + byte[] signature); + + public final class CryptoSession { + private MediaDrm mDrm; + private byte[] mSessionId; + + /** + * Construct a CryptoSession which can be used to encrypt, decrypt, + * sign and verify messages or data using the session keys established + * for the session using methods {@link getKeyRequest} and + * {@link provideKeyResponse} using a session key server. + * + * @param sessionId the session ID for the session containing keys + * to be used for encrypt, decrypt, sign and/or verify + * + * @param cipherAlgorithm the algorithm to use for encryption and + * decryption ciphers. The algorithm string conforms to JCA Standard + * Names for Cipher Transforms and is case insensitive. For example + * "AES/CBC/PKCS5Padding". + * + * @param macAlgorithm the algorithm to use for sign and verify + * The algorithm string conforms to JCA Standard Names for Mac + * Algorithms and is case insensitive. For example "HmacSHA256". + * + * The list of supported algorithms for a DRM engine plugin can be obtained + * using the method {@link getPropertyString("algorithms")} + */ + + public CryptoSession(MediaDrm drm, byte[] sessionId, + String cipherAlgorithm, String macAlgorithm) + throws MediaDrmException { + mSessionId = sessionId; + mDrm = drm; + setCipherAlgorithmNative(drm, sessionId, cipherAlgorithm); + setMacAlgorithmNative(drm, sessionId, macAlgorithm); + } + + public byte[] encrypt(byte[] keyid, byte[] input, byte[] iv) { + return encryptNative(mDrm, mSessionId, keyid, input, iv); + } + + public byte[] decrypt(byte[] keyid, byte[] input, byte[] iv) { + return decryptNative(mDrm, mSessionId, keyid, input, iv); + } + + public byte[] sign(byte[] keyid, byte[] message) { + return signNative(mDrm, mSessionId, keyid, message); + } + public boolean verify(byte[] keyid, byte[] message, byte[] signature) { + return verifyNative(mDrm, mSessionId, keyid, message, signature); + } + }; + + public CryptoSession getCryptoSession(byte[] sessionId, + String cipherAlgorithm, + String macAlgorithm) + throws MediaDrmException { + return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm); + } + @Override protected void finalize() { native_finalize(); diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index cc59d02..376bb2d 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -367,7 +367,7 @@ public class MediaMetadataRetriever * counterparts in include/media/mediametadataretriever.h! */ /** - * The metadata key to retrieve the numberic string describing the + * The metadata key to retrieve the numeric string describing the * order of the audio data source on its original recording. */ public static final int METADATA_KEY_CD_TRACK_NUMBER = 0; diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 11f4180..85a32ca 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -328,8 +328,8 @@ import java.lang.ref.WeakReference; * the state. Calling this method in an invalid state transfers the * object to the <em>Error</em> state. </p></td></tr> * <tr><td>pause </p></td> - * <td>{Started, Paused}</p></td> - * <td>{Idle, Initialized, Prepared, Stopped, PlaybackCompleted, Error}</p></td> + * <td>{Started, Paused, PlaybackCompleted}</p></td> + * <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td> * <td>Successful invoke of this method in a valid state transfers the * object to the <em>Paused</em> state. Calling this method in an * invalid state transfers the object to the <em>Error</em> state.</p></td></tr> diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index 9a0ecdf..e076ef0 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -172,6 +172,17 @@ public class RemoteControlClient */ public final static int PLAYBACKINFO_INVALID_VALUE = Integer.MIN_VALUE; + /** + * @hide + * An unknown or invalid playback position value. + */ + public final static long PLAYBACK_POSITION_INVALID = -1; + /** + * @hide + * The default playback speed, 1x. + */ + public final static float PLAYBACK_SPEED_1X = 1.0f; + //========================================== // Public keys for playback information /** @@ -208,15 +219,7 @@ public class RemoteControlClient public final static int PLAYBACKINFO_USES_STREAM = 5; //========================================== - // Private keys for playback information - /** - * @hide - * Used internally to relay playback state (set by the application with - * {@link #setPlaybackState(int)}) to AudioService - */ - public final static int PLAYBACKINFO_PLAYSTATE = 255; - - + // Public flags for the supported transport control capabililities /** * Flag indicating a RemoteControlClient makes use of the "previous" media key. * @@ -273,6 +276,18 @@ public class RemoteControlClient * @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT */ public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7; + /** + * @hide + * TODO un-hide and add in javadoc of setTransportControlFlags(int) + * Flag indicating a RemoteControlClient can receive changes in the media playback position + * through the {@link #OnPlaybackPositionUpdateListener} interface. This flag must be set + * in order for components that display the RemoteControlClient information, to display and + * let the user control media playback position. + * @see #setTransportControlFlags(int) + * @see #setPlaybackPositionProvider(PlaybackPositionProvider) + * @see #setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener) + */ + public final static int FLAG_KEY_MEDIA_POSITION_UPDATE = 1 << 8; /** * @hide @@ -588,17 +603,49 @@ public class RemoteControlClient * {@link #PLAYSTATE_ERROR}. */ public void setPlaybackState(int state) { + setPlaybackState(state, PLAYBACK_POSITION_INVALID, PLAYBACK_SPEED_1X); + } + + /** + * @hide + * TODO un-hide + * Sets the current playback state and the matching media position for the current playback + * speed. + * @param state The current playback state, one of the following values: + * {@link #PLAYSTATE_STOPPED}, + * {@link #PLAYSTATE_PAUSED}, + * {@link #PLAYSTATE_PLAYING}, + * {@link #PLAYSTATE_FAST_FORWARDING}, + * {@link #PLAYSTATE_REWINDING}, + * {@link #PLAYSTATE_SKIPPING_FORWARDS}, + * {@link #PLAYSTATE_SKIPPING_BACKWARDS}, + * {@link #PLAYSTATE_BUFFERING}, + * {@link #PLAYSTATE_ERROR}. + * @param timeInMs a 0 or positive value for the current media position expressed in ms + * (same unit as for when sending the media duration, if applicable, with + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} in the + * {@link RemoteControlClient.MetadataEditor}). Negative values imply that position is not + * known (e.g. listening to a live stream of a radio) or not applicable (e.g. when state + * is {@link #PLAYSTATE_BUFFERING} and nothing had played yet). + * @param playbackSpeed a value expressed as a ratio of 1x playback: 1.0f is normal playback, + * 2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is + * playing (e.g. when state is {@link #PLAYSTATE_ERROR}). + */ + public void setPlaybackState(int state, long timeInMs, float playbackSpeed) { synchronized(mCacheLock) { - if (mPlaybackState != state) { + if ((mPlaybackState != state) || (mPlaybackPositionMs != timeInMs) + || (mPlaybackSpeed != playbackSpeed)) { // store locally mPlaybackState = state; + mPlaybackPositionMs = timeInMs; + mPlaybackSpeed = playbackSpeed; // keep track of when the state change occurred mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime(); // send to remote control display if conditions are met sendPlaybackState_syncCacheLock(); // update AudioService - sendAudioServiceNewPlaybackInfo_syncCacheLock(PLAYBACKINFO_PLAYSTATE, state); + sendAudioServiceNewPlaybackState_syncCacheLock(); } } } @@ -621,10 +668,112 @@ public class RemoteControlClient mTransportControlFlags = transportControlFlags; // send to remote control display if conditions are met - sendTransportControlFlags_syncCacheLock(); + sendTransportControlInfo_syncCacheLock(); + } + } + + /** + * @hide + * TODO un-hide + * Interface definition for a callback to be invoked when the media playback position is + * requested to be updated. + * @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE + */ + public interface OnPlaybackPositionUpdateListener { + /** + * Called on the implementer to notify it that the playback head should be set at the given + * position. If the position can be changed from its current value, the implementor of + * the interface should also update the playback position using + * {@link RemoteControlClient#setPlaybackState(int, long, int)} to reflect the actual new + * position being used, regardless of whether it differs from the requested position. + * @param newPositionMs the new requested position in the current media, expressed in ms. + */ + void onPlaybackPositionUpdate(long newPositionMs); + } + + /** + * @hide + * TODO un-hide + * Interface definition for a callback to be invoked when the media playback position is + * queried. + * @see RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE + */ + public interface PlaybackPositionProvider { + /** + * Called on the implementer of the interface to query the current playback position. + * @return a negative value if the current playback position (or the last valid playback + * position) is not known, or a zero or positive value expressed in ms indicating the + * current position, or the last valid known position. + */ + long getPlaybackPosition(); + } + + /** + * @hide + * TODO un-hide + * Sets the listener to be called whenever the media playback position is requested + * to be updated. + * Notifications will be received in the same thread as the one in which RemoteControlClient + * was created. + * @param l + */ + public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) { + synchronized(mCacheLock) { + int oldCapa = mPlaybackPositionCapabilities; + if (l != null) { + mPlaybackPositionCapabilities |= MEDIA_POSITION_WRITABLE; + } else { + mPlaybackPositionCapabilities &= ~MEDIA_POSITION_WRITABLE; + } + mPositionUpdateListener = l; + if (oldCapa != mPlaybackPositionCapabilities) { + // tell RCDs that this RCC's playback position capabilities have changed + sendTransportControlInfo_syncCacheLock(); + } + } + } + + /** + * @hide + * TODO un-hide + * Sets the listener to be called whenever the media current playback position is needed. + * Queries will be received in the same thread as the one in which RemoteControlClient + * was created. + * @param l + */ + public void setPlaybackPositionProvider(PlaybackPositionProvider l) { + synchronized(mCacheLock) { + int oldCapa = mPlaybackPositionCapabilities; + if (l != null) { + mPlaybackPositionCapabilities |= MEDIA_POSITION_READABLE; + } else { + mPlaybackPositionCapabilities &= ~MEDIA_POSITION_READABLE; + } + mPositionProvider = l; + if (oldCapa != mPlaybackPositionCapabilities) { + // tell RCDs that this RCC's playback position capabilities have changed + sendTransportControlInfo_syncCacheLock(); + } } } + /** + * @hide + * Flag to reflect that the application controlling this RemoteControlClient sends playback + * position updates. The playback position being "readable" is considered from the application's + * point of view. + */ + public static int MEDIA_POSITION_READABLE = 1 << 0; + /** + * @hide + * Flag to reflect that the application controlling this RemoteControlClient can receive + * playback position updates. The playback position being "writable" + * is considered from the application's point of view. + */ + public static int MEDIA_POSITION_WRITABLE = 1 << 1; + + private int mPlaybackPositionCapabilities = 0; + /** @hide */ public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE; /** @hide */ @@ -756,6 +905,14 @@ public class RemoteControlClient */ private long mPlaybackStateChangeTimeMs = 0; /** + * Last playback position in ms reported by the user + */ + private long mPlaybackPositionMs = PLAYBACK_POSITION_INVALID; + /** + * Last playback speed reported by the user + */ + private float mPlaybackSpeed = PLAYBACK_SPEED_1X; + /** * Cache for the artwork bitmap. * Access synchronized on mCacheLock * Artwork and metadata are not kept in one Bundle because the bitmap sometimes needs to be @@ -774,9 +931,17 @@ public class RemoteControlClient * This is re-initialized in apply() and so cannot be final. */ private Bundle mMetadata = new Bundle(); - /** - * The current remote control client generation ID across the system + * Listener registered by user of RemoteControlClient to receive requests for playback position + * update requests. + */ + private OnPlaybackPositionUpdateListener mPositionUpdateListener; + /** + * Provider registered by user of RemoteControlClient to provide the current playback position. + */ + private PlaybackPositionProvider mPositionProvider; + /** + * The current remote control client generation ID across the system, as known by this object */ private int mCurrentClientGenId = -1; /** @@ -789,7 +954,8 @@ public class RemoteControlClient /** * The media button intent description associated with this remote control client - * (can / should include target component for intent handling) + * (can / should include target component for intent handling, used when persisting media + * button event receiver across reboots). */ private final PendingIntent mRcMediaIntent; @@ -836,14 +1002,14 @@ public class RemoteControlClient */ private final IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() { - public void onInformationRequested(int clientGeneration, int infoFlags) { + public void onInformationRequested(int generationId, int infoFlags) { // only post messages, we can't block here if (mEventHandler != null) { // signal new client mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN); mEventHandler.dispatchMessage( mEventHandler.obtainMessage(MSG_NEW_INTERNAL_CLIENT_GEN, - /*arg1*/ clientGeneration, /*arg2, ignored*/ 0)); + /*arg1*/ generationId, /*arg2, ignored*/ 0)); // send the information mEventHandler.removeMessages(MSG_REQUEST_PLAYBACK_STATE); mEventHandler.removeMessages(MSG_REQUEST_METADATA); @@ -890,6 +1056,16 @@ public class RemoteControlClient MSG_UPDATE_DISPLAY_ARTWORK_SIZE, w, h, rcd)); } } + + public void seekTo(int generationId, long timeMs) { + // only post messages, we can't block here + if (mEventHandler != null) { + mEventHandler.removeMessages(MSG_SEEK_TO); + mEventHandler.dispatchMessage(mEventHandler.obtainMessage( + MSG_SEEK_TO, generationId /* arg1 */, 0 /* arg2, ignored */, + new Long(timeMs))); + } + } }; /** @@ -930,6 +1106,7 @@ public class RemoteControlClient private final static int MSG_PLUG_DISPLAY = 7; private final static int MSG_UNPLUG_DISPLAY = 8; private final static int MSG_UPDATE_DISPLAY_ARTWORK_SIZE = 9; + private final static int MSG_SEEK_TO = 10; private class EventHandler extends Handler { public EventHandler(RemoteControlClient rcc, Looper looper) { @@ -951,7 +1128,7 @@ public class RemoteControlClient break; case MSG_REQUEST_TRANSPORTCONTROL: synchronized (mCacheLock) { - sendTransportControlFlags_syncCacheLock(); + sendTransportControlInfo_syncCacheLock(); } break; case MSG_REQUEST_ARTWORK: @@ -974,6 +1151,8 @@ public class RemoteControlClient case MSG_UPDATE_DISPLAY_ARTWORK_SIZE: onUpdateDisplayArtworkSize((IRemoteControlDisplay)msg.obj, msg.arg1, msg.arg2); break; + case MSG_SEEK_TO: + onSeekTo(msg.arg1, ((Long)msg.obj).longValue()); default: Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler"); } @@ -990,7 +1169,8 @@ public class RemoteControlClient final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); try { di.mRcDisplay.setPlaybackState(mInternalClientGenId, - mPlaybackState, mPlaybackStateChangeTimeMs); + mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs, + mPlaybackSpeed); } catch (RemoteException e) { Log.e(TAG, "Error in setPlaybackState(), dead display " + di.mRcDisplay, e); displayIterator.remove(); @@ -1014,14 +1194,14 @@ public class RemoteControlClient } } - private void sendTransportControlFlags_syncCacheLock() { + private void sendTransportControlInfo_syncCacheLock() { if (mCurrentClientGenId == mInternalClientGenId) { final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator(); while (displayIterator.hasNext()) { final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next(); try { - di.mRcDisplay.setTransportControlFlags(mInternalClientGenId, - mTransportControlFlags); + di.mRcDisplay.setTransportControlInfo(mInternalClientGenId, + mTransportControlFlags, mPlaybackPositionCapabilities); } catch (RemoteException e) { Log.e(TAG, "Error in setTransportControlFlags(), dead display " + di.mRcDisplay, e); @@ -1109,7 +1289,20 @@ public class RemoteControlClient try { service.setPlaybackInfoForRcc(mRcseId, what, value); } catch (RemoteException e) { - Log.e(TAG, "Dead object in sendAudioServiceNewPlaybackInfo_syncCacheLock", e); + Log.e(TAG, "Dead object in setPlaybackInfoForRcc", e); + } + } + + private void sendAudioServiceNewPlaybackState_syncCacheLock() { + if (mRcseId == RCSE_ID_UNREGISTERED) { + return; + } + IAudioService service = getService(); + try { + service.setPlaybackStateForRcc(mRcseId, + mPlaybackState, mPlaybackPositionMs, mPlaybackSpeed); + } catch (RemoteException e) { + Log.e(TAG, "Dead object in setPlaybackStateForRcc", e); } } @@ -1190,6 +1383,14 @@ public class RemoteControlClient } } + private void onSeekTo(int generationId, long timeMs) { + synchronized (mCacheLock) { + if ((mCurrentClientGenId == generationId) && (mPositionUpdateListener != null)) { + mPositionUpdateListener.onPlaybackPositionUpdate(timeMs); + } + } + } + //=========================================================== // Internal utilities diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index ea12803..632334b 100644 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -96,7 +96,8 @@ public class MtpDatabase { Files.FileColumns.FORMAT, // 2 Files.FileColumns.PARENT, // 3 Files.FileColumns.DATA, // 4 - Files.FileColumns.DATE_MODIFIED, // 5 + Files.FileColumns.DATE_ADDED, // 5 + Files.FileColumns.DATE_MODIFIED, // 6 }; private static final String ID_WHERE = Files.FileColumns._ID + "=?"; private static final String PATH_WHERE = Files.FileColumns.DATA + "=?"; @@ -840,7 +841,7 @@ public class MtpDatabase { } private boolean getObjectInfo(int handle, int[] outStorageFormatParent, - char[] outName, long[] outModified) { + char[] outName, long[] outCreatedModified) { Cursor c = null; try { c = mMediaProvider.query(mPackageName, mObjectsUri, OBJECT_INFO_PROJECTION, @@ -861,7 +862,12 @@ public class MtpDatabase { path.getChars(start, end, outName, 0); outName[end - start] = 0; - outModified[0] = c.getLong(5); + outCreatedModified[0] = c.getLong(5); + outCreatedModified[1] = c.getLong(6); + // use modification date as creation date if date added is not set + if (outCreatedModified[0] == 0) { + outCreatedModified[0] = outCreatedModified[1]; + } return true; } } catch (RemoteException e) { diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index 9938f76..1618edf 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -76,7 +76,7 @@ struct EntryFields { struct fields_t { jfieldID context; - RequestFields licenseRequest; + RequestFields keyRequest; RequestFields provisionRequest; ArrayListFields arraylist; HashmapFields hashmap; @@ -204,6 +204,7 @@ static String8 JStringToString8(JNIEnv *env, jstring const &jstr) { } return result; } + /* import java.util.HashMap; import java.util.Set; @@ -329,9 +330,9 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { FIND_CLASS(clazz, "android/media/MediaDrm"); GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "I"); - FIND_CLASS(clazz, "android/media/MediaDrm$LicenseRequest"); - GET_FIELD_ID(gFields.licenseRequest.data, clazz, "data", "[B"); - GET_FIELD_ID(gFields.licenseRequest.defaultUrl, clazz, "defaultUrl", "Ljava/lang/String;"); + FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); + GET_FIELD_ID(gFields.keyRequest.data, clazz, "data", "[B"); + GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "defaultUrl", "Ljava/lang/String;"); FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest"); GET_FIELD_ID(gFields.provisionRequest.data, clazz, "data", "[B"); @@ -451,9 +452,9 @@ static void android_media_MediaDrm_closeSession( throwExceptionAsNecessary(env, err, "Failed to close session"); } -static jobject android_media_MediaDrm_getLicenseRequest( +static jobject android_media_MediaDrm_getKeyRequest( JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jinitData, - jstring jmimeType, jint jlicenseType, jobject joptParams) { + jstring jmimeType, jint jkeyType, jobject joptParams) { sp<IDrm> drm = GetDrm(env, thiz); if (!CheckSession(env, drm, jsessionId)) { @@ -472,7 +473,7 @@ static jobject android_media_MediaDrm_getLicenseRequest( mimeType = JStringToString8(env, jmimeType); } - DrmPlugin::LicenseType licenseType = (DrmPlugin::LicenseType)jlicenseType; + DrmPlugin::KeyType keyType = (DrmPlugin::KeyType)jkeyType; KeyedVector<String8, String8> optParams; if (joptParams != NULL) { @@ -482,68 +483,94 @@ static jobject android_media_MediaDrm_getLicenseRequest( Vector<uint8_t> request; String8 defaultUrl; - status_t err = drm->getLicenseRequest(sessionId, initData, mimeType, - licenseType, optParams, request, defaultUrl); + status_t err = drm->getKeyRequest(sessionId, initData, mimeType, + keyType, optParams, request, defaultUrl); - if (throwExceptionAsNecessary(env, err, "Failed to get license request")) { + if (throwExceptionAsNecessary(env, err, "Failed to get key request")) { return NULL; } // Fill out return obj jclass clazz; - FIND_CLASS(clazz, "android/media/MediaDrm$LicenseRequest"); + FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); - jobject licenseObj = NULL; + jobject keyObj = NULL; if (clazz) { - licenseObj = env->AllocObject(clazz); + keyObj = env->AllocObject(clazz); jbyteArray jrequest = VectorToJByteArray(env, request); - env->SetObjectField(licenseObj, gFields.licenseRequest.data, jrequest); + env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest); jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string()); - env->SetObjectField(licenseObj, gFields.licenseRequest.defaultUrl, jdefaultUrl); + env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl); } - return licenseObj; + return keyObj; } -static void android_media_MediaDrm_provideLicenseResponse( +static jbyteArray android_media_MediaDrm_provideKeyResponse( JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jresponse) { sp<IDrm> drm = GetDrm(env, thiz); if (!CheckSession(env, drm, jsessionId)) { - return; + return NULL; } Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); if (jresponse == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; + return NULL; } Vector<uint8_t> response(JByteArrayToVector(env, jresponse)); + Vector<uint8_t> keySetId; - status_t err = drm->provideLicenseResponse(sessionId, response); + status_t err = drm->provideKeyResponse(sessionId, response, keySetId); - throwExceptionAsNecessary(env, err, "Failed to handle license response"); + throwExceptionAsNecessary(env, err, "Failed to handle key response"); + return VectorToJByteArray(env, keySetId); } -static void android_media_MediaDrm_removeLicense( - JNIEnv *env, jobject thiz, jbyteArray jsessionId) { +static void android_media_MediaDrm_removeKeys( + JNIEnv *env, jobject thiz, jbyteArray jkeysetId) { + sp<IDrm> drm = GetDrm(env, thiz); + + if (jkeysetId == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId)); + + status_t err = drm->removeKeys(keySetId); + + throwExceptionAsNecessary(env, err, "Failed to remove keys"); +} + +static void android_media_MediaDrm_restoreKeys( + JNIEnv *env, jobject thiz, jbyteArray jsessionId, + jbyteArray jkeysetId) { + sp<IDrm> drm = GetDrm(env, thiz); if (!CheckSession(env, drm, jsessionId)) { return; } + if (jkeysetId == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); + Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId)); - status_t err = drm->removeLicense(sessionId); + status_t err = drm->restoreKeys(sessionId, keySetId); - throwExceptionAsNecessary(env, err, "Failed to remove license"); + throwExceptionAsNecessary(env, err, "Failed to restore keys"); } -static jobject android_media_MediaDrm_queryLicenseStatus( +static jobject android_media_MediaDrm_queryKeyStatus( JNIEnv *env, jobject thiz, jbyteArray jsessionId) { sp<IDrm> drm = GetDrm(env, thiz); @@ -554,9 +581,9 @@ static jobject android_media_MediaDrm_queryLicenseStatus( KeyedVector<String8, String8> infoMap; - status_t err = drm->queryLicenseStatus(sessionId, infoMap); + status_t err = drm->queryKeyStatus(sessionId, infoMap); - if (throwExceptionAsNecessary(env, err, "Failed to query license")) { + if (throwExceptionAsNecessary(env, err, "Failed to query key status")) { return NULL; } @@ -752,6 +779,162 @@ static void android_media_MediaDrm_setPropertyByteArray( throwExceptionAsNecessary(env, err, "Failed to set property"); } +static void android_media_MediaDrm_setCipherAlgorithmNative( + JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, + jstring jalgorithm) { + + sp<IDrm> drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return; + } + + if (jalgorithm == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); + String8 algorithm = JStringToString8(env, jalgorithm); + + status_t err = drm->setCipherAlgorithm(sessionId, algorithm); + + throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm"); +} + +static void android_media_MediaDrm_setMacAlgorithmNative( + JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, + jstring jalgorithm) { + + sp<IDrm> drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return; + } + + if (jalgorithm == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); + String8 algorithm = JStringToString8(env, jalgorithm); + + status_t err = drm->setMacAlgorithm(sessionId, algorithm); + + throwExceptionAsNecessary(env, err, "Failed to set mac algorithm"); +} + + +static jbyteArray android_media_MediaDrm_encryptNative( + JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, + jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) { + + sp<IDrm> drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return NULL; + } + + if (jkeyId == NULL || jinput == NULL || jiv == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return NULL; + } + + Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); + Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); + Vector<uint8_t> input(JByteArrayToVector(env, jinput)); + Vector<uint8_t> iv(JByteArrayToVector(env, jiv)); + Vector<uint8_t> output; + + status_t err = drm->encrypt(sessionId, keyId, input, iv, output); + + throwExceptionAsNecessary(env, err, "Failed to encrypt"); + + return VectorToJByteArray(env, output); +} + +static jbyteArray android_media_MediaDrm_decryptNative( + JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, + jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) { + + sp<IDrm> drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return NULL; + } + + if (jkeyId == NULL || jinput == NULL || jiv == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return NULL; + } + + Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); + Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); + Vector<uint8_t> input(JByteArrayToVector(env, jinput)); + Vector<uint8_t> iv(JByteArrayToVector(env, jiv)); + Vector<uint8_t> output; + + status_t err = drm->decrypt(sessionId, keyId, input, iv, output); + throwExceptionAsNecessary(env, err, "Failed to decrypt"); + + return VectorToJByteArray(env, output); +} + +static jbyteArray android_media_MediaDrm_signNative( + JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, + jbyteArray jkeyId, jbyteArray jmessage) { + + sp<IDrm> drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return NULL; + } + + if (jkeyId == NULL || jmessage == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return NULL; + } + + Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); + Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); + Vector<uint8_t> message(JByteArrayToVector(env, jmessage)); + Vector<uint8_t> signature; + + status_t err = drm->sign(sessionId, keyId, message, signature); + + throwExceptionAsNecessary(env, err, "Failed to sign"); + + return VectorToJByteArray(env, signature); +} + +static jboolean android_media_MediaDrm_verifyNative( + JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, + jbyteArray jkeyId, jbyteArray jmessage, jbyteArray jsignature) { + + sp<IDrm> drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return false; + } + + if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return false; + } + + Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); + Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); + Vector<uint8_t> message(JByteArrayToVector(env, jmessage)); + Vector<uint8_t> signature(JByteArrayToVector(env, jsignature)); + bool match; + + status_t err = drm->verify(sessionId, keyId, message, signature, match); + + throwExceptionAsNecessary(env, err, "Failed to verify"); + return match; +} + static JNINativeMethod gMethods[] = { { "release", "()V", (void *)android_media_MediaDrm_release }, @@ -772,18 +955,21 @@ static JNINativeMethod gMethods[] = { { "closeSession", "([B)V", (void *)android_media_MediaDrm_closeSession }, - { "getLicenseRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)" - "Landroid/media/MediaDrm$LicenseRequest;", - (void *)android_media_MediaDrm_getLicenseRequest }, + { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)" + "Landroid/media/MediaDrm$KeyRequest;", + (void *)android_media_MediaDrm_getKeyRequest }, - { "provideLicenseResponse", "([B[B)V", - (void *)android_media_MediaDrm_provideLicenseResponse }, + { "provideKeyResponse", "([B[B)[B", + (void *)android_media_MediaDrm_provideKeyResponse }, - { "removeLicense", "([B)V", - (void *)android_media_MediaDrm_removeLicense }, + { "removeKeys", "([B)V", + (void *)android_media_MediaDrm_removeKeys }, - { "queryLicenseStatus", "([B)Ljava/util/HashMap;", - (void *)android_media_MediaDrm_queryLicenseStatus }, + { "restoreKeys", "([B[B)V", + (void *)android_media_MediaDrm_restoreKeys }, + + { "queryKeyStatus", "([B)Ljava/util/HashMap;", + (void *)android_media_MediaDrm_queryKeyStatus }, { "getProvisionRequest", "()Landroid/media/MediaDrm$ProvisionRequest;", (void *)android_media_MediaDrm_getProvisionRequest }, @@ -808,6 +994,26 @@ static JNINativeMethod gMethods[] = { { "setPropertyByteArray", "(Ljava/lang/String;[B)V", (void *)android_media_MediaDrm_setPropertyByteArray }, + + { "setCipherAlgorithmNative", + "(Landroid/media/MediaDrm;[BLjava/lang/String;)V", + (void *)android_media_MediaDrm_setCipherAlgorithmNative }, + + { "setMacAlgorithmNative", + "(Landroid/media/MediaDrm;[BLjava/lang/String;)V", + (void *)android_media_MediaDrm_setMacAlgorithmNative }, + + { "encryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B", + (void *)android_media_MediaDrm_encryptNative }, + + { "decryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B", + (void *)android_media_MediaDrm_decryptNative }, + + { "signNative", "(Landroid/media/MediaDrm;[B[B[B)[B", + (void *)android_media_MediaDrm_signNative }, + + { "verifyNative", "(Landroid/media/MediaDrm;[B[B[B[B)Z", + (void *)android_media_MediaDrm_verifyNative }, }; int register_android_media_Drm(JNIEnv *env) { diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index bc65de5..fbd5d21 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -775,7 +775,8 @@ MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, env->ReleaseIntArrayElements(mIntBuffer, intValues, 0); jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); - info.mDateModified = longValues[0]; + info.mDateCreated = longValues[0]; + info.mDateModified = longValues[1]; env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); // info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ? diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 4a67997..a446e40 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -98,11 +98,11 @@ public class SettingsHelper { private void setGpsLocation(String value) { UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (! um.isLocationSharingToggleAllowed()) { + if (um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION)) { return; } final String GPS = LocationManager.GPS_PROVIDER; - boolean enabled = + boolean enabled = GPS.equals(value) || value.startsWith(GPS + ",") || value.endsWith("," + GPS) || diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 45319a8..659651b 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -322,7 +322,7 @@ public class SettingsProvider extends ContentProvider { @Override public boolean onCreate() { mBackupManager = new BackupManager(getContext()); - mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); + mUserManager = UserManager.get(getContext()); setAppOps(AppOpsManager.OP_NONE, AppOpsManager.OP_WRITE_SETTINGS); establishDbTracking(UserHandle.USER_OWNER); diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk index 262000e..015c0cc 100644 --- a/packages/SystemUI/Android.mk +++ b/packages/SystemUI/Android.mk @@ -4,13 +4,10 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) \ - ../../../ex/carousel/java/com/android/ex/carousel/carousel.rs \ src/com/android/systemui/EventLogTags.logtags LOCAL_JAVA_LIBRARIES := services telephony-common -LOCAL_STATIC_JAVA_LIBRARIES := android-common-carousel - LOCAL_PACKAGE_NAME := SystemUI LOCAL_CERTIFICATE := platform diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index b498368..5041617 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -76,11 +76,13 @@ class SaveImageInBackgroundData { int result; void clearImage() { - context = null; image = null; imageUri = null; iconSize = 0; } + void clearContext() { + context = null; + } } /** @@ -172,8 +174,6 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi mNotificationBuilder.setLargeIcon(croppedIcon); // But we still don't set it for the expanded view, allowing the smallIcon to show here. mNotificationStyle.bigLargeIcon(null); - - Log.d(TAG, "SaveImageInBackgroundTask constructor"); } @Override @@ -181,7 +181,7 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi if (params.length != 1) return null; if (isCancelled()) { params[0].clearImage(); - Log.d(TAG, "doInBackground cancelled"); + params[0].clearContext(); return null; } @@ -246,7 +246,6 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi // mounted params[0].clearImage(); params[0].result = 1; - Log.d(TAG, "doInBackground failed"); } // Recycle the bitmap data @@ -254,7 +253,6 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi image.recycle(); } - Log.d(TAG, "doInBackground complete"); return params[0]; } @@ -263,7 +261,7 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi if (isCancelled()) { params.finisher.run(); params.clearImage(); - Log.d(TAG, "onPostExecute cancelled"); + params.clearContext(); return; } @@ -291,7 +289,7 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi mNotificationManager.notify(mNotificationId, n); } params.finisher.run(); - Log.d(TAG, "onPostExecute complete"); + params.clearContext(); } } @@ -395,15 +393,12 @@ class GlobalScreenshot { // Setup the Camera shutter sound mCameraSound = new MediaActionSound(); mCameraSound.load(MediaActionSound.SHUTTER_CLICK); - - Log.d(TAG, "GlobalScreenshot constructor"); } /** * Creates a new worker thread and saves the screenshot to the media store. */ private void saveScreenshotInWorkerThread(Runnable finisher) { - Log.d(TAG, "saveScreenshotInWorkerThread"); SaveImageInBackgroundData data = new SaveImageInBackgroundData(); data.context = mContext; data.image = mScreenBitmap; @@ -411,7 +406,6 @@ class GlobalScreenshot { data.finisher = finisher; if (mSaveInBgTask != null) { mSaveInBgTask.cancel(false); - Log.d(TAG, "saveScreenshotInWorkerThread cancel"); } mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager, SCREENSHOT_NOTIFICATION_ID).execute(data); @@ -436,8 +430,6 @@ class GlobalScreenshot { * Takes a screenshot of the current display and shows an animation. */ void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) { - Log.d(TAG, "takeScreenshot"); - // We need to orient the screenshot correctly (and the Surface api seems to take screenshots // only in the natural orientation of the device :!) mDisplay.getRealMetrics(mDisplayMetrics); @@ -451,8 +443,6 @@ class GlobalScreenshot { mDisplayMatrix.mapPoints(dims); dims[0] = Math.abs(dims[0]); dims[1] = Math.abs(dims[1]); - - Log.d(TAG, "takeScreenshot requiresRotation"); } // Take the screenshot @@ -460,7 +450,6 @@ class GlobalScreenshot { if (mScreenBitmap == null) { notifyScreenshotError(mContext, mNotificationManager); finisher.run(); - Log.d(TAG, "takeScreenshot null bitmap"); return; } @@ -477,7 +466,6 @@ class GlobalScreenshot { // Recycle the previous bitmap mScreenBitmap.recycle(); mScreenBitmap = ss; - Log.d(TAG, "takeScreenshot rotation bitmap created"); } // Optimizations @@ -487,7 +475,6 @@ class GlobalScreenshot { // Start the post-screenshot animation startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, statusBarVisible, navBarVisible); - Log.d(TAG, "takeScreenshot startedAnimation"); } @@ -496,7 +483,6 @@ class GlobalScreenshot { */ private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible, boolean navBarVisible) { - Log.d(TAG, "startAnimation"); // Add the view for the animation mScreenshotView.setImageBitmap(mScreenBitmap); mScreenshotLayout.requestFocus(); @@ -505,11 +491,9 @@ class GlobalScreenshot { if (mScreenshotAnimation != null) { mScreenshotAnimation.end(); mScreenshotAnimation.removeAllListeners(); - Log.d(TAG, "startAnimation reset previous animations"); } mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); - Log.d(TAG, "startAnimation layout added to WM"); ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation(); ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible); @@ -525,7 +509,6 @@ class GlobalScreenshot { // Clear any references to the bitmap mScreenBitmap = null; mScreenshotView.setImageBitmap(null); - Log.d(TAG, "startAnimation onAnimationEnd"); } }); mScreenshotLayout.post(new Runnable() { @@ -537,7 +520,6 @@ class GlobalScreenshot { mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null); mScreenshotView.buildLayer(); mScreenshotAnimation.start(); - Log.d(TAG, "startAnimation post runnable"); } }); } @@ -675,7 +657,6 @@ class GlobalScreenshot { } static void notifyScreenshotError(Context context, NotificationManager nManager) { - Log.d(TAG, "notifyScreenshotError"); Resources r = context.getResources(); // Clear all existing notification, compose the new notification and show it diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 1954af8..6a0fe47 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -38,15 +38,12 @@ public class TakeScreenshotService extends Service { final Messenger callback = msg.replyTo; if (mScreenshot == null) { mScreenshot = new GlobalScreenshot(TakeScreenshotService.this); - Log.d(TAG, "Global screenshot initialized"); } - Log.d(TAG, "Global screenshot captured"); mScreenshot.takeScreenshot(new Runnable() { @Override public void run() { Message reply = Message.obtain(null, 1); try { callback.send(reply); - Log.d(TAG, "Global screenshot completed"); } catch (RemoteException e) { } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java index 0f894a1..0f894a1 100755..100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java index 60e22c5..a7c7fba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java @@ -206,8 +206,7 @@ class QuickSettings { mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() { @Override protected Pair<String, Drawable> doInBackground(Void... params) { - final UserManager um = - (UserManager) mContext.getSystemService(Context.USER_SERVICE); + final UserManager um = UserManager.get(mContext); // Fall back to the UserManager nickname if we can't read the name from the local // profile below. @@ -292,8 +291,7 @@ class QuickSettings { @Override public void onClick(View v) { mBar.collapseAllPanels(true); - final UserManager um = - (UserManager) mContext.getSystemService(Context.USER_SERVICE); + final UserManager um = UserManager.get(mContext); if (um.getUsers(true).size() > 1) { try { WindowManagerGlobal.getWindowManagerService().lockNow(null); diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index a2ac8fe..a2ac8fe 100755..100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java diff --git a/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java b/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java new file mode 100644 index 0000000..3cf7e82 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/GlobalKeyManager.java @@ -0,0 +1,126 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.policy.impl; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.os.UserHandle; +import android.util.Log; +import android.util.SparseArray; +import android.view.KeyEvent; + +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +/** + * Stores a mapping of global keys. + * <p> + * A global key will NOT go to the foreground application and instead only ever be sent via targeted + * broadcast to the specified component. The action of the intent will be + * {@link Intent#ACTION_GLOBAL_BUTTON} and the KeyEvent will be included in the intent with + * {@link Intent#EXTRA_KEY_EVENT}. + */ +final class GlobalKeyManager { + + private static final String TAG = "GlobalKeyManager"; + + private static final String TAG_GLOBAL_KEYS = "global_keys"; + private static final String ATTR_VERSION = "version"; + private static final String TAG_KEY = "key"; + private static final String ATTR_KEY_CODE = "keyCode"; + private static final String ATTR_COMPONENT = "component"; + + private static final int GLOBAL_KEY_FILE_VERSION = 1; + + private SparseArray<ComponentName> mKeyMapping; + + public GlobalKeyManager(Context context) { + mKeyMapping = new SparseArray<ComponentName>(); + loadGlobalKeys(context); + } + + /** + * Broadcasts an intent if the keycode is part of the global key mapping. + * + * @param context context used to broadcast the event + * @param keyCode keyCode which triggered this function + * @param event keyEvent which trigged this function + * @return {@code true} if this was handled + */ + boolean handleGlobalKey(Context context, int keyCode, KeyEvent event) { + if (mKeyMapping.size() > 0) { + ComponentName component = mKeyMapping.get(keyCode); + if (component != null) { + Intent intent = new Intent(Intent.ACTION_GLOBAL_BUTTON) + .setComponent(component) + .putExtra(Intent.EXTRA_KEY_EVENT, event); + context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null); + return true; + } + } + return false; + } + + /** + * Returns {@code true} if the key will be handled globally. + */ + boolean shouldHandleGlobalKey(int keyCode, KeyEvent event) { + return mKeyMapping.get(keyCode) != null; + } + + private void loadGlobalKeys(Context context) { + XmlResourceParser parser = null; + try { + parser = context.getResources().getXml(com.android.internal.R.xml.global_keys); + XmlUtils.beginDocument(parser, TAG_GLOBAL_KEYS); + int version = parser.getAttributeIntValue(null, ATTR_VERSION, 0); + if (GLOBAL_KEY_FILE_VERSION == version) { + while (true) { + XmlUtils.nextElement(parser); + String element = parser.getName(); + if (element == null) { + break; + } + if (TAG_KEY.equals(element)) { + String keyCodeName = parser.getAttributeValue(null, ATTR_KEY_CODE); + String componentName = parser.getAttributeValue(null, ATTR_COMPONENT); + int keyCode = KeyEvent.keyCodeFromString(keyCodeName); + if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { + mKeyMapping.put(keyCode, ComponentName.unflattenFromString( + componentName)); + } + } + } + } + } catch (Resources.NotFoundException e) { + Log.w(TAG, "global keys file not found", e); + } catch (XmlPullParserException e) { + Log.w(TAG, "XML parser exception reading global keys file", e); + } catch (IOException e) { + Log.w(TAG, "I/O exception reading global keys file", e); + } finally { + if (parser != null) { + parser.close(); + } + } + } +} diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 5ad305c..ad5e20b 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -989,6 +989,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final Callback cb = getCallback(); if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) { if (cb != null && !isDestroyed() && mActionBar.getVisibility() == View.VISIBLE) { + // If we have a menu invalidation pending, do it now. + if (mInvalidatePanelMenuPosted && + (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) { + mDecor.removeCallbacks(mInvalidatePanelMenuRunnable); + mInvalidatePanelMenuRunnable.run(); + } + final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); // If we don't have a menu or we're waiting for a full content refresh, diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index bb05325..49460de 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -441,6 +441,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { PowerManager.WakeLock mBroadcastWakeLock; boolean mHavePendingMediaKeyRepeatWithWakeLock; + // Maps global key codes to the components that will handle them. + private GlobalKeyManager mGlobalKeyManager; + // Fallback actions by key code. private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions = new SparseArray<KeyCharacterMap.FallbackAction>(); @@ -898,6 +901,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mScreenshotChordEnabled = mContext.getResources().getBoolean( com.android.internal.R.bool.config_enableScreenshotChord); + mGlobalKeyManager = new GlobalKeyManager(mContext); + // Controls rotation and the like. initializeHdmiState(); @@ -2140,6 +2145,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { return -1; } + if (mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) { + return -1; + } + // Let the application handle the key. return 0; } @@ -3585,6 +3594,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + // If the key would be handled globally, just return the result, don't worry about special + // key processing. + if (mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) { + return result; + } + // Handle special keys. switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ClockView.java b/policy/src/com/android/internal/policy/impl/keyguard/ClockView.java index 6c701c7..34bf6e7 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/ClockView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/ClockView.java @@ -42,7 +42,7 @@ import com.android.internal.R; public class ClockView extends RelativeLayout { private static final String ANDROID_CLOCK_FONT_FILE = "/system/fonts/AndroidClock.ttf"; private final static String M12 = "h:mm"; - private final static String M24 = "kk:mm"; + private final static String M24 = "HH:mm"; private Calendar mCalendar; private String mFormat; diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java index 4df434c..965e378 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java @@ -18,17 +18,22 @@ package com.android.internal.policy.impl.keyguard; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; +import android.view.IRotationWatcher; +import android.view.IWindowManager; import android.view.View; import android.widget.ImageButton; import android.widget.LinearLayout; import com.android.internal.R; - import com.android.internal.widget.LockPatternUtils; +import java.lang.Math; + public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecurityView { private static final String TAG = "FULKeyguardFaceUnlockView"; @@ -45,6 +50,30 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu private boolean mIsShowing = false; private final Object mIsShowingLock = new Object(); + private int mLastRotation; + private boolean mWatchingRotation; + private final IWindowManager mWindowManager = + IWindowManager.Stub.asInterface(ServiceManager.getService("window")); + + private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() { + public void onRotationChanged(int rotation) { + if (DEBUG) Log.d(TAG, "onRotationChanged(): " + mLastRotation + "->" + rotation); + + // If the difference between the new rotation value and the previous rotation value is + // equal to 2, the rotation change was 180 degrees. This stops the biometric unlock + // and starts it in the new position. This is not performed for 90 degree rotations + // since a 90 degree rotation is a configuration change, which takes care of this for + // us. + if (Math.abs(rotation - mLastRotation) == 2) { + if (mBiometricUnlock != null) { + mBiometricUnlock.stop(); + maybeStartBiometricUnlock(); + } + } + mLastRotation = rotation; + } + }; + public KeyguardFaceUnlockView(Context context) { this(context, null); } @@ -91,6 +120,14 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu mBiometricUnlock.stop(); } KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback); + if (mWatchingRotation) { + try { + mWindowManager.removeRotationWatcher(mRotationWatcher); + mWatchingRotation = false; + } catch (RemoteException e) { + Log.e(TAG, "Remote exception when removing rotation watcher"); + } + } } @Override @@ -100,6 +137,14 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu mBiometricUnlock.stop(); } KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback); + if (mWatchingRotation) { + try { + mWindowManager.removeRotationWatcher(mRotationWatcher); + mWatchingRotation = false; + } catch (RemoteException e) { + Log.e(TAG, "Remote exception when removing rotation watcher"); + } + } } @Override @@ -108,6 +153,17 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu mIsShowing = KeyguardUpdateMonitor.getInstance(mContext).isKeyguardVisible(); maybeStartBiometricUnlock(); KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback); + + // Registers a callback which handles stopping the biometric unlock and restarting it in + // the new position for a 180 degree rotation change. + if (!mWatchingRotation) { + try { + mLastRotation = mWindowManager.watchRotation(mRotationWatcher); + mWatchingRotation = true; + } catch (RemoteException e) { + Log.e(TAG, "Remote exception when adding rotation watcher"); + } + } } @Override @@ -172,9 +228,15 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu return; } - // TODO: Some of these conditions are handled in KeyguardSecurityModel and may not be - // necessary here. + // Although these same conditions are handled in KeyguardSecurityModel, they are still + // necessary here. When a tablet is rotated 90 degrees, a configuration change is + // triggered and everything is torn down and reconstructed. That means + // KeyguardSecurityModel gets a chance to take care of the logic and doesn't even + // reconstruct KeyguardFaceUnlockView if the biometric unlock should be suppressed. + // However, for a 180 degree rotation, no configuration change is triggered, so only + // the logic here is capable of suppressing Face Unlock. if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE + && monitor.isAlternateUnlockEnabled() && !monitor.getMaxBiometricUnlockAttemptsReached() && !backupIsTimedOut) { mBiometricUnlock.start(); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java index 9712ea8..5e3b7da 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java @@ -132,7 +132,8 @@ public class KeyguardTransportControlView extends FrameLayout implements OnClick mLocalHandler = new WeakReference<Handler>(handler); } - public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) { + public void setPlaybackState(int generationId, int state, long stateChangeTimeMs, + long currentPosMs, float speed) { Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget(); @@ -146,7 +147,7 @@ public class KeyguardTransportControlView extends FrameLayout implements OnClick } } - public void setTransportControlFlags(int generationId, int flags) { + public void setTransportControlInfo(int generationId, int flags, int posCapabilities) { Handler handler = mLocalHandler.get(); if (handler != null) { handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags) diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java index 27d816e..159a92d 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java @@ -200,7 +200,8 @@ public class KeyguardUpdateMonitor { private final IRemoteControlDisplay.Stub mRemoteControlDisplay = new IRemoteControlDisplay.Stub() { - public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) { + public void setPlaybackState(int generationId, int state, long stateChangeTimeMs, + long currentPosMs, float speed) { Message msg = mHandler.obtainMessage(MSG_SET_PLAYBACK_STATE, generationId, state, stateChangeTimeMs); mHandler.sendMessage(msg); @@ -210,7 +211,7 @@ public class KeyguardUpdateMonitor { } - public void setTransportControlFlags(int generationId, int flags) { + public void setTransportControlInfo(int generationId, int flags, int posCapabilities) { } diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java index 33e712a..ea7b696 100755..100644 --- a/services/java/com/android/server/BluetoothManagerService.java +++ b/services/java/com/android/server/BluetoothManagerService.java @@ -19,6 +19,7 @@ package com.android.server; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetooth; +import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; @@ -87,6 +88,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // and Airplane mode will have higher priority. private static final int BLUETOOTH_ON_AIRPLANE=2; + private static final int SERVICE_IBLUETOOTH = 1; + private static final int SERVICE_IBLUETOOTHGATT = 2; + private final Context mContext; // Locks are not provided for mName and mAddress. @@ -97,6 +101,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks; private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks; private IBluetooth mBluetooth; + private IBluetoothGatt mBluetoothGatt; private boolean mBinding; private boolean mUnbinding; // used inside handler thread @@ -463,6 +468,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + public IBluetoothGatt getBluetoothGatt() { + // sync protection + return mBluetoothGatt; + } + private void sendBluetoothStateCallback(boolean isUp) { int n = mStateChangeCallbacks.beginBroadcast(); if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers."); @@ -575,16 +585,35 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public void onServiceConnected(ComponentName className, IBinder service) { - if (DBG) Log.d(TAG, "BluetoothServiceConnection: connected to AdapterService"); + if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName()); Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED); + // TBD if (className.getClassName().equals(IBluetooth.class.getName())) { + if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { + msg.arg1 = SERVICE_IBLUETOOTH; + // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) { + } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { + msg.arg1 = SERVICE_IBLUETOOTHGATT; + } else { + Log.e(TAG, "Unknown service connected: " + className.getClassName()); + return; + } msg.obj = service; mHandler.sendMessage(msg); } public void onServiceDisconnected(ComponentName className) { // Called if we unexpected disconnected. - if (DBG) Log.d(TAG, "BluetoothServiceConnection: disconnected from AdapterService"); + if (DBG) Log.d(TAG, "BluetoothServiceConnection, disconnected: " + + className.getClassName()); Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED); + if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) { + msg.arg1 = SERVICE_IBLUETOOTH; + } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) { + msg.arg1 = SERVICE_IBLUETOOTHGATT; + } else { + Log.e(TAG, "Unknown service disconnected: " + className.getClassName()); + return; + } mHandler.sendMessage(msg); } } @@ -746,13 +775,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: { - if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED"); - - //Remove timeout - mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1); IBinder service = (IBinder) msg.obj; synchronized(mConnection) { + if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { + mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service); + break; + } // else must be SERVICE_IBLUETOOTH + + //Remove timeout + mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + mBinding = false; mBluetooth = IBluetooth.Stub.asInterface(service); @@ -816,11 +850,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: { - Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED"); + Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1); synchronized(mConnection) { - // if service is unbinded already, do nothing and return - if (mBluetooth == null) return; - mBluetooth = null; + if (msg.arg1 == SERVICE_IBLUETOOTH) { + // if service is unbinded already, do nothing and return + if (mBluetooth == null) break; + mBluetooth = null; + } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) { + mBluetoothGatt = null; + break; + } else { + Log.e(TAG, "Bad msg.arg1: " + msg.arg1); + break; + } } if (mEnable) { @@ -1048,10 +1090,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub { boolean isUp = (newState==BluetoothAdapter.STATE_ON); sendBluetoothStateCallback(isUp); - //If Bluetooth is off, send service down event to proxy objects, and unbind - if (!isUp && canUnbindBluetoothService()) { - sendBluetoothServiceDownCallback(); - unbindAndFinish(); + if (isUp) { + // connect to GattService + Intent i = new Intent(IBluetoothGatt.class.getName()); + if (!mContext.bindServiceAsUser(i, mConnection, Context.BIND_AUTO_CREATE, + UserHandle.CURRENT)) { + Log.e(TAG, "Fail to bind to: " + IBluetoothGatt.class.getName()); + } + } else { + //If Bluetooth is off, send service down event to proxy objects, and unbind + if (!isUp && canUnbindBluetoothService()) { + sendBluetoothServiceDownCallback(); + unbindAndFinish(); + } } } @@ -1081,9 +1132,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true; } else if (off) { if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true; - } else { + } else { if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true; - } + } } catch (RemoteException e) { Log.e(TAG, "getState()", e); break; @@ -1091,9 +1142,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (on || off) { SystemClock.sleep(300); - } else { + } else { SystemClock.sleep(50); - } + } i++; } Log.e(TAG,"waitForOnOff time out"); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 6dcb403..9e06db8 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -74,6 +74,7 @@ import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; +import android.os.Messenger; import android.os.ParcelFileDescriptor; import android.os.PowerManager; import android.os.Process; @@ -2284,9 +2285,17 @@ public class ConnectivityService extends IConnectivityManager.Stub { } // Update 464xlat state. - // TODO: Move to handleConnect() NetworkStateTracker tracker = mNetTrackers[netType]; if (mClat.requiresClat(netType, tracker)) { + // If the connection was previously using clat, but is not using it now, stop the clat + // daemon. Normally, this happens automatically when the connection disconnects, but if + // the disconnect is not reported, or if the connection's LinkProperties changed for + // some other reason (e.g., handoff changes the IP addresses on the link), it would + // still be running. If it's not running, then stopping it is a no-op. + if (Nat464Xlat.isRunningClat(curLp) && !Nat464Xlat.isRunningClat(newLp)) { + mClat.stopClat(); + } + // If the link requires clat to be running, then start the daemon now. if (mNetTrackers[netType].getNetworkInfo().isConnected()) { mClat.startClat(tracker); } else { @@ -3220,7 +3229,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { throwIfLockdownEnabled(); try { int type = mActiveDefaultNetwork; - if (ConnectivityManager.isNetworkTypeValid(type)) { + if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) { mVpn.protect(socket, mNetTrackers[type].getLinkProperties().getInterfaceName()); return true; } @@ -3425,4 +3434,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { throw new IllegalStateException("Unavailable in lockdown mode"); } } + + public void supplyMessenger(int networkType, Messenger messenger) { + enforceConnectivityInternalPermission(); + + if (isNetworkTypeValid(networkType) && mNetTrackers[networkType] != null) { + mNetTrackers[networkType].supplyMessenger(messenger); + } + } } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index d3e7c24..ab70e6f 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -2443,7 +2443,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { ipm.setApplicationEnabledSetting(packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, - PackageManager.DONT_KILL_APP, userId); + PackageManager.DONT_KILL_APP, userId, "DevicePolicyManager"); } } catch (RemoteException e) { } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 2d53023..3b541ec 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1598,7 +1598,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(), PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, - PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId()); + PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(), + mContext.getBasePackageName()); } } catch (RemoteException e) { } diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 2210a18..d2acb40 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -839,33 +839,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub return event.getMessage().endsWith("started"); } - // TODO(BT) Remove - @Override - public void startReverseTethering(String iface) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - // cmd is "tether start first_start first_stop second_start second_stop ..." - // an odd number of addrs will fail - try { - mConnector.execute("tether", "start-reverse", iface); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - BluetoothTetheringDataTracker.getInstance().startReverseTether(iface); - - } - - // TODO(BT) Remove - @Override - public void stopReverseTethering() { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute("tether", "stop-reverse"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - BluetoothTetheringDataTracker.getInstance().stopReverseTether(); - } - @Override public void tetherInterface(String iface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 4631395..a30fc3b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -862,6 +862,11 @@ class ServerThread extends Thread { public void run() { Slog.i(TAG, "Making services ready"); + try { + ActivityManagerService.self().startObservingNativeCrashes(); + } catch (Throwable e) { + reportWtf("observing native crashes", e); + } if (!headless) startSystemUi(contextF); try { if (mountServiceF != null) mountServiceF.systemReady(); diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index 1663106..167e7af 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -88,7 +88,6 @@ public class Watchdog extends Thread { AlarmManagerService mAlarm; ActivityManagerService mActivity; boolean mCompleted; - boolean mForceKillSystem; Monitor mCurrentMonitor; int mPhonePid; @@ -135,7 +134,9 @@ public class Watchdog extends Thread { final int size = mMonitors.size(); for (int i = 0 ; i < size ; i++) { - mCurrentMonitor = mMonitors.get(i); + synchronized (Watchdog.this) { + mCurrentMonitor = mMonitors.get(i); + } mCurrentMonitor.monitor(); } @@ -388,6 +389,8 @@ public class Watchdog extends Thread { mCompleted = false; mHandler.sendEmptyMessage(MONITOR); + + final String name; synchronized (this) { long timeout = TIME_TO_WAIT; @@ -396,16 +399,16 @@ public class Watchdog extends Thread { // to timeout on is asleep as well and won't have a chance to run, causing a false // positive on when to kill things. long start = SystemClock.uptimeMillis(); - while (timeout > 0 && !mForceKillSystem) { + while (timeout > 0) { try { - wait(timeout); // notifyAll() is called when mForceKillSystem is set + wait(timeout); } catch (InterruptedException e) { Log.wtf(TAG, e); } timeout = TIME_TO_WAIT - (SystemClock.uptimeMillis() - start); } - if (mCompleted && !mForceKillSystem) { + if (mCompleted) { // The monitors have returned. waitedHalf = false; continue; @@ -421,14 +424,14 @@ public class Watchdog extends Thread { waitedHalf = true; continue; } + + name = (mCurrentMonitor != null) ? + mCurrentMonitor.getClass().getName() : "null"; } // If we got here, that means that the system is most likely hung. // First collect stack traces from all threads of the system process. // Then kill this process so that the system will restart. - - final String name = (mCurrentMonitor != null) ? - mCurrentMonitor.getClass().getName() : "null"; EventLog.writeEvent(EventLogTags.WATCHDOG, name); ArrayList<Integer> pids = new ArrayList<Integer>(); diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java index c4b98ad..14d808f 100644 --- a/services/java/com/android/server/accounts/AccountManagerService.java +++ b/services/java/com/android/server/accounts/AccountManagerService.java @@ -58,6 +58,7 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -115,6 +116,7 @@ public class AccountManagerService // Messages that can be sent on mHandler private static final int MESSAGE_TIMED_OUT = 3; + private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4; private final IAccountAuthenticatorCache mAuthenticatorCache; @@ -270,7 +272,7 @@ public class AccountManagerService private UserManager getUserManager() { if (mUserManager == null) { - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + mUserManager = UserManager.get(mContext); } return mUserManager; } @@ -541,17 +543,22 @@ public class AccountManagerService } } - public boolean addAccount(Account account, String password, Bundle extras) { + @Override + public boolean addAccountExplicitly(Account account, String password, Bundle extras) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "addAccount: " + account + Log.v(TAG, "addAccountExplicitly: " + account + ", caller's uid " + Binder.getCallingUid() + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); checkAuthenticateAccountsPermission(account); - if (!canUserModifyAccounts(Binder.getCallingUid())) { - return false; - } + /* + * Child users are not allowed to add accounts. Only the accounts that are + * shared by the parent profile can be added to child profile. + * + * TODO: Only allow accounts that were shared to be added by + * a limited user. + */ UserAccounts accounts = getUserAccountsForCaller(); // fails if the account already exists @@ -588,12 +595,9 @@ public class AccountManagerService if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { // Create a Session for the target user and pass in the bundle completeCloningAccount(result, account, toAccounts); - } else { - clonePassword(fromAccounts, toAccounts, account); } return; } else { - clonePassword(fromAccounts, toAccounts, account); super.onResult(result); } } @@ -604,23 +608,6 @@ public class AccountManagerService return true; } - // TODO: Remove fallback - move to authenticator - private void clonePassword(UserAccounts fromAccounts, UserAccounts toAccounts, - Account account) { - long id = clearCallingIdentity(); - try { - String password = readPasswordInternal(fromAccounts, account); - String extraFlags = readUserDataInternal(fromAccounts, account, "flags"); - String extraServices = readUserDataInternal(fromAccounts, account, "services"); - Bundle extras = new Bundle(); - extras.putString("flags", extraFlags); - extras.putString("services", extraServices); - addAccountInternal(toAccounts, account, password, extras, true); - } finally { - restoreCallingIdentity(id); - } - } - void completeCloningAccount(final Bundle result, final Account account, final UserAccounts targetUser) { long id = clearCallingIdentity(); @@ -633,7 +620,17 @@ public class AccountManagerService } public void run() throws RemoteException { - mAuthenticator.addAccountFromCredentials(this, account, result); + // Confirm that the owner's account still exists before this step. + UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER); + synchronized (owner.cacheLock) { + Account[] ownerAccounts = getAccounts(UserHandle.USER_OWNER); + for (Account acc : ownerAccounts) { + if (acc.equals(account)) { + mAuthenticator.addAccountFromCredentials(this, account, result); + break; + } + } + } } public void onResult(Bundle result) { @@ -706,7 +703,33 @@ public class AccountManagerService db.endTransaction(); } sendAccountsChangedBroadcast(accounts.userId); - return true; + } + if (accounts.userId == UserHandle.USER_OWNER) { + addAccountToLimitedUsers(account); + } + return true; + } + + /** + * Adds the account to all limited users as shared accounts. If the user is currently + * running, then clone the account too. + * @param account the account to share with limited users + */ + private void addAccountToLimitedUsers(Account account) { + List<UserInfo> users = getUserManager().getUsers(); + for (UserInfo user : users) { + if (user.isRestricted()) { + addSharedAccountAsUser(account, user.id); + try { + if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) { + mMessageHandler.sendMessage(mMessageHandler.obtainMessage( + MESSAGE_COPY_SHARED_ACCOUNT, UserHandle.USER_OWNER, user.id, + account)); + } + } catch (RemoteException re) { + // Shouldn't happen + } + } } } @@ -906,6 +929,7 @@ public class AccountManagerService } } + @Override public void invalidateAuthToken(String accountType, String authToken) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "invalidateAuthToken: accountType " + accountType @@ -1181,7 +1205,7 @@ public class AccountManagerService final int callingUid = getCallingUid(); clearCallingIdentity(); - if (callingUid != android.os.Process.SYSTEM_UID) { + if (callingUid != Process.SYSTEM_UID) { throw new SecurityException("can only call from system"); } UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid)); @@ -1351,7 +1375,7 @@ public class AccountManagerService String subtitle = ""; if (index > 0) { title = titleAndSubtitle.substring(0, index); - subtitle = titleAndSubtitle.substring(index + 1); + subtitle = titleAndSubtitle.substring(index + 1); } UserHandle user = new UserHandle(userId); n.setLatestEventInfo(mContext, title, subtitle, @@ -1409,7 +1433,7 @@ public class AccountManagerService return id; } - public void addAcount(final IAccountManagerResponse response, final String accountType, + public void addAccount(final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -1426,8 +1450,7 @@ public class AccountManagerService checkManageAccountsPermission(); // Is user disallowed from modifying accounts? - if (getUserManager().getUserRestrictions(Binder.getCallingUserHandle()) - .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) { + if (!canUserModifyAccounts(Binder.getCallingUid())) { try { response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, "User is not allowed to add an account!"); @@ -1472,7 +1495,7 @@ public class AccountManagerService int userId) { // Only allow the system process to read accounts of other users if (userId != UserHandle.getCallingUserId() - && Binder.getCallingUid() != android.os.Process.myUid()) { + && Binder.getCallingUid() != Process.myUid()) { throw new SecurityException("User " + UserHandle.getCallingUserId() + " trying to confirm account credentials for " + userId); } @@ -1588,7 +1611,8 @@ public class AccountManagerService public void run() throws RemoteException { synchronized (mAccounts.cacheLock) { - mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid); + mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid, + null); } // check whether each account matches the requested features mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length); @@ -1677,7 +1701,7 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, null, callingUid); + return getAccountsFromCacheLocked(accounts, null, callingUid, null); } } finally { restoreCallingIdentity(identityToken); @@ -1718,7 +1742,7 @@ public class AccountManagerService if (userAccounts == null) continue; synchronized (userAccounts.cacheLock) { Account[] accounts = getAccountsFromCacheLocked(userAccounts, null, - Binder.getCallingUid()); + Binder.getCallingUid(), null); for (int a = 0; a < accounts.length; a++) { runningAccounts.add(new AccountAndUser(accounts[a], userId)); } @@ -1732,10 +1756,15 @@ public class AccountManagerService @Override public Account[] getAccountsAsUser(String type, int userId) { - final int callingUid = Binder.getCallingUid(); + return getAccountsAsUser(type, userId, null, -1); + } + + private Account[] getAccountsAsUser(String type, int userId, String callingPackage, + int packageUid) { + int callingUid = Binder.getCallingUid(); // Only allow the system process to read accounts of other users if (userId != UserHandle.getCallingUserId() - && callingUid != android.os.Process.myUid()) { + && callingUid != Process.myUid()) { throw new SecurityException("User " + UserHandle.getCallingUserId() + " trying to get account for " + userId); } @@ -1745,12 +1774,17 @@ public class AccountManagerService + ", caller's uid " + Binder.getCallingUid() + ", pid " + Binder.getCallingPid()); } + // If the original calling app was using the framework account chooser activity, we'll + // be passed in the original caller's uid here, which is what should be used for filtering. + if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) { + callingUid = packageUid; + } checkReadAccountsPermission(); UserAccounts accounts = getUserAccounts(userId); long identityToken = clearCallingIdentity(); try { synchronized (accounts.cacheLock) { - return getAccountsFromCacheLocked(accounts, type, callingUid); + return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage); } } finally { restoreCallingIdentity(identityToken); @@ -1821,6 +1855,16 @@ public class AccountManagerService return getAccountsAsUser(type, UserHandle.getCallingUserId()); } + @Override + public Account[] getAccountsForPackage(String packageName, int uid) { + int callingUid = Binder.getCallingUid(); + if (!UserHandle.isSameApp(callingUid, Process.myUid())) { + throw new SecurityException("getAccountsForPackage() called from unauthorized uid " + + callingUid + " with uid=" + uid); + } + return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid); + } + public void getAccountsByFeatures(IAccountManagerResponse response, String type, String[] features) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -1840,7 +1884,7 @@ public class AccountManagerService if (features == null || features.length == 0) { Account[] accounts; synchronized (userAccounts.cacheLock) { - accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid); + accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null); } Bundle result = new Bundle(); result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); @@ -2154,6 +2198,10 @@ public class AccountManagerService session.onTimedOut(); break; + case MESSAGE_COPY_SHARED_ACCOUNT: + copyAccountToUser((Account) msg.obj, msg.arg1, msg.arg2); + break; + default: throw new IllegalStateException("unhandled message: " + msg.what); } @@ -2363,7 +2411,7 @@ public class AccountManagerService } } else { Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */, - android.os.Process.myUid()); + Process.myUid(), null); fout.println("Accounts: " + accounts.length); for (Account account : accounts) { fout.println(" " + account); @@ -2516,7 +2564,7 @@ public class AccountManagerService private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType, int callerUid) { - if (callerUid == android.os.Process.SYSTEM_UID) { + if (callerUid == Process.SYSTEM_UID) { return true; } UserAccounts accounts = getUserAccountsForCaller(); @@ -2569,10 +2617,10 @@ public class AccountManagerService } private boolean canUserModifyAccounts(int callingUid) { - if (callingUid != android.os.Process.myUid()) { - Bundle restrictions = getUserManager().getUserRestrictions( - new UserHandle(UserHandle.getUserId(callingUid))); - if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) { + if (callingUid != Process.myUid()) { + if (getUserManager().getUserRestrictions( + new UserHandle(UserHandle.getUserId(callingUid))) + .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { return false; } } @@ -2583,7 +2631,7 @@ public class AccountManagerService throws RemoteException { final int callingUid = getCallingUid(); - if (callingUid != android.os.Process.SYSTEM_UID) { + if (callingUid != Process.SYSTEM_UID) { throw new SecurityException(); } @@ -2703,14 +2751,14 @@ public class AccountManagerService } private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered, - int callingUid) { + int callingUid, String callingPackage) { if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0 - || callingUid == android.os.Process.myUid()) { + || callingUid == Process.myUid()) { return unfiltered; } if (mUserManager.getUserInfo(userAccounts.userId).isRestricted()) { String[] packages = mPackageManager.getPackagesForUid(callingUid); - // If any of the packages includes a white listed package, return the full set, + // If any of the packages is a white listed package, return the full set, // otherwise return non-shared accounts only. // This might be a temporary way to specify a whitelist String whiteList = mContext.getResources().getString( @@ -2723,16 +2771,34 @@ public class AccountManagerService ArrayList<Account> allowed = new ArrayList<Account>(); Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId); if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered; - for (Account account : unfiltered) { - boolean found = false; - for (Account shared : sharedAccounts) { - if (shared.equals(account)) { - found = true; - break; + String requiredAccountType = ""; + try { + for (String packageName : packages) { + PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); + if (pi != null && pi.restrictedAccountType != null) { + requiredAccountType = pi.restrictedAccountType; + // If it matches the package name of the original caller, use this choice. + if (callingPackage != null && packageName.equals(callingPackage)) { + break; + } } } - if (!found) { + } catch (NameNotFoundException nnfe) { + } + for (Account account : unfiltered) { + if (account.type.equals(requiredAccountType)) { allowed.add(account); + } else { + boolean found = false; + for (Account shared : sharedAccounts) { + if (shared.equals(account)) { + found = true; + break; + } + } + if (!found) { + allowed.add(account); + } } } Account[] filtered = new Account[allowed.size()]; @@ -2743,15 +2809,19 @@ public class AccountManagerService } } + /* + * packageName can be null. If not null, it should be used to filter out restricted accounts + * that the package is not allowed to access. + */ protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType, - int callingUid) { + int callingUid, String callingPackage) { if (accountType != null) { final Account[] accounts = userAccounts.accountCache.get(accountType); if (accounts == null) { return EMPTY_ACCOUNT_ARRAY; } else { return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length), - callingUid); + callingUid, callingPackage); } } else { int totalLength = 0; @@ -2768,7 +2838,7 @@ public class AccountManagerService accountsOfType.length); totalLength += accountsOfType.length; } - return filterSharedAccounts(userAccounts, accounts, callingUid); + return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 90a727d..7710f13 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1410,7 +1410,7 @@ public final class ActivityManagerService extends ActivityManagerNative public static void setSystemProcess() { try { ActivityManagerService m = mSelf; - + ServiceManager.addService("activity", m, true); ServiceManager.addService("meminfo", new MemBinder(m)); ServiceManager.addService("gfxinfo", new GraphicsBinder(m)); @@ -1448,6 +1448,11 @@ public final class ActivityManagerService extends ActivityManagerNative mWindowManager = wm; } + public void startObservingNativeCrashes() { + final NativeCrashListener ncl = new NativeCrashListener(); + ncl.start(); + } + public static final Context main(int factoryTest) { AThread thr = new AThread(); thr.start(); @@ -8345,6 +8350,14 @@ public final class ActivityManagerService extends ActivityManagerNative final String processName = app == null ? "system_server" : (r == null ? "unknown" : r.processName); + handleApplicationCrashInner(r, processName, crashInfo); + } + + /* Native crash reporting uses this inner version because it needs to be somewhat + * decoupled from the AM-managed cleanup lifecycle + */ + void handleApplicationCrashInner(ProcessRecord r, String processName, + ApplicationErrorReport.CrashInfo crashInfo) { EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(), UserHandle.getUserId(Binder.getCallingUid()), processName, r == null ? -1 : r.info.flags, @@ -8858,7 +8871,7 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } - if (!r.crashing && !r.notResponding) { + if (!r.crashing && !r.notResponding && !r.forceCrashReport) { return null; } @@ -8869,7 +8882,7 @@ public final class ActivityManagerService extends ActivityManagerNative report.time = timeMillis; report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - if (r.crashing) { + if (r.crashing || r.forceCrashReport) { report.type = ApplicationErrorReport.TYPE_CRASH; report.crashInfo = crashInfo; } else if (r.notResponding) { @@ -10879,7 +10892,7 @@ public final class ActivityManagerService extends ActivityManagerNative mProcessesToGc.remove(app); // Dismiss any open dialogs. - if (app.crashDialog != null) { + if (app.crashDialog != null && !app.forceCrashReport) { app.crashDialog.dismiss(); app.crashDialog = null; } diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java new file mode 100644 index 0000000..e83433f --- /dev/null +++ b/services/java/com/android/server/am/NativeCrashListener.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.app.ApplicationErrorReport.CrashInfo; +import android.util.Slog; + +import libcore.io.ErrnoException; +import libcore.io.Libcore; +import libcore.io.StructTimeval; +import libcore.io.StructUcred; + +import static libcore.io.OsConstants.*; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.net.InetSocketAddress; +import java.net.InetUnixAddress; + +/** + * Set up a Unix domain socket that debuggerd will connect() to in + * order to write a description of a native crash. The crash info is + * then parsed and forwarded to the ActivityManagerService's normal + * crash handling code. + * + * Note that this component runs in a separate thread. + */ +class NativeCrashListener extends Thread { + static final String TAG = "NativeCrashListener"; + static final boolean DEBUG = false; + + // Must match the path defined in debuggerd.c. + static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket"; + + // Use a short timeout on socket operations and abandon the connection + // on hard errors + static final long SOCKET_TIMEOUT_MILLIS = 1000; // 1 second + + final ActivityManagerService mAm; + + /* + * Spin the actual work of handling a debuggerd crash report into a + * separate thread so that the listener can go immediately back to + * accepting incoming connections. + */ + class NativeCrashReporter extends Thread { + ProcessRecord mApp; + int mSignal; + String mCrashReport; + + NativeCrashReporter(ProcessRecord app, int signal, String report) { + super("NativeCrashReport"); + mApp = app; + mSignal = signal; + mCrashReport = report; + } + + @Override + public void run() { + try { + CrashInfo ci = new CrashInfo(); + ci.exceptionClassName = "Native crash"; + ci.exceptionMessage = Libcore.os.strsignal(mSignal); + ci.throwFileName = "unknown"; + ci.throwClassName = "unknown"; + ci.throwMethodName = "unknown"; + ci.stackTrace = mCrashReport; + + if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()"); + mAm.handleApplicationCrashInner(mApp, mApp.processName, ci); + if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned"); + } catch (Exception e) { + Slog.e(TAG, "Unable to report native crash", e); + } + } + } + + /* + * Daemon thread that accept()s incoming domain socket connections from debuggerd + * and processes the crash dump that is passed through. + */ + NativeCrashListener() { + mAm = ActivityManagerService.self(); + } + + @Override + public void run() { + final byte[] ackSignal = new byte[1]; + + if (DEBUG) Slog.i(TAG, "Starting up"); + + // The file system entity for this socket is created with 0700 perms, owned + // by system:system. debuggerd runs as root, so is capable of connecting to + // it, but 3rd party apps cannot. + { + File socketFile = new File(DEBUGGERD_SOCKET_PATH); + if (socketFile.exists()) { + socketFile.delete(); + } + } + + try { + FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0); + final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH); + Libcore.os.bind(serverFd, sockAddr, 0); + Libcore.os.listen(serverFd, 1); + + while (true) { + InetSocketAddress peer = new InetSocketAddress(); + FileDescriptor peerFd = null; + try { + if (DEBUG) Slog.v(TAG, "Waiting for debuggerd connection"); + peerFd = Libcore.os.accept(serverFd, peer); + if (DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd); + if (peerFd != null) { + // Only the superuser is allowed to talk to us over this socket + StructUcred credentials = + Libcore.os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED); + if (credentials.uid == 0) { + // the reporting thread may take responsibility for + // acking the debugger; make sure we play along. + consumeNativeCrashData(peerFd); + } + } + } catch (Exception e) { + Slog.w(TAG, "Error handling connection", e); + } finally { + // Always ack debuggerd's connection to us. The actual + // byte written is irrelevant. + if (peerFd != null) { + try { + Libcore.os.write(peerFd, ackSignal, 0, 1); + } catch (Exception e) { /* we don't care about failures here */ } + } + } + } + } catch (Exception e) { + Slog.e(TAG, "Unable to init native debug socket!", e); + } + } + + static int unpackInt(byte[] buf, int offset) { + int b0, b1, b2, b3; + + b0 = ((int) buf[offset]) & 0xFF; // mask against sign extension + b1 = ((int) buf[offset+1]) & 0xFF; + b2 = ((int) buf[offset+2]) & 0xFF; + b3 = ((int) buf[offset+3]) & 0xFF; + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + + static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes) + throws ErrnoException { + int totalRead = 0; + while (numBytes > 0) { + int n = Libcore.os.read(fd, buffer, offset + totalRead, numBytes); + if (n <= 0) { + if (DEBUG) { + Slog.w(TAG, "Needed " + numBytes + " but saw " + n); + } + return -1; // premature EOF or timeout + } + numBytes -= n; + totalRead += n; + } + return totalRead; + } + + // Read the crash report from the debuggerd connection + void consumeNativeCrashData(FileDescriptor fd) { + if (DEBUG) Slog.i(TAG, "debuggerd connected"); + final byte[] buf = new byte[4096]; + final ByteArrayOutputStream os = new ByteArrayOutputStream(4096); + + try { + StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS); + Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout); + Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout); + + // first, the pid and signal number + int headerBytes = readExactly(fd, buf, 0, 8); + if (headerBytes != 8) { + // protocol failure; give up + Slog.e(TAG, "Unable to read from debuggerd"); + return; + } + + int pid = unpackInt(buf, 0); + int signal = unpackInt(buf, 4); + if (DEBUG) { + Slog.v(TAG, "Read pid=" + pid + " signal=" + signal); + } + + // now the text of the dump + if (pid > 0) { + final ProcessRecord pr; + synchronized (mAm.mPidsSelfLocked) { + pr = mAm.mPidsSelfLocked.get(pid); + } + if (pr != null) { + int bytes; + do { + // get some data + bytes = Libcore.os.read(fd, buf, 0, buf.length); + if (bytes > 0) { + if (DEBUG) { + String s = new String(buf, 0, bytes, "UTF-8"); + Slog.v(TAG, "READ=" + bytes + "> " + s); + } + // did we just get the EOD null byte? + if (buf[bytes-1] == 0) { + os.write(buf, 0, bytes-1); // exclude the EOD token + break; + } + // no EOD, so collect it and read more + os.write(buf, 0, bytes); + } + } while (bytes > 0); + + // Okay, we've got the report. + if (DEBUG) Slog.v(TAG, "processing"); + + // Mark the process record as being a native crash so that the + // cleanup mechanism knows we're still submitting the report + // even though the process will vanish as soon as we let + // debuggerd proceed. + synchronized (mAm) { + pr.crashing = true; + pr.forceCrashReport = true; + } + + // Crash reporting is synchronous but we want to let debuggerd + // go about it business right away, so we spin off the actual + // reporting logic on a thread and let it take it's time. + final String reportString = new String(os.toByteArray(), "UTF-8"); + (new NativeCrashReporter(pr, signal, reportString)).start(); + } else { + Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid); + } + } else { + Slog.e(TAG, "Bogus pid!"); + } + } catch (Exception e) { + Slog.e(TAG, "Exception dealing with report", e); + // ugh, fail. + } + } + +} diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index 9e25e30..1a635a9 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -144,8 +144,8 @@ class ProcessList { // These are the high-end OOM level limits. This is appropriate for a // 1280x800 or larger screen with around 1GB RAM. Values are in KB. private final long[] mOomMinFreeHigh = new long[] { - 32768, 40960, 49152, - 57344, 65536, 81920 + 49152, 61440, 73728, + 86016, 98304, 122880 }; // The actual OOM killer memory levels we are using. private final long[] mOomMinFree = new long[mOomAdj.length]; diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index a32af2f..7929f96 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -138,6 +138,7 @@ class ProcessRecord { boolean persistent; // always keep this application running? boolean crashing; // are we in the process of crashing? Dialog crashDialog; // dialog being displayed due to crash. + boolean forceCrashReport; // suppress normal auto-dismiss of crash dialog & report UI? boolean notResponding; // does the app have a not responding dialog? Dialog anrDialog; // dialog being displayed due to app not resp. boolean removed; // has app package been removed from device? diff --git a/services/java/com/android/server/connectivity/Nat464Xlat.java b/services/java/com/android/server/connectivity/Nat464Xlat.java index 2884eaf..59403c5 100644 --- a/services/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/java/com/android/server/connectivity/Nat464Xlat.java @@ -87,6 +87,10 @@ public class Nat464Xlat extends BaseNetworkObserver { return netType == TYPE_MOBILE && !lp.hasIPv4Address(); } + public static boolean isRunningClat(LinkProperties lp) { + return lp != null && lp.getAllInterfaceNames().contains(CLAT_INTERFACE_NAME); + } + /** * Starts the clat daemon. * @param lp The link properties of the interface to start clatd on. diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 30d98eb..ca7bba2 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -110,8 +110,10 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.UserManager; import android.os.Environment.UserEnvironment; +import android.os.UserManager; +import android.provider.Settings.Secure; +import android.security.KeyStore; import android.security.SystemKeyStore; import android.util.DisplayMetrics; import android.util.EventLog; @@ -1323,6 +1325,12 @@ public class PackageManagerService extends IPackageManager.Stub { ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL) : 0)); + // If this is the first boot, and it is a normal boot, then + // we need to initialize the default preferred apps. + if (!mRestoredSettings && !onlyCore) { + mSettings.readDefaultPreferredAppsLPw(this, 0); + } + // can downgrade to reader mSettings.writeLPr(); @@ -5114,141 +5122,90 @@ public class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp); } } - if (bp != null && bp.packageSetting != null) { - final String perm = bp.name; - boolean allowed; - boolean allowedSig = false; - final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; - if (level == PermissionInfo.PROTECTION_NORMAL - || level == PermissionInfo.PROTECTION_DANGEROUS) { - // If the permission is required, or it's optional and was previously - // granted to the application, then allow it. Otherwise deny. - allowed = (required || origPermissions.contains(perm)); - } else if (bp.packageSetting == null) { - // This permission is invalid; skip it. - allowed = false; - } else if (level == PermissionInfo.PROTECTION_SIGNATURE) { - allowed = (compareSignatures( - bp.packageSetting.signatures.mSignatures, pkg.mSignatures) - == PackageManager.SIGNATURE_MATCH) - || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures) - == PackageManager.SIGNATURE_MATCH); - if (!allowed && (bp.protectionLevel - & PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) { - if (isSystemApp(pkg)) { - // For updated system applications, a system permission - // is granted only if it had been defined by the original application. - if (isUpdatedSystemApp(pkg)) { - final PackageSetting sysPs = mSettings - .getDisabledSystemPkgLPr(pkg.packageName); - final GrantedPermissions origGp = sysPs.sharedUser != null - ? sysPs.sharedUser : sysPs; - if (origGp.grantedPermissions.contains(perm)) { - allowed = true; - } else { - // The system apk may have been updated with an older - // version of the one on the data partition, but which - // granted a new system permission that it didn't have - // before. In this case we do want to allow the app to - // now get the new permission, because it is allowed by - // the system image. - allowed = false; - if (sysPs.pkg != null) { - for (int j=0; - j<sysPs.pkg.requestedPermissions.size(); j++) { - if (perm.equals( - sysPs.pkg.requestedPermissions.get(j))) { - allowed = true; - break; - } - } - } - } - } else { - allowed = true; - } - } - } - if (!allowed && (bp.protectionLevel - & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) { - // For development permissions, a development permission - // is granted only if it was already granted. - allowed = origPermissions.contains(perm); - } - if (allowed) { - allowedSig = true; - } - } else { - allowed = false; + + if (bp == null || bp.packageSetting == null) { + Slog.w(TAG, "Unknown permission " + name + + " in package " + pkg.packageName); + continue; + } + + final String perm = bp.name; + boolean allowed; + boolean allowedSig = false; + final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; + if (level == PermissionInfo.PROTECTION_NORMAL + || level == PermissionInfo.PROTECTION_DANGEROUS) { + // We grant a normal or dangerous permission if any of the following + // are true: + // 1) The permission is required + // 2) The permission is optional, but was granted in the past + // 3) The permission is optional, but was requested by an + // app in /system (not /data) + // + // Otherwise, reject the permission. + allowed = (required || origPermissions.contains(perm) + || (isSystemApp(ps) && !isUpdatedSystemApp(ps))); + } else if (bp.packageSetting == null) { + // This permission is invalid; skip it. + allowed = false; + } else if (level == PermissionInfo.PROTECTION_SIGNATURE) { + allowed = grantSignaturePermission(perm, pkg, bp, origPermissions); + if (allowed) { + allowedSig = true; + } + } else { + allowed = false; + } + if (DEBUG_INSTALL) { + if (gp != ps) { + Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); } - if (DEBUG_INSTALL) { - if (gp != ps) { - Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); + } + if (allowed) { + if (!isSystemApp(ps) && ps.permissionsFixed) { + // If this is an existing, non-system package, then + // we can't add any new permissions to it. + if (!allowedSig && !gp.grantedPermissions.contains(perm)) { + // Except... if this is a permission that was added + // to the platform (note: need to only do this when + // updating the platform). + allowed = isNewPlatformPermissionForPackage(perm, pkg); } } if (allowed) { - if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0 - && ps.permissionsFixed) { - // If this is an existing, non-system package, then - // we can't add any new permissions to it. - if (!allowedSig && !gp.grantedPermissions.contains(perm)) { - allowed = false; - // Except... if this is a permission that was added - // to the platform (note: need to only do this when - // updating the platform). - final int NP = PackageParser.NEW_PERMISSIONS.length; - for (int ip=0; ip<NP; ip++) { - final PackageParser.NewPermissionInfo npi - = PackageParser.NEW_PERMISSIONS[ip]; - if (npi.name.equals(perm) - && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) { - allowed = true; - Log.i(TAG, "Auto-granting " + perm + " to old pkg " - + pkg.packageName); - break; - } - } - } - } - if (allowed) { - if (!gp.grantedPermissions.contains(perm)) { - changedPermission = true; - gp.grantedPermissions.add(perm); - gp.gids = appendInts(gp.gids, bp.gids); - } else if (!ps.haveGids) { - gp.gids = appendInts(gp.gids, bp.gids); - } - } else { - Slog.w(TAG, "Not granting permission " + perm - + " to package " + pkg.packageName - + " because it was previously installed without"); - } - } else { - if (gp.grantedPermissions.remove(perm)) { + if (!gp.grantedPermissions.contains(perm)) { changedPermission = true; - gp.gids = removeInts(gp.gids, bp.gids); - Slog.i(TAG, "Un-granting permission " + perm - + " from package " + pkg.packageName - + " (protectionLevel=" + bp.protectionLevel - + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) - + ")"); - } else { - Slog.w(TAG, "Not granting permission " + perm - + " to package " + pkg.packageName - + " (protectionLevel=" + bp.protectionLevel - + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) - + ")"); + gp.grantedPermissions.add(perm); + gp.gids = appendInts(gp.gids, bp.gids); + } else if (!ps.haveGids) { + gp.gids = appendInts(gp.gids, bp.gids); } + } else { + Slog.w(TAG, "Not granting permission " + perm + + " to package " + pkg.packageName + + " because it was previously installed without"); } } else { - Slog.w(TAG, "Unknown permission " + name - + " in package " + pkg.packageName); + if (gp.grantedPermissions.remove(perm)) { + changedPermission = true; + gp.gids = removeInts(gp.gids, bp.gids); + Slog.i(TAG, "Un-granting permission " + perm + + " from package " + pkg.packageName + + " (protectionLevel=" + bp.protectionLevel + + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + + ")"); + } else { + Slog.w(TAG, "Not granting permission " + perm + + " to package " + pkg.packageName + + " (protectionLevel=" + bp.protectionLevel + + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + + ")"); + } } } if ((changedPermission || replace) && !ps.permissionsFixed && - ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) || - ((ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)){ + !isSystemApp(ps) || isUpdatedSystemApp(ps)){ // This is the first that we have heard about this package, so the // permissions we have now selected are fixed until explicitly // changed. @@ -5256,7 +5213,77 @@ public class PackageManagerService extends IPackageManager.Stub { } ps.haveGids = true; } - + + private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) { + boolean allowed = false; + final int NP = PackageParser.NEW_PERMISSIONS.length; + for (int ip=0; ip<NP; ip++) { + final PackageParser.NewPermissionInfo npi + = PackageParser.NEW_PERMISSIONS[ip]; + if (npi.name.equals(perm) + && pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) { + allowed = true; + Log.i(TAG, "Auto-granting " + perm + " to old pkg " + + pkg.packageName); + break; + } + } + return allowed; + } + + private boolean grantSignaturePermission(String perm, PackageParser.Package pkg, + BasePermission bp, HashSet<String> origPermissions) { + boolean allowed; + allowed = (compareSignatures( + bp.packageSetting.signatures.mSignatures, pkg.mSignatures) + == PackageManager.SIGNATURE_MATCH) + || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures) + == PackageManager.SIGNATURE_MATCH); + if (!allowed && (bp.protectionLevel + & PermissionInfo.PROTECTION_FLAG_SYSTEM) != 0) { + if (isSystemApp(pkg)) { + // For updated system applications, a system permission + // is granted only if it had been defined by the original application. + if (isUpdatedSystemApp(pkg)) { + final PackageSetting sysPs = mSettings + .getDisabledSystemPkgLPr(pkg.packageName); + final GrantedPermissions origGp = sysPs.sharedUser != null + ? sysPs.sharedUser : sysPs; + if (origGp.grantedPermissions.contains(perm)) { + allowed = true; + } else { + // The system apk may have been updated with an older + // version of the one on the data partition, but which + // granted a new system permission that it didn't have + // before. In this case we do want to allow the app to + // now get the new permission, because it is allowed by + // the system image. + allowed = false; + if (sysPs.pkg != null) { + for (int j=0; + j<sysPs.pkg.requestedPermissions.size(); j++) { + if (perm.equals( + sysPs.pkg.requestedPermissions.get(j))) { + allowed = true; + break; + } + } + } + } + } else { + allowed = true; + } + } + } + if (!allowed && (bp.protectionLevel + & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) { + // For development permissions, a development permission + // is granted only if it was already granted. + allowed = origPermissions.contains(perm); + } + return allowed; + } + final class ActivityIntentResolver extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> { public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, @@ -8361,6 +8388,10 @@ public class PackageManagerService extends IPackageManager.Stub { return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0; } + private static boolean isUpdatedSystemApp(PackageSetting ps) { + return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; + } + private static boolean isUpdatedSystemApp(PackageParser.Package pkg) { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } @@ -8616,6 +8647,17 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.writeLPr(); } } + // A user ID was deleted here. Go through all users and remove it from + // KeyStore. + final int appId = outInfo.removedAppId; + if (appId != -1) { + final KeyStore keyStore = KeyStore.getInstance(); + if (keyStore != null) { + for (final int userId : sUserManager.getUserIds()) { + keyStore.clearUid(UserHandle.getUid(userId, appId)); + } + } + } } /* @@ -8735,7 +8777,7 @@ public class PackageManagerService extends IPackageManager.Stub { false, //installed true, //stopped true, //notLaunched - null, null); + null, null, null); if (!isSystemApp(ps)) { if (ps.isAnyInstalled(sUserManager.getUserIds())) { // Other user still have this package installed, so all @@ -9308,9 +9350,12 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public void setApplicationEnabledSetting(String appPackageName, - int newState, int flags, int userId) { + int newState, int flags, int userId, String callingPackage) { if (!sUserManager.exists(userId)) return; - setEnabledSetting(appPackageName, null, newState, flags, userId); + if (callingPackage == null) { + callingPackage = Integer.toString(Binder.getCallingUid()); + } + setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage); } @Override @@ -9318,11 +9363,11 @@ public class PackageManagerService extends IPackageManager.Stub { int newState, int flags, int userId) { if (!sUserManager.exists(userId)) return; setEnabledSetting(componentName.getPackageName(), - componentName.getClassName(), newState, flags, userId); + componentName.getClassName(), newState, flags, userId, null); } - private void setEnabledSetting( - final String packageName, String className, int newState, final int flags, int userId) { + private void setEnabledSetting(final String packageName, String className, int newState, + final int flags, int userId, String callingPackage) { if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT || newState == COMPONENT_ENABLED_STATE_ENABLED || newState == COMPONENT_ENABLED_STATE_DISABLED @@ -9368,7 +9413,12 @@ public class PackageManagerService extends IPackageManager.Stub { // Nothing to do return; } - pkgSetting.setEnabled(newState, userId); + if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + // Don't care about who enables an app. + callingPackage = null; + } + pkgSetting.setEnabled(newState, userId, callingPackage); // pkgSetting.pkg.mSetEnabled = newState; } else { // We're dealing with a component level state change diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java index ae1b213..e64ec6d 100644 --- a/services/java/com/android/server/pm/PackageSettingBase.java +++ b/services/java/com/android/server/pm/PackageSettingBase.java @@ -189,14 +189,20 @@ class PackageSettingBase extends GrantedPermissions { return DEFAULT_USER_STATE; } - void setEnabled(int state, int userId) { - modifyUserState(userId).enabled = state; + void setEnabled(int state, int userId, String callingPackage) { + PackageUserState st = modifyUserState(userId); + st.enabled = state; + st.lastDisableAppCaller = callingPackage; } int getEnabled(int userId) { return readUserState(userId).enabled; } + String getLastDisabledAppCaller(int userId) { + return readUserState(userId).lastDisableAppCaller; + } + void setInstalled(boolean inst, int userId) { modifyUserState(userId).installed = inst; } @@ -249,13 +255,14 @@ class PackageSettingBase extends GrantedPermissions { } void setUserState(int userId, int enabled, boolean installed, boolean stopped, - boolean notLaunched, HashSet<String> enabledComponents, + boolean notLaunched, String lastDisableAppCaller, HashSet<String> enabledComponents, HashSet<String> disabledComponents) { PackageUserState state = modifyUserState(userId); state.enabled = enabled; state.installed = installed; state.stopped = stopped; state.notLaunched = notLaunched; + state.lastDisableAppCaller = lastDisableAppCaller; state.enabledComponents = enabledComponents; state.disabledComponents = disabledComponents; } diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java index 15d2a5a..4bbdb5e 100644 --- a/services/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/java/com/android/server/pm/SELinuxMMAC.java @@ -206,10 +206,10 @@ public final class SELinuxMMAC { String tagName = parser.getName(); if ("seinfo".equals(tagName)) { String seinfoValue = parser.getAttributeValue(null, "value"); - if (seinfoValue != null) { + if (validateValue(seinfoValue)) { seinfo = seinfoValue; } else { - Slog.w(TAG, "<seinfo> without value at " + Slog.w(TAG, "<seinfo> without valid value at " + parser.getPositionDescription()); } } @@ -219,6 +219,28 @@ public final class SELinuxMMAC { } /** + * General validation routine for tag values. + * Returns a boolean indicating if the passed string + * contains only letters or underscores. + */ + private static boolean validateValue(String name) { + if (name == null) + return false; + + final int N = name.length(); + if (N == 0) + return false; + + for (int i = 0; i < N; i++) { + final char c = name.charAt(i); + if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c != '_')) { + return false; + } + } + return true; + } + + /** * Labels a package based on an seinfo tag from install policy. * The label is attached to the ApplicationInfo instance of the package. * @param PackageParser.Package object representing the package diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index e645078..2e48074 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -104,6 +104,7 @@ final class Settings { private static final String ATTR_CODE = "code"; private static final String ATTR_NOT_LAUNCHED = "nl"; private static final String ATTR_ENABLED = "enabled"; + private static final String ATTR_ENABLED_CALLER = "enabledCaller"; private static final String ATTR_STOPPED = "stopped"; private static final String ATTR_INSTALLED = "inst"; @@ -453,7 +454,7 @@ final class Settings { installed, true, // stopped, true, // notLaunched - null, null); + null, null, null); writePackageRestrictionsLPr(user.id); } } @@ -850,7 +851,7 @@ final class Settings { true, // installed false, // stopped false, // notLaunched - null, null); + null, null, null); } return; } @@ -895,6 +896,8 @@ final class Settings { final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); final int enabled = enabledStr == null ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr); + final String enabledCaller = parser.getAttributeValue(null, + ATTR_ENABLED_CALLER); final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED); final boolean installed = installedStr == null ? true : Boolean.parseBoolean(installedStr); @@ -925,7 +928,7 @@ final class Settings { } ps.setUserState(userId, enabled, installed, stopped, notLaunched, - enabledComponents, disabledComponents); + enabledCaller, enabledComponents, disabledComponents); } else if (tagName.equals("preferred-activities")) { readPreferredActivitiesLPw(parser, userId); } else { @@ -1052,6 +1055,10 @@ final class Settings { if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) { serializer.attribute(null, ATTR_ENABLED, Integer.toString(ustate.enabled)); + if (ustate.lastDisableAppCaller != null) { + serializer.attribute(null, ATTR_ENABLED_CALLER, + ustate.lastDisableAppCaller); + } } if (ustate.enabledComponents != null && ustate.enabledComponents.size() > 0) { @@ -1365,6 +1372,7 @@ final class Settings { // userId - application-specific user id // debugFlag - 0 or 1 if the package is debuggable. // dataPath - path to package's data path + // seinfo - seinfo label for the app (assigned at install time) // // NOTE: We prefer not to expose all ApplicationInfo flags for now. // @@ -1378,6 +1386,8 @@ final class Settings { sb.append((int)ai.uid); sb.append(isDebug ? " 1 " : " 0 "); sb.append(dataPath); + sb.append(" "); + sb.append(ai.seinfo); sb.append("\n"); str.write(sb.toString().getBytes()); } @@ -1593,9 +1603,6 @@ final class Settings { mReadMessages.append("No settings file found\n"); PackageManagerService.reportSettingsProblem(Log.INFO, "No settings file; creating initial state"); - if (!onlyCore) { - readDefaultPreferredAppsLPw(service, 0); - } mInternalSdkPlatform = mExternalSdkPlatform = sdkVersion; return false; } @@ -2239,14 +2246,14 @@ final class Settings { final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); if (enabledStr != null) { try { - packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */); + packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */, null); } catch (NumberFormatException e) { if (enabledStr.equalsIgnoreCase("true")) { - packageSetting.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 0); + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 0, null); } else if (enabledStr.equalsIgnoreCase("false")) { - packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0); + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, null); } else if (enabledStr.equalsIgnoreCase("default")) { - packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0); + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null); } else { PackageManagerService.reportSettingsProblem(Log.WARN, "Error in package manager settings: package " + name @@ -2255,7 +2262,7 @@ final class Settings { } } } else { - packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0); + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null); } final String installStatusStr = parser.getAttributeValue(null, "installStatus"); @@ -2789,6 +2796,11 @@ final class Settings { pw.print(ps.getNotLaunched(user.id)); pw.print(" enabled="); pw.println(ps.getEnabled(user.id)); + String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id); + if (lastDisabledAppCaller != null) { + pw.print(prefix); pw.print(" lastDisabledCaller: "); + pw.println(lastDisabledAppCaller); + } HashSet<String> cmp = ps.getDisabledComponents(user.id); if (cmp != null && cmp.size() > 0) { pw.print(prefix); pw.println(" disabledComponents:"); diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index fecc2df..aa1b2ff 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -26,7 +26,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.RestrictionEntry; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; @@ -619,6 +618,10 @@ public class UserManagerService extends IUserManager.Stub { writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS); writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS); writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION); + writeBoolean(serializer, restrictions, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); serializer.endTag(null, TAG_RESTRICTIONS); } serializer.endTag(null, TAG_USER); @@ -735,6 +738,10 @@ public class UserManagerService extends IUserManager.Stub { readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS); readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS); readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION); + readBoolean(parser, restrictions, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH); + readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); } } } @@ -1072,12 +1079,12 @@ public class UserManagerService extends IUserManager.Stub { for (RestrictionEntry entry : entries) { serializer.startTag(null, TAG_ENTRY); - serializer.attribute(null, ATTR_KEY, entry.key); - if (entry.getStringValue() != null || entry.getMultipleValues() == null) { - String value = entry.getStringValue(); + serializer.attribute(null, ATTR_KEY, entry.getKey()); + if (entry.getSelectedString() != null || entry.getAllSelectedStrings() == null) { + String value = entry.getSelectedString(); serializer.text(value != null ? value : ""); } else { - String[] values = entry.getMultipleValues(); + String[] values = entry.getAllSelectedStrings(); serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length)); for (String value : values) { serializer.startTag(null, TAG_VALUE); diff --git a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java index b065310..d603cfa 100644 --- a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java +++ b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java @@ -102,6 +102,7 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { Slog.i(TAG, "Found new update, installing..."); install(altContent, altVersion); Slog.i(TAG, "Installation successful"); + postInstall(context, intent); } } catch (Exception e) { Slog.e(TAG, "Could not update content!", e); @@ -257,4 +258,7 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { writeUpdate(updateDir, updateContent, content); writeUpdate(updateDir, updateVersion, Long.toString(version).getBytes()); } + + protected void postInstall(Context context, Intent intent) { + } } diff --git a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java new file mode 100644 index 0000000..748849e --- /dev/null +++ b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.updates; + +import android.content.Context; +import android.content.Intent; +import android.os.SELinux; +import android.provider.Settings; +import android.util.Base64; +import android.util.Slog; + +import java.io.IOException; + +public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { + + public SELinuxPolicyInstallReceiver() { + super("/data/security/", "sepolicy", "metadata/", "version"); + } + + @Override + protected void install(byte[] encodedContent, int version) throws IOException { + super.install(Base64.decode(encodedContent, Base64.DEFAULT), version); + } + + @Override + protected void postInstall(Context context, Intent intent) { + boolean mode = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.SELINUX_STATUS, 0) == 1; + SELinux.setSELinuxEnforce(mode); + } +} diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java index f9aaa17..9b5b312 100644 --- a/services/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/java/com/android/server/usb/UsbSettingsManager.java @@ -34,6 +34,7 @@ import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; import android.os.Binder; import android.os.Environment; +import android.os.Process; import android.os.UserHandle; import android.util.AtomicFile; import android.util.Log; @@ -853,21 +854,29 @@ class UsbSettingsManager { public boolean hasPermission(UsbDevice device) { synchronized (mLock) { + int uid = Binder.getCallingUid(); + if (uid == Process.SYSTEM_UID) { + return true; + } SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); if (uidList == null) { return false; } - return uidList.get(Binder.getCallingUid()); + return uidList.get(uid); } } public boolean hasPermission(UsbAccessory accessory) { synchronized (mLock) { + int uid = Binder.getCallingUid(); + if (uid == Process.SYSTEM_UID) { + return true; + } SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); if (uidList == null) { return false; } - return uidList.get(Binder.getCallingUid()); + return uidList.get(uid); } } diff --git a/services/java/com/android/server/wifi/README.txt b/services/java/com/android/server/wifi/README.txt index c03bff5..39e1475 100644 --- a/services/java/com/android/server/wifi/README.txt +++ b/services/java/com/android/server/wifi/README.txt @@ -10,3 +10,10 @@ WifiNotificationController: Controls whether the open network notification is di WifiStateMachine: Tracks the various states on STA and AP connectivity and handles bring up and shut down. + +Feature description: + +Scan-only mode with Wi-Fi turned off: + - Setup wizard opts user into allowing scanning for improved location. We show no further dialogs in setup wizard since the user has just opted into the feature. This is the reason WifiService listens to DEVICE_PROVISIONED setting. + - Once the user has his device provisioned, turning off Wi-Fi from settings or from a third party app will show up a dialog reminding the user that scan mode will be on even though Wi-Fi is being turned off. The user has the choice to turn this notification off. + - In the scan mode, the device continues to allow scanning from any app with Wi-Fi turned off. This is done by disabling all networks and allowing only scans to be passed. diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java index 9dde58f..f8d5d2e 100644 --- a/services/java/com/android/server/wifi/WifiService.java +++ b/services/java/com/android/server/wifi/WifiService.java @@ -58,6 +58,7 @@ import java.net.InetAddress; import java.net.Inet4Address; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import com.android.internal.R; import com.android.internal.app.IBatteryStats; @@ -101,6 +102,9 @@ public final class WifiService extends IWifiManager.Stub { private int mMulticastEnabled; private int mMulticastDisabled; + private AtomicBoolean mDeviceProvisioned = new AtomicBoolean(); + private AtomicBoolean mNotifyScanMode = new AtomicBoolean(); + private final IBatteryStats mBatteryStats; private final AppOpsManager mAppOps; @@ -245,6 +249,8 @@ public final class WifiService extends IWifiManager.Stub { mWifiController.start(); registerForScanModeChange(); + registerForDeviceProvisionedChange(); + registerForNotifyUserOnScanModeChange(); mContext.registerReceiver( new BroadcastReceiver() { @Override @@ -399,10 +405,8 @@ public final class WifiService extends IWifiManager.Stub { /* Turning off Wi-Fi when scans are still available */ if (!enable && isScanningAlwaysAvailable()) { - /* This can be changed by user in the app to not be notified again */ - boolean notifyUser = (Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE, 1) == 1); - if (notifyUser) { + /* Notify if device is provisioned and user has not opted out of the notification */ + if (mNotifyScanMode.get() && mDeviceProvisioned.get()) { Intent intent = new Intent(WifiManager.ACTION_NOTIFY_SCAN_ALWAYS_AVAILABLE); mContext.startActivityAsUser(intent, null, UserHandle.CURRENT); } @@ -875,6 +879,51 @@ public final class WifiService extends IWifiManager.Stub { false, contentObserver); } + private void getPersistedDeviceProvisioned() { + mDeviceProvisioned.set(Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 0) != 0); + } + + private void getPersistedNotifyScanMode() { + mNotifyScanMode.set(Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE, 1) == 1); + } + + /** + * Observes settings changes to notify the user when scan mode is active and + * Wi-Fi is turned off + */ + private void registerForNotifyUserOnScanModeChange() { + ContentObserver contentObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + getPersistedNotifyScanMode(); + } + }; + + getPersistedNotifyScanMode(); + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE), + false, contentObserver); + } + + /* + * Observes settings changes device provisioned status + */ + private void registerForDeviceProvisionedChange() { + ContentObserver contentObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + getPersistedDeviceProvisioned(); + } + }; + + getPersistedDeviceProvisioned(); + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), + false, contentObserver); + } + private void registerForBroadcasts() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_SCREEN_ON); @@ -899,6 +948,8 @@ public final class WifiService extends IWifiManager.Stub { pw.println("Stay-awake conditions: " + Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0)); + pw.println("mDeviceProvisioned " + mDeviceProvisioned.get()); + pw.println("mNotifyScanMode " + mNotifyScanMode.get()); pw.println("mMulticastEnabled " + mMulticastEnabled); pw.println("mMulticastDisabled " + mMulticastDisabled); mWifiController.dump(fd, pw, args); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 34052f3..cbc42eb 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5803,6 +5803,19 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override + public void removeRotationWatcher(IRotationWatcher watcher) { + final IBinder watcherBinder = watcher.asBinder(); + synchronized (mWindowMap) { + for (int i=0; i<mRotationWatchers.size(); i++) { + if (watcherBinder == mRotationWatchers.get(i).asBinder()) { + mRotationWatchers.remove(i); + i--; + } + } + } + } + /** * Apps that use the compact menu panel (as controlled by the panelMenuIsCompact * theme attribute) on devices that feature a physical options menu key attempt to position diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 00c3a67..8ae3824 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -75,12 +75,12 @@ public class AccountManagerServiceTest extends AndroidTestCase { Account a12 = new Account("account1", "type2"); Account a22 = new Account("account2", "type2"); Account a32 = new Account("account3", "type2"); - mAms.addAccount(a11, "p11", null); - mAms.addAccount(a12, "p12", null); - mAms.addAccount(a21, "p21", null); - mAms.addAccount(a22, "p22", null); - mAms.addAccount(a31, "p31", null); - mAms.addAccount(a32, "p32", null); + mAms.addAccountExplicitly(a11, "p11", null); + mAms.addAccountExplicitly(a12, "p12", null); + mAms.addAccountExplicitly(a21, "p21", null); + mAms.addAccountExplicitly(a22, "p22", null); + mAms.addAccountExplicitly(a31, "p31", null); + mAms.addAccountExplicitly(a32, "p32", null); Account[] accounts = mAms.getAccounts(null); Arrays.sort(accounts, new AccountSorter()); @@ -111,8 +111,8 @@ public class AccountManagerServiceTest extends AndroidTestCase { public void testPasswords() throws Exception { Account a11 = new Account("account1", "type1"); Account a12 = new Account("account1", "type2"); - mAms.addAccount(a11, "p11", null); - mAms.addAccount(a12, "p12", null); + mAms.addAccountExplicitly(a11, "p11", null); + mAms.addAccountExplicitly(a12, "p12", null); assertEquals("p11", mAms.getPassword(a11)); assertEquals("p12", mAms.getPassword(a12)); @@ -134,8 +134,8 @@ public class AccountManagerServiceTest extends AndroidTestCase { u12.putString("a", "a_a12"); u12.putString("b", "b_a12"); u12.putString("c", "c_a12"); - mAms.addAccount(a11, "p11", u11); - mAms.addAccount(a12, "p12", u12); + mAms.addAccountExplicitly(a11, "p11", u11); + mAms.addAccountExplicitly(a12, "p12", u12); assertEquals("a_a11", mAms.getUserData(a11, "a")); assertEquals("b_a11", mAms.getUserData(a11, "b")); @@ -158,8 +158,8 @@ public class AccountManagerServiceTest extends AndroidTestCase { public void testAuthtokens() throws Exception { Account a11 = new Account("account1", "type1"); Account a12 = new Account("account1", "type2"); - mAms.addAccount(a11, "p11", null); - mAms.addAccount(a12, "p12", null); + mAms.addAccountExplicitly(a11, "p11", null); + mAms.addAccountExplicitly(a12, "p12", null); mAms.setAuthToken(a11, "att1", "a11_att1"); mAms.setAuthToken(a11, "att2", "a11_att2"); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 1ab5f45..a70ebf4 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -111,9 +111,9 @@ public class PackageManagerSettingsTests extends AndroidTestCase { private void writePackagesList() { writeFile(new File(getContext().getFilesDir(), "system/packages.list"), - ( "com.google.app1 11000 0 /data/data/com.google.app1" - + "com.google.app2 11001 0 /data/data/com.google.app2" - + "com.android.app3 11030 0 /data/data/com.android.app3") + ( "com.google.app1 11000 0 /data/data/com.google.app1 seinfo1" + + "com.google.app2 11001 0 /data/data/com.google.app2 seinfo2" + + "com.android.app3 11030 0 /data/data/com.android.app3 seinfo3") .getBytes()); } @@ -176,8 +176,8 @@ public class PackageManagerSettingsTests extends AndroidTestCase { // Enable/Disable a package PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_1); - ps.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0); - ps.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 1); + ps.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, null); + ps.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 1, null); assertEquals(COMPONENT_ENABLED_STATE_DISABLED, ps.getEnabled(0)); assertEquals(COMPONENT_ENABLED_STATE_ENABLED, ps.getEnabled(1)); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 8c47332..4aee902 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -907,6 +907,24 @@ public class TelephonyManager { } /** + * Returns the Group Identifier Level1 for a GSM phone. + * Return null if it is unavailable. + * <p> + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + */ + public String getGroupIdLevel1() { + try { + return getSubscriberInfo().getGroupIdLevel1(); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + // This could happen before phone restarts due to crashing + return null; + } + } + + /** * Returns the phone number string for line 1, for example, the MSISDN * for a GSM phone. Return null if it is unavailable. * <p> diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index cacafd4..9d556c0 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -111,7 +111,4 @@ public class DctConstants { public static final int ENABLED = 1; public static final String APN_TYPE_KEY = "apnType"; - public static String ACTION_DATA_CONNECTION_TRACKER_MESSENGER = - "com.android.internal.telephony"; - public static String EXTRA_MESSENGER = "EXTRA_MESSENGER"; } diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl index da0326c..03940dc 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl @@ -39,6 +39,11 @@ interface IPhoneSubInfo { String getSubscriberId(); /** + * Retrieves the Group Identifier Level1 for GSM phones. + */ + String getGroupIdLevel1(); + + /** * Retrieves the serial number of the ICC, if applicable. */ String getIccSerialNumber(); diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 6fbe32f..434b131 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -449,6 +449,10 @@ public class IWindowManagerImpl implements IWindowManager { } @Override + public void removeRotationWatcher(IRotationWatcher arg0) throws RemoteException { + } + + @Override public boolean waitForWindowDrawn(IBinder token, IRemoteCallback callback) { return false; } diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java index 2385c24..47f1fbf 100644 --- a/wifi/java/android/net/wifi/WifiConfigStore.java +++ b/wifi/java/android/net/wifi/WifiConfigStore.java @@ -1468,6 +1468,8 @@ class WifiConfigStore { if (config.enterpriseConfig.migrateOldEapTlsNative(mWifiNative, netId)) { saveConfig(); } + + config.enterpriseConfig.migrateCerts(mKeyStore); } private String removeDoubleQuotes(String string) { diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 95ffb1c..f73a13c 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -17,9 +17,9 @@ package android.net.wifi; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.security.Credentials; import android.text.TextUtils; -import android.util.Log; import com.android.org.bouncycastle.asn1.ASN1InputStream; import com.android.org.bouncycastle.asn1.ASN1Sequence; @@ -481,7 +481,7 @@ public class WifiEnterpriseConfig implements Parcelable { String caCertName = Credentials.CA_CERTIFICATE + name; if (mClientCertificate != null) { byte[] privKeyData = mClientPrivateKey.getEncoded(); - ret = keyStore.importKey(privKeyName, privKeyData); + ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID); if (ret == false) { return ret; } @@ -489,7 +489,7 @@ public class WifiEnterpriseConfig implements Parcelable { ret = putCertInKeyStore(keyStore, userCertName, mClientCertificate); if (ret == false) { // Remove private key installed - keyStore.delKey(privKeyName); + keyStore.delKey(privKeyName, Process.WIFI_UID); return ret; } } @@ -499,8 +499,8 @@ public class WifiEnterpriseConfig implements Parcelable { if (ret == false) { if (mClientCertificate != null) { // Remove client key+cert - keyStore.delKey(privKeyName); - keyStore.delete(userCertName); + keyStore.delKey(privKeyName, Process.WIFI_UID); + keyStore.delete(userCertName, Process.WIFI_UID); } return ret; } @@ -525,7 +525,7 @@ public class WifiEnterpriseConfig implements Parcelable { Certificate cert) { try { byte[] certData = Credentials.convertToPem(cert); - return keyStore.put(name, certData); + return keyStore.put(name, certData, Process.WIFI_UID); } catch (IOException e1) { return false; } catch (CertificateException e2) { @@ -537,14 +537,14 @@ public class WifiEnterpriseConfig implements Parcelable { String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); // a valid client certificate is configured if (!TextUtils.isEmpty(client)) { - keyStore.delKey(Credentials.USER_PRIVATE_KEY + client); - keyStore.delete(Credentials.USER_CERTIFICATE + client); + keyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); + keyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); } String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); // a valid ca certificate is configured if (!TextUtils.isEmpty(ca)) { - keyStore.delete(Credentials.CA_CERTIFICATE + ca); + keyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); } } @@ -625,6 +625,29 @@ public class WifiEnterpriseConfig implements Parcelable { return true; } + /** Migrate certs from global pool to wifi UID if not already done */ + void migrateCerts(android.security.KeyStore keyStore) { + String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX); + // a valid client certificate is configured + if (!TextUtils.isEmpty(client)) { + if (!keyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) { + keyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1, + Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID); + keyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1, + Credentials.USER_CERTIFICATE + client, Process.WIFI_UID); + } + } + + String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX); + // a valid ca certificate is configured + if (!TextUtils.isEmpty(ca)) { + if (!keyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) { + keyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1, + Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID); + } + } + } + private String removeDoubleQuotes(String string) { int length = string.length(); if ((length > 1) && (string.charAt(0) == '"') diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 32cd2f6..f637e89 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -793,4 +793,14 @@ public class WifiNative { public boolean p2pServDiscCancelReq(String id) { return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id); } + + /* Set the current mode of miracast operation. + * 0 = disabled + * 1 = operating as source + * 2 = operating as sink + */ + public void setMiracastMode(int mode) { + // Note: optional feature on the driver. It is ok for this to fail. + doBooleanCommand("DRIVER MIRACAST " + mode); + } } diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 81d2e11..cf75381 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -27,6 +27,7 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkStateTracker; import android.os.Handler; import android.os.Message; +import android.os.Messenger; import android.util.Slog; import java.util.concurrent.atomic.AtomicBoolean; @@ -262,4 +263,9 @@ public class WifiStateTracker implements NetworkStateTracker { public void removeStackedLink(LinkProperties link) { mLinkProperties.removeStackedLink(link); } + + @Override + public void supplyMessenger(Messenger messenger) { + // not supported on this network + } } diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl index 381a450..1c9c40d 100644 --- a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl +++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl @@ -26,5 +26,6 @@ import android.os.Messenger; interface IWifiP2pManager { Messenger getMessenger(); + void setMiracastMode(int mode); } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index 2e80064..737ab91 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -1258,6 +1258,21 @@ public class WifiP2pManager { c.mAsyncChannel.sendMessage(REQUEST_PERSISTENT_GROUP_INFO, 0, c.putListener(listener)); } + /** @hide */ + public static final int MIRACAST_DISABLED = 0; + /** @hide */ + public static final int MIRACAST_SOURCE = 1; + /** @hide */ + public static final int MIRACAST_SINK = 2; + /** Internal use only @hide */ + public void setMiracastMode(int mode) { + try { + mService.setMiracastMode(mode); + } catch(RemoteException e) { + // ignore + } + } + /** * Get a reference to WifiP2pService handler. This is used to establish * an AsyncChannel communication with WifiService diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java index e6a1df1..447ddb0 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -164,6 +164,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub { public static final int DISCONNECT_WIFI_REQUEST = BASE + 12; public static final int DISCONNECT_WIFI_RESPONSE = BASE + 13; + public static final int SET_MIRACAST_MODE = BASE + 14; + private final boolean mP2pSupported; private WifiP2pDevice mThisDevice = new WifiP2pDevice(); @@ -310,6 +312,12 @@ public class WifiP2pService extends IWifiP2pManager.Stub { "WifiP2pService"); } + private void enforceConnectivityInternalPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CONNECTIVITY_INTERNAL, + "WifiP2pService"); + } + /** * Get a reference to handler. This is used by a client to establish * an AsyncChannel communication with WifiP2pService @@ -320,6 +328,20 @@ public class WifiP2pService extends IWifiP2pManager.Stub { return new Messenger(mP2pStateMachine.getHandler()); } + /** This is used to provide information to drivers to optimize performance depending + * on the current mode of operation. + * 0 - disabled + * 1 - source operation + * 2 - sink operation + * + * As an example, the driver could reduce the channel dwell time during scanning + * when acting as a source or sink to minimize impact on miracast. + */ + public void setMiracastMode(int mode) { + enforceConnectivityInternalPermission(); + mP2pStateMachine.sendMessage(SET_MIRACAST_MODE, mode); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -572,6 +594,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { case DhcpStateMachine.CMD_POST_DHCP_ACTION: case DhcpStateMachine.CMD_ON_QUIT: case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT: + case SET_MIRACAST_MODE: break; case WifiStateMachine.CMD_ENABLE_P2P: // Enable is lazy and has no response @@ -878,7 +901,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { sendPeersChangedBroadcast(); } break; - case WifiP2pManager.ADD_LOCAL_SERVICE: + case WifiP2pManager.ADD_LOCAL_SERVICE: if (DBG) logd(getName() + " add service"); WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo)message.obj; if (addLocalService(message.replyTo, servInfo)) { @@ -916,7 +939,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { clearServiceRequests(message.replyTo); replyToMessage(message, WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED); break; - case WifiMonitor.P2P_SERV_DISC_RESP_EVENT: + case WifiMonitor.P2P_SERV_DISC_RESP_EVENT: if (DBG) logd(getName() + " receive service response"); List<WifiP2pServiceResponse> sdRespList = (List<WifiP2pServiceResponse>) message.obj; @@ -927,13 +950,16 @@ public class WifiP2pService extends IWifiP2pManager.Stub { sendServiceResponse(resp); } break; - case WifiP2pManager.DELETE_PERSISTENT_GROUP: + case WifiP2pManager.DELETE_PERSISTENT_GROUP: if (DBG) logd(getName() + " delete persistent group"); mGroups.remove(message.arg1); replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED); break; + case SET_MIRACAST_MODE: + mWifiNative.setMiracastMode(message.arg1); + break; default: - return NOT_HANDLED; + return NOT_HANDLED; } return HANDLED; } |